Unity升级版·Excel数据解析,自动创建对应C#类,自动创建ScriptableObject生成类,自动序列化Asset文件

Posted wx62bbed1842396

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity升级版·Excel数据解析,自动创建对应C#类,自动创建ScriptableObject生成类,自动序列化Asset文件相关的知识,希望对你有一定的参考价值。


 实现功能:

  • 自动创建继承ScriptableObject的C#数据类,每条Excel的数据,都有对应的字段的Get函数;
  • 【Unity】升级版·Excel数据解析,自动创建对应C#类,自动创建ScriptableObject生成类,自动序列化Asset文件_excel【Unity】升级版·Excel数据解析,自动创建对应C#类,自动创建ScriptableObject生成类,自动序列化Asset文件_unity_02


  • 自动创建每个Excel的Asset生成类和生成函数,用于自动生成Asset文件

【Unity】升级版·Excel数据解析,自动创建对应C#类,自动创建ScriptableObject生成类,自动序列化Asset文件_unity_03

  • 使用Asset生成类自动序列化Excel数据到Asset文件,可直接在项目运行时加载使用

【Unity】升级版·Excel数据解析,自动创建对应C#类,自动创建ScriptableObject生成类,自动序列化Asset文件_工具类_04


【Unity】升级版·Excel数据解析,自动创建对应C#类,自动创建ScriptableObject生成类,自动序列化Asset文件_excel_05

实现原理:

Excel配置格式:

  • 第1行对应特殊标记(可以设置有效性,指定要创建的文件)
  • 第2行对应中文说明(作为第3行字段的注释)
  • 第3行对应字段名称(自动创建的字段名称)
  • 第4行对应字段类型(自动创建的字段类型,与字段名称一一对应)
  • 第5行及以后对应字段值(所有数据,以行为单位解析、保存数据)
  • 第一列固定字段为"id",是代码中索引每行数据的Key

Excel注释操作:

  • 字段名称行,每个字段单元格内容前加"//",可以注释该字段,不会解析生成到C#类;
  • 第一列的单元格内容前加"//",可以注释一行数据,不会保存到Asset文件中;
  • 注释可以用于添加说明行,或剔除指定无用数据。

【Unity】升级版·Excel数据解析,自动创建对应C#类,自动创建ScriptableObject生成类,自动序列化Asset文件_工具类_06

【Unity】升级版·Excel数据解析,自动创建对应C#类,自动创建ScriptableObject生成类,自动序列化Asset文件_游戏开发_07

生成的C#类格式:

行数据类,对应每一行数据:

[Serializable]
public class TestConfigExcelItem : ExcelItemBase

/// <summary>
///
/// </summary>>
public int id;
/// <summary>
///
/// </summary>>
public string testString;
/// <summary>
///
/// </summary>>
public int testInt;
/// <summary>
///
/// </summary>>
public float

【Unity】升级版·Excel数据解析,自动创建对应C#类,自动创建ScriptableObject生成类,自动序列化Asset文件_工具类_08

完整数据类,包含所有行的数据、初始化函数、Get函数:

public class TestConfigExcelData : ExcelDataBase<TestConfigExcelItem>

public TestConfigExcelItem[] items;

public Dictionary<int,TestConfigExcelItem> itemDic = new Dictionary<int,TestConfigExcelItem>();

public void Init()

itemDic.Clear();
if(items != null && items.Length > 0)

for(int i = 0; i < items.Length; i++)

itemDic.Add(items[i].id, items[i]);




public TestConfigExcelItem GetTestConfigExcelItem(int)

if(itemDic.ContainsKey(id))
return itemDic[id];
else
return null;

#region

public string GetTestString(int)

var item = GetTestConfigExcelItem(id);
if(item == null)
return default;
return item.testString;


// ··· ···

#endregion

【Unity】升级版·Excel数据解析,自动创建对应C#类,自动创建ScriptableObject生成类,自动序列化Asset文件_工具类_09

目前支持的数据结构:

字符串

testString

字符串数组

testStringArray

字符串二维数组

testStringArray2

Int

testInt

Int数组

testIntArray

Int二维数组

testIntArray2

Float

testFloat

Float数组

testFloatArray

Float二维数组

testFloatArray2

Bool

testBool

Bool数组

testBoolArray

Bool二维数组

testBoolArray2

Enum|枚举名(或枚举值)

testEnum

Enum数组

testEnumArray

Enum二维数组

不支持

Vector2

testVector2

Vector2数组

testVector2Array

Vector2二维数组

testVector2Array2

Vector3

testVector3

Vector3数组

testVector3Array

Vector3二维数组

testVector3Array2

Vector2Int

testVector2Int

Vector2Int数组

testVector2IntArray

Vector2Int二维数组

testVector2IntArray2

Vector3Int

testVector3Int

Vector3Int数组

testVector3IntArray

Vector3Int二维数组

testVector3IntArray2

Color

testColor

Color数组

testColorArray

Color二维数组

testColorArray2

Color32

testColor32

Color32数组

testColor32Array

Color32二维数组

testColor32Array2

因为Unity不能序列化二维数组,这里改成一维数组+结构体的方式实现:

[Serializable]
public struct StringArr

public string[] array;


//二维数组表示方式: StringArr[]


【Unity】升级版·Excel数据解析,自动创建对应C#类,自动创建ScriptableObject生成类,自动序列化Asset文件_工具类_10

Asset数据文件:

在自动生成数据的C#类时,会同步生成Asset文件的创建类,用于自动创建Asset文件并序列化数据。

【Unity】升级版·Excel数据解析,自动创建对应C#类,自动创建ScriptableObject生成类,自动序列化Asset文件_工具类_11


优点:

  • 数据修改后只需要重新一键生成即可
  • 每个Excel对应一个类,使用灵活,对Excel限制少
  • 自动创建C#类,不需要对每个Excel手动写代码,每条数据对应字段,不需要拆箱装修
  • 自动创建ScriptableObject的Asset文件,自动序列化数据,方便查看,可以手动修改调整,不需要每次改动都在Excel里操作
  • 在游戏内直接读取Asset的ScriptableObject子类,不需要额外操作,业务层直接调取数据字段

使用方法:

  • 按照标准格式配置Excel
  • 一键生成C#类、Asset文件
  • 项目运行时加载Asset资源,调用Init初始化,Get函数获取对应字段值即可

【Unity】升级版·Excel数据解析,自动创建对应C#类,自动创建ScriptableObject生成类,自动序列化Asset文件_unity_12

【Unity】升级版·Excel数据解析,自动创建对应C#类,自动创建ScriptableObject生成类,自动序列化Asset文件_unity_13


完整代码:

扩展Unity编辑器窗口:

using UnityEngine;
using UnityEditor;
using System.IO;
using System.Collections.Generic;
using System.Linq;

public class BuildExcelWindow : EditorWindow

[MenuItem("MyTools/Excel Window",priority = 100)]
public static void ShowReadExcelWindow()

BuildExcelWindow window = GetWindow<BuildExcelWindow>(true);
window.Show();
window.minSize = new Vector2(475,475);


//Excel读取路径,绝对路径,放在Assets同级路径
private static string excelReadAbsolutePath;

//自动生成C#类文件路径,绝对路径
private static string scriptSaveAbsolutePath;
private static string scriptSaveRelativePath;
//自动生成Asset文件路径,相对路径
private static string assetSaveRelativePath;

private List<string> fileNameList = new List<string>();
private List<string> filePathList = new List<string>();

private void Awake()

titleContent.text = "Excel配置表读取";

excelReadAbsolutePath = Application.dataPath.Replace("Assets","Excel");
scriptSaveAbsolutePath = Application.dataPath + CheckEditorPath("/Script/Excel/AutoCreateCSCode");
scriptSaveRelativePath = CheckEditorPath("Assets/Script/Excel/AutoCreateCSCode");
assetSaveRelativePath = CheckEditorPath("Assets/AssetData/Excel/AutoCreateAsset");


private void OnEnable()

RefreshExcelFile();


private void OnDisable()

fileNameList.Clear();
filePathList.Clear();


private Vector2 scrollPosition = Vector2.zero;
private void OnGUI()

GUILayout.Space(10);

scrollPosition = GUILayout.BeginScrollView(scrollPosition,GUILayout.Width(position.width),GUILayout.Height(position.height));

//展示路径
GUILayout.BeginHorizontal(GUILayout.Height(20));
if(GUILayout.Button("Excel读取路径",GUILayout.Width(100)))

EditorUtility.OpenWithDefaultApp(excelReadAbsolutePath);
Debug.Log(excelReadAbsolutePath);

if(GUILayout.Button("Script保存路径",GUILayout.Width(100)))

SelectObject(scriptSaveRelativePath);

if(GUILayout.Button("Asset保存路径",GUILayout.Width(100)))

SelectObject(assetSaveRelativePath);

GUILayout.EndHorizontal();

GUILayout.Space(5);

//Excel列表

GUILayout.Label("Excel列表:");
for(int i = 0; i < fileNameList.Count; i++)

GUILayout.BeginHorizontal("Box",GUILayout.Height(40));

GUILayout.Label($"i:","Titlebar Foldout",GUILayout.Width(30),GUILayout.Height(35));
GUILayout.Box(fileNameList[i],"MeTransitionBlock",GUILayout.MinWidth(200),GUILayout.Height(35));
GUILayout.Space(10);

//生成CS代码
if(GUILayout.Button("Create Script",GUILayout.Width(100),GUILayout.Height(30)))

ExcelDataReader.ReadOneExcelToCode(filePathList[i],scriptSaveAbsolutePath);

//生成Asset文件
if(GUILayout.Button("Create Asset",GUILayout.Width(100),GUILayout.Height(30)))

ExcelDataReader.CreateOneExcelAsset(filePathList[i],assetSaveRelativePath);


GUILayout.EndHorizontal();
GUILayout.Space(5);

GUILayout.Space(10);

//一键处理所有Excel

GUILayout.Label("一键操作:");
GUILayout.BeginHorizontal("Box",GUILayout.Height(40));

GUILayout.Label("all","Titlebar Foldout",GUILayout.Width(30),GUILayout.Height(35));
GUILayout.Box("All Excel","MeTransitionBlock",GUILayout.MinWidth(200),GUILayout.Height(35));
GUILayout.Space(10);

if(GUILayout.Button("Create Script",GUILayout.Width(100),GUILayout.Height(30)))

ExcelDataReader.ReadAllExcelToCode(excelReadAbsolutePath,scriptSaveAbsolutePath);

if(GUILayout.Button("Create Asset",GUILayout.Width(100),GUILayout.Height(30)))

ExcelDataReader.CreateAllExcelAsset(excelReadAbsolutePath,assetSaveRelativePath);

GUILayout.EndHorizontal();

//
GUILayout.Space(20);
//
GUILayout.EndScrollView();


//读取指定路径下的Excel文件名
private void RefreshExcelFile()

fileNameList.Clear();
filePathList.Clear();

if(!Directory.Exists(excelReadAbsolutePath))

Debug.LogError("无效路径:" + excelReadAbsolutePath);
return;

string[] excelFileFullPaths = Directory.GetFiles(excelReadAbsolutePath,"*.xlsx");

if(excelFileFullPaths == null || excelFileFullPaths.Length == 0)

Debug.LogError(excelReadAbsolutePath + "路径下没有找到Excel文件");
return;


filePathList.AddRange(excelFileFullPaths);
for(int i = 0; i < filePathList.Count; i++)

fileNameList.Add(Path.GetFileName(filePathList[i]));

Debug.Log("找到Excel文件:" + fileNameList.Count + "个");


private void SelectObject(string)

Object targetObj = AssetDatabase.LoadAssetAtPath<Object>(targetPath);
EditorGUIUtility.PingObject(targetObj);
Selection.activeObject = targetObj;
Debug.Log(targetPath);


private static string CheckEditorPath(string)

#if
return path.Replace("/","\\\\");
#elif
return path.Replace("\\\\","/");
#else
return path;
#endif

【Unity】升级版·Excel数据解析,自动创建对应C#类,自动创建ScriptableObject生成类,自动序列化Asset文件_游戏开发_14

Excel数据读取类: 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using Excel;
using System.Reflection;
using System;
using System.Linq;

public class ExcelDataReader

//Excel第1行对应特殊标记
private const int specialSignRow = 0;
//Excel第2行对应中文说明
private const int excelNodeRow = 1;
//Excel第3行对应字段名称
private const int excelNameRow = 2;
//Excel第4行对应字段类型
private const int excelTypeRow = 3;
//Excel第5行及以后对应字段值
private const int excelDataRow = 4;

//标记注释行/列
private const string annotationSign = "//";

#region

//创建Excel对应的C#类
public static void ReadAllExcelToCode(string allExcelPath,string)

//读取所有Excel文件
//指定目录中与指定的搜索模式和选项匹配的文件的完整名称(包含路径)的数组;如果未找到任何文件,则为空数组。
string[] excelFileFullPaths = Directory.GetFiles(allExcelPath,"*.xlsx");
if(excelFileFullPaths == null || excelFileFullPaths.Length == 0)

Debug.Log("Excel file count == 0");
return;

//遍历所有Excel,创建C#类
for(int i = 0; i < excelFileFullPaths.Length; i++)

ReadOneExcelToCode(excelFileFullPaths[i],codeSavePath);



//创建Excel对应的C#类
public static void ReadOneExcelToCode(string excelFullPath,string)

//解析Excel获取中间数据
ExcelMediumData excelMediumData = CreateClassCodeByExcelPath(excelFullPath);
if(excelMediumData == null)

Debug.LogError($"读取Excel失败 : excelFullPath");
return;

if(!excelMediumData.isValid)

Debug.LogError($"读取Excel失败,Excel标记失效 : excelMediumData.excelName");
return;


if(!excelMediumData.isCreateCSharp && !excelMediumData.isCreateAssignment)

Debug.LogError($"读取Excel失败,Excel不允许生成CSCode : excelMediumData.excelName");
return;


//根据数据生成C#脚本
string classCodeStr = ExcelCodeCreater.CreateCodeStrByExcelData(excelMediumData);
if(string.IsNullOrEmpty(classCodeStr))

Debug.LogError($"解析Excel失败 : excelMediumData.excelName");
return;


//检查导出路径
if(!Directory.Exists(codeSavePath))
Directory.CreateDirectory(codeSavePath);
//类名
string codeFileName = excelMediumData.excelName + "ExcelData";
//写文件,生成CS类文件
StreamWriter sw = new StreamWriter($"codeSavePath/codeFileName.cs");
sw.WriteLine(classCodeStr);
sw.Close();
//
UnityEditor.AssetDatabase.SaveAssets();
UnityEditor.AssetDatabase.Refresh();
//
Debug.Log($"生成Excel的CS成功 : excelMediumData.excelName");


#endregion

#region

//创建Excel对应的Asset数据文件
public static void CreateAllExcelAsset(string allExcelPath,string)

//读取所有Excel文件
//指定目录中与指定的搜索模式和选项匹配的文件的完整名称(包含路径)的数组;如果未找到任何文件,则为空数组。
string[] excelFileFullPaths = Directory.GetFiles(allExcelPath,"*.xlsx");
if(excelFileFullPaths == null || excelFileFullPaths.Length == 0)

Debug.Log("Excel file count == 0");
return;

//遍历所有Excel,创建Asset
for(int i = 0; i < excelFileFullPaths.Length; i++)

CreateOneExcelAsset(excelFileFullPaths[i],assetSavePath);



//创建Excel对应的Asset数据文件
public static void CreateOneExcelAsset(string excelFullPath,string)

//解析Excel获取中间数据
ExcelMediumData excelMediumData = CreateClassCodeByExcelPath(excelFullPath);
if(excelMediumData == null)

Debug.LogError($"读取Excel失败 : excelFullPath");
return;

if(!excelMediumData.isValid)

Debug.LogError($"读取Excel失败,Excel标记失效 : excelMediumData.excelName");
return;


if(!excelMediumData.isCreateAsset)

Debug.LogError($"读取Excel失败,Excel不允许生成Asset : excelMediumData.excelName");
return;


////获取当前程序集
//Assembly assembly = Assembly.GetExecutingAssembly();
////创建类的实例,返回为 object 类型,需要强制类型转换,assembly.CreateInstance("类的完全限定名(即包括命名空间)");
//object class0bj = assembly.CreateInstance(excelMediumData.excelName + "Assignment",true);

//必须遍历所有程序集来获得类型。当前在Assembly-CSharp-Editor中,目标类型在Assembly-CSharp中,不同程序将无法获取类型
Type assignmentType = null;
string assetAssignmentName = excelMediumData.excelName + "AssetAssignment";
foreach(var asm in AppDomain.CurrentDomain.GetAssemblies())

//查找目标类型
Type tempType = asm.GetType(assetAssignmentName);
if(tempType != null)

assignmentType = tempType;
break;


if(assignmentType == null)

Debug.LogError($"创界Asset失败,未找到Asset生成类 : excelMediumData.excelName");
return;


//反射获取方法
MethodInfo methodInfo = assignmentType.GetMethod("CreateAsset");
if(methodInfo == null)

if(assignmentType == null)

Debug.LogError($"创界Asset失败,未找到Asset创建函数 : excelMediumData.excelName");
return;



methodInfo.Invoke(null,new object[] excelMediumData,assetSavePath );
//创建Asset文件成功
Debug.Log($"生成Excel的Asset成功 : excelMediumData.excelName");


#endregion

#region

//解析Excel,创建中间数据
private static ExcelMediumData CreateClassCodeByExcelPath(string)

if(string.IsNullOrEmpty(excelFileFullPath))
return null;

excelFileFullPath = excelFileFullPath.Replace("\\\\","/");
//读取Excel
FileStream stream = File.Open(excelFileFullPath,FileMode.Open,FileAccess.Read);
if(stream == null)
return null;
//解析Excel
IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream);
//无效Excel
if(excelReader == null || !excelReader.IsValid)

Debug.Log("Invalid excel : " + excelFileFullPath);
return null;


Debug.Log("开始解析Excel : " + excelReader.Name);

//记录Excel数据
ExcelMediumData excelMediumData = new ExcelMediumData();

//Excel名字
excelMediumData.excelName = excelReader.Name;

//当前遍历的行
int curRowIndex = 0;
//开始读取,按行遍历
while(excelReader.Read())

//这一行没有读取到数据,视为无效行数据
if(excelReader.FieldCount <= 0)

curRowIndex++;
continue;

//读取每一行的完整数据
string[] datas = new string[excelReader.FieldCount];
for(int j = 0; j < excelReader.FieldCount; ++j)

//可以直接读取指定类型数据,不过只支持有限数据类型,这里统一读取string,然后再数据转化
//excelReader.GetInt32(j); excelReader.GetFloat(j);

//读取每一个单元格数据
datas[j] = excelReader.GetString(j);


switch(curRowIndex)

case specialSignRow:
//特殊标记行
string specialSignStr = datas[0];
if(specialSignStr.Length >= 4)

excelMediumData.isValid = specialSignStr[0] == T;
excelMediumData.isCreateCSharp = specialSignStr[1] == T;
excelMediumData.isCreateAssignment = specialSignStr[2] == T;
excelMediumData.isCreateAsset = specialSignStr[3] == T;

else

Debug.LogError("未解析到特殊标记");

break;
case excelNodeRow:
//数据注释行
excelMediumData.propertyNodeArray = datas;
break;
case excelNameRow:
//数据名称行
excelMediumData.propertyNameArray = datas;
//注释列号
for(int i = 0; i < datas.Length; i++)

if(string.IsNullOrEmpty(datas[i]) || datas[i].StartsWith(annotationSign))
excelMediumData.annotationColList.Add(i);

break;
case excelTypeRow:
//数据类型行
excelMediumData.propertyTypeArray = datas;
break;
default:
//数据内容行
excelMediumData.allRowItemList.Add(datas);
//注释行号
if(string.IsNullOrEmpty(datas[0]) || datas[0].StartsWith(annotationSign))
excelMediumData.annotationRowList.Add(excelMediumData.allRowItemList.Count - 1);
break;

//
curRowIndex++;


if(CheckExcelMediumData(ref excelMediumData))

Debug.Log("读取Excel成功");
return excelMediumData;

else

Debug.LogError("读取Excel失败");
return null;



//校验Excel数据
private static bool CheckExcelMediumData(ref)

if(mediumData == null)
return false;

//检查数据有效性

if(!mediumData.isValid)

Debug.LogError("Excel被标记无效");
return false;


if(string.IsNullOrEmpty(mediumData.excelName))

Debug.LogError("Excel名字为空");
return false;


if(mediumData.propertyNameArray == null || mediumData.propertyNameArray.Length == 0)

Debug.LogError("未解析到数据名称");
return false;

if(mediumData.propertyTypeArray == null || mediumData.propertyTypeArray.Length == 0)

Debug.LogError("未解析到数据类型");
return false;

if(mediumData.propertyNameArray.Length != mediumData.propertyTypeArray.Length)

Debug.LogError("数据名称与数据类型数量不一致");
return false;

if(mediumData.allRowItemList.Count == 0)

Debug.LogError("数据内容为空");
return false;


if(mediumData.propertyNameArray[0] != "id")

Debug.LogError("第一个字段必须是id字段");
return false;


return true;


#endregion

【Unity】升级版·Excel数据解析,自动创建对应C#类,自动创建ScriptableObject生成类,自动序列化Asset文件_游戏开发_15

C#代码生成类:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Text;
using System.Linq;
using System;

public class ExcelCodeCreater


//创建代码,生成数据C#类
public static string CreateCodeStrByExcelData(ExcelMediumData excelMediumData)

if(excelMediumData == null)
return null;

//行数据类名
string itemClassName = excelMediumData.excelName + "ExcelItem";
//整体数据类名
string dataClassName = excelMediumData.excelName + "ExcelData";

//开始生成类
StringBuilder classSource = new StringBuilder();
classSource.AppendLine("/*Auto Create, Dont Edit !!!*/");
classSource.AppendLine();
//添加引用
classSource.AppendLine("using UnityEngine;");
classSource.AppendLine("using System.Collections.Generic;");
classSource.AppendLine("using System;");
classSource.AppendLine("using System.IO;");
classSource.AppendLine();
//生成CSharp数据类
if(excelMediumData.isCreateCSharp)

//生成行数据类,记录每行数据
classSource.AppendLine(CreateExcelRowItemClass(itemClassName,excelMediumData));
classSource.AppendLine();
//生成整体数据类,记录整个Excel的所有行数据
classSource.AppendLine(CreateExcelAllDataClass(dataClassName,itemClassName,excelMediumData));
classSource.AppendLine();

//生成Asset创建类
if(excelMediumData.isCreateAssignment)

//生成Asset操作类,用于自动创建Excel对应的Asset文件并赋值
classSource.AppendLine(CreateExcelAssetClass(excelMediumData));
classSource.AppendLine();

//
return classSource.ToString();


//----------

//生成行数据类
private static string CreateExcelRowItemClass(string)

//生成Excel行数据类
StringBuilder classSource = new StringBuilder();
//类名
classSource.AppendLine("[Serializable]");
classSource.AppendLine($"public class itemClassName);
classSource.AppendLine("");
//声明所有字段
for(int i = 0; i < excelMediumData.propertyNameArray.Length; i++)

//跳过注释字段
if(excelMediumData.annotationColList.Contains(i))
continue;

//添加注释
if(i < excelMediumData.propertyNodeArray.Length)

string propertyNode = excelMediumData.propertyNodeArray[i];
if(!string.IsNullOrEmpty(propertyNode))

classSource.AppendLine("\\t/// <summary>");
classSource.AppendLine($"\\t/// propertyNode");
classSource.AppendLine("\\t/// </summary>>");



//声明行数据类的字段
string propertyName = excelMediumData.propertyNameArray[i];
string propertyType = excelMediumData.propertyTypeArray[i];
string typeStr = GetPropertyType(propertyType);
classSource.AppendLine($"\\tpublic typeStr propertyName;");

classSource.AppendLine("");
return classSource.ToString();


//----------

//生成整体数据类
private static string CreateExcelAllDataClass(string dataClassName,string)

StringBuilder classSource = new StringBuilder();
//类名
classSource.AppendLine($"public class dataClassName : ExcelDataBase<itemClassName>");
classSource.AppendLine("");
//声明字段,行数据类数组
classSource.AppendLine($"\\tpublic itemClassName[] items;");
classSource.AppendLine();
//id字段类型
string idTypeStr = GetPropertyType(excelMediumData.propertyTypeArray[0]);
//声明字典
classSource.AppendLine($"\\tpublic Dictionary<idTypeStr,itemClassName> itemDic = new Dictionary<idTypeStr,itemClassName>();");
classSource.AppendLine();
//字段初始化方法
classSource.AppendLine("\\tpublic void Init()");
classSource.AppendLine("\\t");
classSource.AppendLine("\\t\\titemDic.Clear();");
classSource.AppendLine("\\t\\tif(items != null && items.Length > 0)");
classSource.AppendLine("\\t\\t");
classSource.AppendLine("\\t\\t\\tfor(int i = 0; i < items.Length; i++)");
classSource.AppendLine("\\t\\t\\t");
classSource.AppendLine("\\t\\t\\t\\titemDic.Add(items[i].id, items[i]);");
classSource.AppendLine("\\t\\t\\t");
classSource.AppendLine("\\t\\t");
classSource.AppendLine("\\t");
classSource.AppendLine();
//字典获取方法
classSource.AppendLine($"\\tpublic itemClassName GetitemClassName(idTypeStr);
classSource.AppendLine("\\t");
classSource.AppendLine("\\t\\tif(itemDic.ContainsKey(id))");
classSource.AppendLine("\\t\\t\\treturn itemDic[id];");
classSource.AppendLine("\\t\\telse");
classSource.AppendLine("\\t\\t\\treturn null;");
classSource.AppendLine("\\t");

//每个字段Get函数
classSource.AppendLine("\\t#region --- Get Method ---");
classSource.AppendLine();

for(int i = 1; i < excelMediumData.propertyNameArray.Length; i++)

if(excelMediumData.annotationColList.Contains(i))
continue;
string propertyName = excelMediumData.propertyNameArray[i];
string propertyType = excelMediumData.propertyTypeArray[i];
//每个字段Get函数
classSource.AppendLine(CreateCodePropertyMethod(itemClassName,idTypeStr,propertyName,propertyType));

classSource.AppendLine("\\t#endregion");
classSource.AppendLine("");
return classSource.ToString();


//生成数据字段对应Get方法
private static string CreateCodePropertyMethod(string itemClassName,string idTypeStr,string propertyName,string)

StringBuilder methodBuilder = new StringBuilder();
string itemNameStr = propertyName.FirstOrDefault().ToString().ToUpper() + propertyName.Substring(1);
string itemTypeStr = GetPropertyType(propertyType);
//字段Get函数
methodBuilder.AppendLine($"\\tpublic itemTypeStr GetitemNameStr(idTypeStr);
methodBuilder.AppendLine("\\t");
methodBuilder.AppendLine($"\\t\\tvar item = GetitemClassName(id);");
methodBuilder.AppendLine("\\t\\tif(item == null)");
methodBuilder.AppendLine("\\t\\t\\treturn default;");
methodBuilder.AppendLine($"\\t\\treturn item.propertyName;");
methodBuilder.AppendLine("\\t");
//如果是一维数组
if(propertyType.Contains("[]"))

//typeStr:int[]或IntArr[] ,返回值:int或IntArr
//string itemTypeStr1d = GetPropertyType(propertyType.Replace("[]",""));
string itemTypeStr1d = itemTypeStr.Replace("[]","");
methodBuilder.AppendLine($"\\tpublic itemTypeStr1d GetitemNameStr(idTypeStr);
methodBuilder.AppendLine("\\t");
methodBuilder.AppendLine($"\\t\\tvar item0 = GetitemClassName);
methodBuilder.AppendLine("\\t\\tif(item0 == null)");
methodBuilder.AppendLine("\\t\\t\\treturn default;");
methodBuilder.AppendLine($"\\t\\tvar item1 = item0.propertyName;");
methodBuilder.AppendLine("\\t\\tif(item1 == null || index < 0 || index >= item1.Length)");
methodBuilder.AppendLine("\\t\\t\\treturn default;");
methodBuilder.AppendLine("\\t\\treturn item1[index];");
methodBuilder.AppendLine("\\t");

//如果是二维数组
if(propertyType.Contains("[][]"))

//propertyType:int[][], 返回值:int
string itemTypeStr1d = GetPropertyType(propertyType.Replace("[][]",""));
methodBuilder.AppendLine($"\\tpublic itemTypeStr1d GetitemNameStr(idTypeStr);
methodBuilder.AppendLine("\\t");
methodBuilder.AppendLine($"\\t\\tvar item0 = GetitemClassName(id);");
methodBuilder.AppendLine("\\t\\tif(item0 == null)");
methodBuilder.AppendLine("\\t\\t\\treturn default;");
methodBuilder.AppendLine($"\\t\\tvar item1 = item0.propertyName;");
methodBuilder.AppendLine("\\t\\tif(item1 == null || index1 < 0 || index1 >= item1.Length)");
methodBuilder.AppendLine("\\t\\t\\treturn default;");
methodBuilder.AppendLine("\\t\\tvar item2 = item1[index1];");
methodBuilder.AppendLine("\\t\\tif(item2.array == null || index2 < 0 || index2 >= item2.array.Length)");
methodBuilder.AppendLine("\\t\\t\\treturn default;");
methodBuilder.AppendLine("\\t\\treturn item2.array[index2];");
methodBuilder.AppendLine("\\t");

//
return methodBuilder.ToString();


//----------

//生成Asset创建类
private static string CreateExcelAssetClass(ExcelMediumData excelMediumData)

string itemClassName = excelMediumData.excelName + "ExcelItem";
string dataClassName = excelMediumData.excelName + "ExcelData";
string assignmentClassName = excelMediumData.excelName + "AssetAssignment";

StringBuilder classSource = new StringBuilder();
classSource.AppendLine("#if UNITY_EDITOR");
//类名
classSource.AppendLine($"public class assignmentClassName");
classSource.AppendLine("");
//方法名
classSource.AppendLine("\\tpublic static bool CreateAsset(ExcelMediumData excelMediumData, string excelAssetPath)");
//方法体,若有需要可加入try/catch
classSource.AppendLine("\\t");
classSource.AppendLine("\\t\\tvar allRowItemDicList = excelMediumData.GetAllRowItemDicList();");
classSource.AppendLine("\\t\\tif(allRowItemDicList == null || allRowItemDicList.Count == 0)");
classSource.AppendLine("\\t\\t\\treturn false;");
classSource.AppendLine();
classSource.AppendLine("\\t\\tint rowCount = allRowItemDicList.Count;");
classSource.AppendLine($"\\t\\tdataClassName excelDataAsset = ScriptableObject.CreateInstance<dataClassName>();");
classSource.AppendLine($"\\t\\texcelDataAsset.items = new itemClassName[rowCount];");
classSource.AppendLine();
classSource.AppendLine("\\t\\tfor(int i = 0; i < rowCount; i++)");
classSource.AppendLine("\\t\\t");
classSource.AppendLine("\\t\\t\\tvar itemRowDic = allRowItemDicList[i];");
classSource.AppendLine($"\\t\\t\\texcelDataAsset.items[i] = new itemClassName();");

for(int i = 0; i < excelMediumData.propertyNameArray.Length; i++)

if(excelMediumData.annotationColList.Contains(i))
continue;
string propertyName = excelMediumData.propertyNameArray[i];
string propertyType = excelMediumData.propertyTypeArray[i];
classSource.Append($"\\t\\t\\texcelDataAsset.items[i].propertyName);
classSource.Append(AssignmentCodeProperty(propertyName,propertyType));
classSource.AppendLine(";");

classSource.AppendLine("\\t\\t");
classSource.AppendLine("\\t\\tif(!Directory.Exists(excelAssetPath))");
classSource.AppendLine("\\t\\t\\tDirectory.CreateDirectory(excelAssetPath);");
classSource.AppendLine($"\\t\\tstring fullPath = Path.Combine(excelAssetPath,typeof(dataClassNam

以上是关于Unity升级版·Excel数据解析,自动创建对应C#类,自动创建ScriptableObject生成类,自动序列化Asset文件的主要内容,如果未能解决你的问题,请参考以下文章

Unity中基于EPPLUS的Excel转换以及Json数据读取

【Unity】读取Excel工具

如何创建 python+requests接口自动化测试框架

js解析json文件并生成对应的表格

有json数据如何填到excel里

Unity中解析Excel表格工具