From cf530483d43eb9565db6050aa23fe8398b0d826c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=B5=A9=E5=A4=A7=E4=BA=BA?= Date: Tue, 3 Mar 2026 15:35:18 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20Test2.txt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Test2.txt | 366 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 366 insertions(+) create mode 100644 Test2.txt diff --git a/Test2.txt b/Test2.txt new file mode 100644 index 0000000..2942a1b --- /dev/null +++ b/Test2.txt @@ -0,0 +1,366 @@ +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 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 ParseFieldInfo(IRow nameRow, IRow typeRow) + { + List fields = new List(); + 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"; + case "floatlist": return "List"; + case "boollist": return "List"; + case "stringlist": return "List"; + default: return "string"; + } + } + + private static string GenerateRowClass(List 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 fields = ParseFieldInfo(nameRow, typeRow); + + // 创建容器实例 + ScriptableObject container = ScriptableObject.CreateInstance(containerType); + if (container == null) + { + Debug.LogError($"无法创建容器实例: {containerClassName}"); + return; + } + + // 获取 items 字段并创建 List + 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(value, int.Parse); + case "floatlist": + return ParseList(value, float.Parse); + case "boollist": + return ParseList(value, bool.Parse); + case "stringlist": + return ParseList(value, s => s); + default: + return value; + } + } + catch (Exception e) + { + Debug.LogWarning($"转换值 '{value}' 到类型 {rawType} 失败: {e.Message},使用默认值。"); + return GetDefaultValue(rawType); + } + } + + private static List ParseList(string value, Func parser) + { + List list = new List(); + 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(); + case "floatlist": return new List(); + case "boollist": return new List(); + case "stringlist": return new List(); + default: return null; + } + } + + private class FieldInfo + { + public string Name; + public string RawType; // Excel中的原始类型标识 + public string Type; // 映射后的C#类型字符串 + } +} + +public static class ExcelBatchLoader +{ + public static Dictionary> LoadAllXlsxFiles(string directoryPath, SearchOption searchOption = SearchOption.TopDirectoryOnly) + { + var result = new Dictionary>(); + + 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 sheets = new List(); + 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; + } +} \ No newline at end of file