Files
Temp/Test2.txt
2026-03-03 15:35:18 +08:00

366 lines
13 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using UnityEngine;
using UnityEditor;
using UnityEditor.Compilation;
public class ExcelToEntityGenerator : EditorWindow
{
private static string ExcelFolder = "Assets/Data/Excels";
private static string ScriptsFolder = "Assets/Scripts/Data";
private static string AssetFolder = "Assets/Data/Containers";
public static void LoadAll(string excelFolder, string scriptsFolder, string assetFolder)
{
ExcelFolder = excelFolder;
ScriptsFolder = scriptsFolder;
AssetFolder = assetFolder;
// 确保输出目录存在
if (!Directory.Exists(ScriptsFolder)) Directory.CreateDirectory(ScriptsFolder);
if (!Directory.Exists(AssetFolder)) Directory.CreateDirectory(AssetFolder);
// 加载所有 Excel 文件
var allData = ExcelBatchLoader.LoadAllXlsxFiles(ExcelFolder);
if (allData.Count == 0)
{
Debug.LogWarning("未找到任何 .xlsx 文件");
return;
}
// 为每个文件生成数据行类和容器类
foreach (var kv in allData)
{
string fileName = kv.Key; // 不含扩展名
var sheet = kv.Value[0]; // 只处理第一个工作表
// 解析字段信息
IRow nameRow = sheet.GetRow(0);
IRow typeRow = sheet.GetRow(1);
List<FieldInfo> fields = ParseFieldInfo(nameRow, typeRow);
// 生成数据行类
string rowClassPath = $"{ScriptsFolder}/{fileName}Container.cs";
var rowClassContent = GenerateRowClass(fields, fileName, rowClassPath);
// 生成容器类
string containerClassPath = $"{ScriptsFolder}/{fileName}Container.cs";
GenerateContainerClass(fileName, containerClassPath, rowClassContent);
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
public static void CreateAllContainers()
{
// 重新加载 Excel 文件(此时数据行类已编译)
var allData = ExcelBatchLoader.LoadAllXlsxFiles(ExcelFolder);
foreach (var kv in allData)
{
string fileName = kv.Key;
var sheet = kv.Value[0];
CreateContainerFromSheet(sheet, fileName);
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
Debug.Log("所有 ScriptableObject 容器已生成完毕");
}
private static List<FieldInfo> ParseFieldInfo(IRow nameRow, IRow typeRow)
{
List<FieldInfo> fields = new List<FieldInfo>();
for (int i = 0; i < nameRow.LastCellNum; i++)
{
string rawName = nameRow.GetCell(i)?.ToString()?.Trim();
string rawType = typeRow.GetCell(i)?.ToString()?.Trim();
if (string.IsNullOrEmpty(rawName) || string.IsNullOrEmpty(rawType))
continue;
string fieldName = SanitizeName(rawName);
string fieldType = MapType(rawType); // 映射为C#类型字符串
fields.Add(new FieldInfo { Name = fieldName, RawType = rawType, Type = fieldType });
}
return fields;
}
private static string SanitizeName(string name)
{
// 移除非法字符,并确保首字母大写
string sanitized = Regex.Replace(name, @"[^a-zA-Z0-9_]", "");
if (string.IsNullOrEmpty(sanitized)) return "Field";
return char.ToUpper(sanitized[0]) + sanitized.Substring(1);
}
private static string MapType(string typeStr)
{
switch (typeStr.ToLower())
{
case "int": return "int";
case "float": return "float";
case "bool": return "bool";
case "string": return "string";
case "intlist": return "List<int>";
case "floatlist": return "List<float>";
case "boollist": return "List<bool>";
case "stringlist": return "List<string>";
default: return "string";
}
}
private static string GenerateRowClass(List<FieldInfo> fields, string className, string outputPath)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("//由程序自动生成");
sb.AppendLine("using System;");
sb.AppendLine("using System.Collections.Generic;");
sb.AppendLine("using UnityEngine;");
sb.AppendLine();
sb.AppendLine("[System.Serializable]");
sb.AppendLine($"public class {className}");
sb.AppendLine("{");
foreach (var field in fields)
{
sb.AppendLine($" public {field.Type} {field.Name};");
}
sb.AppendLine("}");
// WriteFile(outputPath, sb.ToString());
return sb.ToString();
}
private static void GenerateContainerClass(string rowClassName, string outputPath, string rowClassContent)
{
string containerName = rowClassName + "Container";
StringBuilder sb = new StringBuilder();
// sb.AppendLine("using System.Collections.Generic;");
// sb.AppendLine("using UnityEngine;");
sb.AppendLine(rowClassContent);
sb.AppendLine();
sb.AppendLine("[CreateAssetMenu(fileName = \"" + containerName + "\", menuName = \"Data/" + containerName + "\")]");
sb.AppendLine($"public class {containerName} : ScriptableObject");
sb.AppendLine("{");
sb.AppendLine($" public List<{rowClassName}> items;");
sb.AppendLine("}");
WriteFile(outputPath, sb.ToString());
}
private static void WriteFile(string path, string content)
{
// 如果文件已存在,删除后重新创建(避免残留)
if (File.Exists(path))
{
File.Delete(path);
}
File.WriteAllText(path, content, Encoding.UTF8);
}
private static void CreateContainerFromSheet(ISheet sheet, string fileName)
{
try
{
// 获取已编译的类型
string rowClassName = fileName;
string containerClassName = fileName + "Container";
System.Reflection.Assembly assembly = System.Reflection.Assembly.Load("Assembly-CSharp");
Type rowType = assembly.GetType(rowClassName);
Type containerType = assembly.GetType(containerClassName);
if (rowType == null || containerType == null)
{
Debug.LogError($"无法找到类型: {rowClassName} 或 {containerClassName},请检查编译是否成功");
return;
}
// 解析字段信息(与生成类时一致)
IRow nameRow = sheet.GetRow(0);
IRow typeRow = sheet.GetRow(1);
List<FieldInfo> fields = ParseFieldInfo(nameRow, typeRow);
// 创建容器实例
ScriptableObject container = ScriptableObject.CreateInstance(containerType);
if (container == null)
{
Debug.LogError($"无法创建容器实例: {containerClassName}");
return;
}
// 获取 items 字段并创建 List<T>
System.Reflection.FieldInfo itemsField = containerType.GetField("items");
Type listType = typeof(List<>).MakeGenericType(rowType);
object listInstance = Activator.CreateInstance(listType);
// 遍历数据行从第3行开始索引2
for (int rowIdx = 2; rowIdx <= sheet.LastRowNum; rowIdx++)
{
IRow dataRow = sheet.GetRow(rowIdx);
if (dataRow == null) continue;
// 创建数据行对象
object rowObj = Activator.CreateInstance(rowType);
bool hasData = false; // 标记该行是否有有效数据
for (int i = 0; i < fields.Count; i++)
{
var field = fields[i];
var cell = dataRow.GetCell(i);
string cellValue = cell?.ToString()?.Trim();
if (string.IsNullOrEmpty(cellValue)) continue;
hasData = true;
// 转换并赋值
object convertedValue = ConvertValue(cellValue, field.RawType);
System.Reflection.FieldInfo rowField = rowType.GetField(field.Name);
if (rowField != null)
{
rowField.SetValue(rowObj, convertedValue);
}
}
if (hasData)
{
// 添加到列表
listType.GetMethod("Add").Invoke(listInstance, new[] { rowObj });
}
}
// 将列表赋值给容器
itemsField.SetValue(container, listInstance);
// 保存资产
string assetPath = $"{AssetFolder}/{fileName}Container.asset";
// 如果已存在,先删除
if (File.Exists(assetPath))
{
AssetDatabase.DeleteAsset(assetPath);
}
AssetDatabase.CreateAsset(container, assetPath);
Debug.Log($"生成容器资产: {assetPath}");
}
catch (Exception e)
{
Debug.LogError($"处理文件 {fileName} 时出错: {e.Message}\n{e.StackTrace}");
}
}
private static object ConvertValue(string value, string rawType)
{
string lowerType = rawType.ToLower();
try
{
switch (lowerType)
{
case "int":
return int.Parse(value);
case "float":
return float.Parse(value);
case "bool":
return bool.Parse(value);
case "string":
return value;
case "intlist":
return ParseList<int>(value, int.Parse);
case "floatlist":
return ParseList<float>(value, float.Parse);
case "boollist":
return ParseList<bool>(value, bool.Parse);
case "stringlist":
return ParseList<string>(value, s => s);
default:
return value;
}
}
catch (Exception e)
{
Debug.LogWarning($"转换值 '{value}' 到类型 {rawType} 失败: {e.Message},使用默认值。");
return GetDefaultValue(rawType);
}
}
private static List<T> ParseList<T>(string value, Func<string, T> parser)
{
List<T> list = new List<T>();
if (string.IsNullOrEmpty(value)) return list;
string[] parts = value.Split(new[] { '#' }, StringSplitOptions.RemoveEmptyEntries);
foreach (string part in parts)
{
try
{
list.Add(parser(part.Trim()));
}
catch
{
// 忽略解析失败的项
}
}
return list;
}
private static object GetDefaultValue(string rawType)
{
switch (rawType.ToLower())
{
case "int": return 0;
case "float": return 0f;
case "bool": return false;
case "string": return "";
case "intlist": return new List<int>();
case "floatlist": return new List<float>();
case "boollist": return new List<bool>();
case "stringlist": return new List<string>();
default: return null;
}
}
private class FieldInfo
{
public string Name;
public string RawType; // Excel中的原始类型标识
public string Type; // 映射后的C#类型字符串
}
}
public static class ExcelBatchLoader
{
public static Dictionary<string, List<ISheet>> LoadAllXlsxFiles(string directoryPath, SearchOption searchOption = SearchOption.TopDirectoryOnly)
{
var result = new Dictionary<string, List<ISheet>>();
if (!Directory.Exists(directoryPath))
{
Debug.LogError($"目录不存在: {directoryPath}");
return result;
}
string[] files = Directory.GetFiles(directoryPath, "*.xlsx", searchOption);
foreach (string filePath in files)
{
try
{
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
IWorkbook workbook = new XSSFWorkbook(fs);
List<ISheet> sheets = new List<ISheet>();
for (int i = 0; i < workbook.NumberOfSheets; i++)
{
sheets.Add(workbook.GetSheetAt(i));
}
var fileName = Path.GetFileNameWithoutExtension(filePath);
result[fileName] = sheets;
}
}
catch (Exception e)
{
Debug.LogError($"加载文件失败: {filePath}\n{e.Message}");
}
}
return result;
}
}