Unity升级版·Excel数据解析,自动创建对应C#类,自动创建ScriptableObject生成类,自动序列化Asset文件
Posted 萧然CS
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity升级版·Excel数据解析,自动创建对应C#类,自动创建ScriptableObject生成类,自动序列化Asset文件相关的知识,希望对你有一定的参考价值。
实现功能:
- 自动创建继承ScriptableObject的C#数据类,每条Excel的数据,都有对应的字段的Get函数;
- 自动创建每个Excel的Asset生成类和生成函数,用于自动生成Asset文件
- 使用Asset生成类自动序列化Excel数据到Asset文件,可直接在项目运行时加载使用
实现原理:
Excel配置格式:
- 第1行对应特殊标记(可以设置有效性,指定要创建的文件)
- 第2行对应中文说明(作为第3行字段的注释)
- 第3行对应字段名称(自动创建的字段名称)
- 第4行对应字段类型(自动创建的字段类型,与字段名称一一对应)
- 第5行及以后对应字段值(所有数据,以行为单位解析、保存数据)
- 第一列固定字段为"id",是代码中索引每行数据的Key
Excel注释操作:
- 字段名称行,每个字段单元格内容前加"//",可以注释该字段,不会解析生成到C#类;
- 第一列的单元格内容前加"//",可以注释一行数据,不会保存到Asset文件中;
- 注释可以用于添加说明行,或剔除指定无用数据。
生成的C#类格式:
行数据类,对应每一行数据:
[Serializable]
public class TestConfigExcelItem : ExcelItemBase
/// <summary>
/// 数据id
/// </summary>>
public int id;
/// <summary>
/// 字符串
/// </summary>>
public string testString;
/// <summary>
/// Int
/// </summary>>
public int testInt;
/// <summary>
/// Float
/// </summary>>
public float testFloat;
完整数据类,包含所有行的数据、初始化函数、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 id)
if(itemDic.ContainsKey(id))
return itemDic[id];
else
return null;
#region --- Get Method ---
public string GetTestString(int id)
var item = GetTestConfigExcelItem(id);
if(item == null)
return default;
return item.testString;
// ··· ···
#endregion
目前支持的数据结构:
字符串 | 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[]
Asset数据文件:
在自动生成数据的C#类时,会同步生成Asset文件的创建类,用于自动创建Asset文件并序列化数据。
优点:
- 数据修改后只需要重新一键生成即可
- 每个Excel对应一个类,使用灵活,对Excel限制少
- 自动创建C#类,不需要对每个Excel手动写代码,每条数据对应字段,不需要拆箱装修
- 自动创建ScriptableObject的Asset文件,自动序列化数据,方便查看,可以手动修改调整,不需要每次改动都在Excel里操作
- 在游戏内直接读取Asset的ScriptableObject子类,不需要额外操作,业务层直接调取数据字段
使用方法:
- 按照标准格式配置Excel
- 一键生成C#类、Asset文件
- 项目运行时加载Asset资源,调用Init初始化,Get函数获取对应字段值即可
完整代码:
扩展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 targetPath)
Object targetObj = AssetDatabase.LoadAssetAtPath<Object>(targetPath);
EditorGUIUtility.PingObject(targetObj);
Selection.activeObject = targetObj;
Debug.Log(targetPath);
private static string CheckEditorPath(string path)
#if UNITY_EDITOR_WIN
return path.Replace("/","\\\\");
#elif UNITY_EDITOR_OSX
return path.Replace("\\\\","/");
#else
return path;
#endif
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 --- Read Excel ---
//创建Excel对应的C#类
public static void ReadAllExcelToCode(string allExcelPath,string codeSavePath)
//读取所有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 codeSavePath)
//解析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 --- Create Asset ---
//创建Excel对应的Asset数据文件
public static void CreateAllExcelAsset(string allExcelPath,string assetSavePath)
//读取所有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 assetSavePath)
//解析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 --- private ---
//解析Excel,创建中间数据
private static ExcelMediumData CreateClassCodeByExcelPath(string excelFileFullPath)
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 ExcelMediumData mediumData)
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
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, Don't 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 itemClassName,ExcelMediumData excelMediumData)
//生成Excel行数据类
StringBuilder classSource = new StringBuilder();
//类名
classSource.AppendLine("[Serializable]");
classSource.AppendLine($"public class itemClassName : ExcelItemBase");
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 itemClassName,ExcelMediumData excelMediumData)
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 id)");
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 propertyType)
StringBuilder methodBuilder = new StringBuilder();
string itemNameStr = propertyName.FirstOrDefault().ToString().ToUpper() + propertyName.Substring(1);
string itemTypeStr = GetPropertyType(propertyType);
//字段Get函数
methodBuilder.AppendLine($"\\tpublic itemTypeStr GetitemNameStr(idTypeStr id)");
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 id, int index)");
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 || 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 id, int index1, int index2)");
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(dataClassName).Name) + \\".asset\\";");
classSource.AppendLine("\\t\\tUnityEditor.AssetDatabase.DeleteAsset(fullPath);");
classSource.AppendLine("\\t\\tUnityEditor.AssetDatabase.CreateAsset(excelDataAsset,fullPath);");
classSource.AppendLine("\\t\\tUnityEditor.AssetDatabase.Refresh();");
classSource.AppendLine("\\t\\treturn true;");
classSource.AppendLine("\\t");
//
classSource.AppendLine("");
classSource.AppendLine("#endif");
return classSource.ToString();
//声明Asset操作类字段
private static string AssignmentCodeProperty(string propertyName,string propertyType)
string stringValue = $"itemRowDic[\\"propertyName\\"]";
string typeStr = GetPropertyType(propertyType);
switch(typeStr)
//字段
case "int":
return "StringUtility.StringToInt(" + stringValue + ")";
case "float":
return "StringUtility.StringToFloat(" + stringValue + ")";
case "bool":
return "StringUtility.StringToBool(" + stringValue + ")";
case "Vector2":
return "StringUtility.StringToVector2(" + stringValue + ")";
case "Vector3":
return "StringUtility.StringToVector3(" + stringValue + ")";
case "Vector2Int":
return "StringUtility.StringToVector2Int(" + stringValue + ")";
case "Vector3Int":
return "StringUtility.StringToVector3Int(" + stringValue + ")";
case "Color":
return "StringUtility.StringToColor(" + stringValue + ")";
case "Color32":
return "StringUtility.StringToColor32(" + stringValue + ")";
case "string":
return stringValue;
//一维
case "int[]":
return "StringUtility.StringToIntArray(" + stringValue + ")";
case "float[]":
return "StringUtility.StringToFloatArray(" + stringValue + ")";
case "bool[]":
return "StringUtility.StringToBoolArray(" + stringValue + ")";
case "Vector2[]":
return "StringUtility.StringToVector2Array(" + stringValue + ")";
case "Vector3[]":
return "StringUtility.StringToVector3Array(" + stringValue + ")";
case "Vector2Int[]":
return "StringUtility.StringToVector2IntArray(" + stringValue + ")";
case "Vector3Int[]":
return "StringUtility.StringToVector3IntArray(" + stringValue + ")";
case "Color[]":
return "StringUtility.StringToColorArray(" + stringValue + ")";
case "Color32[]":
return "StringUtility.StringToColor32Array(" + stringValue + ")";
case "string[]":
return "StringUtility.StringToStringArray(" + stringValue + ")";
//二维
case "IntArr[]":
return "StringUtility.StringToIntArray2D(" + stringValue + ")";
case "FloatArr[]":
return "StringUtility.StringToFloatArray2D(" + stringValue + ")";
case "BoolArr[]":
return "StringUtility.StringToBoolArray2D(" + stringValue + ")";
case "Vector2Arr[]":
return "StringUtility.StringToVector2Array2D(" + stringValue + ")";
case "Vector3Arr[]":
return "StringUtility.StringToVector3Array2D(" + stringValue + ")";
case "Vector2IntArr[]":
return "StringUtility.StringToVector2IntArray2D(" + stringValue + ")";
case "Vector3IntArr[]":
return "StringUtility.StringToVector3IntArray2D(" + stringValue + ")";
case "ColorArr[]":
return "StringUtility.StringToColorArray2D(" + stringValue + ")";
case "Color32Arr[]":
return "StringUtility.StringToColor32Array2D(" + stringValue + ")";
case "StringArr[]":
return "StringUtility.StringToStringArray2D(" + stringValue + ")";
default:
//枚举
if(propertyType.StartsWith("enum"))
string enumType = propertyType.Split('|').FirstOrDefault();
string enumName = propertyType.Split('|').LastOrDefault();
if(enumType == "enum")
return "StringUtility.StringToEnum<" + enumName + ">(" + stringValue + ")";
else if(enumType == "enum[]")
return "StringUtility.StringToEnumArray<" + enumName + ">(" + stringValue + ")";
else if(enumType == "enum[][]")
return "StringUtility.StringToEnumArray2D<" + enumName + ">(" + stringValue + ")";
return stringValue;
//判断字段类型
private static string GetPropertyType(string propertyType)
string lowerType = propertyType.ToLower();
switch(lowerType)
case "int":
return "int";
case "int[]":
return "int[]";
case "int[][]":
return "IntArr[]";
case "float":
return "float";
case "float[]":
return "float[]";
case "float[][]":
return "FloatArr[]";
case "bool":
return "bool";
case "bool[]":
return "bool[]";
case "bool[][]":
return "BoolArr[]";
case "string":
return "string";
case "string[]":
return "string[]";
case "string[][]":
return "StringArr[]";
case "vector2":
return "Vector2";
case "vector2[]":
return "Vector2[]";
case "vector2[][]":
return "Vector2Arr[]";
case "vector2int":
return "Vector2Int";
case "vector2int[]":
return "Vector2Int[]";
case "vector2int[][]":
return "Vector2IntArr[]";
case "vector3":
return "Vector3";
case "vector3[]":
return "Vector3[]";
case "vector3[][]":
return "Vector3Arr[]";
case "vector3int":
return "Vector3Int";
case "vector3int[]":
return "Vector3Int[]";
case "vector3int[][]":
return "Vector3IntArr[]";
case "color":
return "Color";
case "color[]":
return "Color[]";
case "color[][]":
return "ColorArr[]";
case "color32":
return "Color32";
case "color32[]":
return "Color32[]";
case "color32[][]":
return "Color32Arr[]";
default:
if(propertyType.StartsWith("enum"))
string enumType = propertyType.Split('|').FirstOrDefault();
string enumName = propertyType.Split('|').LastOrDefault();
switch(enumType)
case "enum":
return enumName;
case "enum[]":
return $"enumName[]";
case "enum[][]":
return $"EnumArr<enumName>[]";
default:
break;
return "string";
Excel数据中间类:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//Excel中间数据
public class ExcelMediumData
//Excel名字
public string excelName;
//Excel是否有效
public bool isValid = false;
//是否生成CSharp数据类
public bool isCreateCSharp = false;
//是否生成Asset创建类
public bool isCreateAssignment = false;
//是否生成Asset文件
public bool isCreateAsset = false;
//数据注释
public string[] propertyNodeArray = null;
//数据名称
public string[] propertyNameArray = null;
//数据类型
public string[] propertyTypeArray = null;
//List<每行数据内容>
public List<string[]> allRowItemList = new List<string[]>();
//注释行号
public List<int> annotationRowList = new List<int>();
//注释列号
public List<int> annotationColList = new List<int>();
//List<每行数据>,List<Dictionary<单元格字段名称, 单元格字段值>>
public List<Dictionary<string,string>> GetAllRowItemDicList()
if(propertyNameArray == null || propertyNameArray.Length == 0)
return null;
if(allRowItemList.Count == 0)
return null;
List<Dictionary<string,string>> allRowItemDicList = new List<Dictionary<string,string>>(allRowItemList.Count);
for(int i = 0; i < allRowItemList.Count; i++)
string[] rowArray = allRowItemList[i];
//跳过空数据
if(rowArray == null || rowArray.Length == 0)
continue;
//跳过注释数据
if(annotationRowList.Contains(i))
continue;
//每行数据,对应字段名称和字段值
Dictionary<string,string> rowDic = new Dictionary<string,string>();
for(int j = 0; j < propertyNameArray.Length; j++)
//跳过注释字段
if(annotationColList.Contains(j))
continue;
string propertyName = propertyNameArray[j];
string propertyValue = j < rowArray.Length ? rowArray[j] : null;
rowDic[propertyName] = propertyValue;
allRowItemDicList.Add(rowDic);
return allRowItemDicList;
Excel数据基类、扩展类:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using System;
public class ExcelDataBase<T> : ScriptableObject where T : ExcelItemBase
public class ExcelItemBase
[Serializable]
public struct StringArr
public string[] array;
[Serializable]
public struct IntArr
public int[] array;
[Serializable]
public struct FloatArr
public float[] array;
[Serializable]
public struct BoolArr
public bool[] array;
[Serializable]
public struct Vector2Arr
public Vector2[] array;
[Serializable]
public struct Vector3Arr
public Vector3[] array;
[Serializable]
public struct Vector2IntArr
public Vector2Int[] array;
[Serializable]
public struct Vector3IntArr
public Vector3Int[] array;
[Serializable]
public struct ColorArr
public Color[] array;
[Serializable]
public struct Color32Arr
public Color32[] array;
不支持泛型枚举序列化
//[Serializable]
//public struct EnumArr<T> where T : Enum
//
// public T[] array;
//
字符串工具类:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Text.RegularExpressions;
using System;
using System.Text;
using System.Linq;
using System.Runtime.CompilerServices;
public static class StringUtility
#region --- AddColor ---
public static string AddColor(object obj,Color color)
return AddColor(obj,color);
public static string AddColor(this string str,Color color)
//把颜色转换为16进制字符串,添加到富文本
return string.Format("<color=#0>1</color>",ColorUtility.TohtmlStringRGBA(color),str);
public static string AddColor(string str1,string str2,Color color)
return AddColor(str1 + str2,color);
public static string AddColor(string str1,string str2,string str3,Color color)
return AddColor(str1 + str2 + str3,color);
#endregion
#region --- string length ---
/// <summary>
/// 化简字符串长度
/// </summary>
/// <param name="targetStr"></param>
/// <param name="targetLength">目标长度,英文字符==1,中文字符==2</param>
/// <returns></returns>
public static string AbbrevStringWithinLength(string targetStr,int targetLength,string abbrevPostfix)
//C#实际统计:一个中文字符长度==1,英文字符长度==1
//UI显示要求:一个中文字符长度==2,英文字符长度==1
//校验参数
if(string.IsNullOrEmpty(targetStr) || targetLength <= 0)
return targetStr;
//字符串长度 * 2 <= 目标长度,即使是全中文也在长度范围内
if(targetStr.Length * 2 <= targetLength)
return targetStr;
//遍历字符
char[] chars = targetStr.ToCharArray();
int curLen = 0;
for(int i = 0; i < chars.Length; i++)
//累加字符串长度
if(chars[i] >= 0x4e00 && chars[i] <= 0x9fbb)
curLen += 2;
else
curLen += 1;
//如果当前位置累计长度超过目标长度,取0~i-1,即Substring(0,i)
if(curLen > targetLength)
return targetStr.Substring(0,i) + abbrevPostfix;
return targetStr;
#endregion
#region --- String To Array ---
//string
public static byte StringToByte(string valueStr)
byte value;
if(byte.TryParse(valueStr,out value))
return value;
else
return 0;
public static string[] StringToStringArray(string valueStr,char splitSign = '|')
if(string.IsNullOrEmpty(valueStr))
return null;
return valueStr.Split(splitSign);
public static StringArr[] StringToStringArray2D(string valueStr,char splitSign1 = '&',char splitSign2 = '|')
if(string.IsNullOrEmpty(valueStr))
return null;
string[] strArr1 = valueStr.Split(splitSign1);
if(strArr1.Length == 0)
return null;
StringArr[] arrArr = new StringArr[strArr1.Length];
for(int i = 0; i < strArr1.Length; i++)
arrArr[i] = new StringArr()
array = strArr1[i].Split(splitSign2)
;
return arrArr;
//int
public static int StringToInt(string valueStr)
int value;
if(int.TryParse(valueStr,out value))
return value;
else
return 0;
public static int[] StringToIntArray(string valueStr,char splitSign = '|')
if(string.IsNullOrEmpty(valueStr))
return null;
string[] valueArr = valueStr.Split(splitSign);
if(valueArr == null || valueArr.Length == 0)
return null;
int[] intArr = new int[valueArr.Length];
for(int i = 0; i < valueArr.Length; i++)
intArr[i] = StringToInt(valueArr[i]);
return intArr;
public static IntArr[] StringToIntArray2D(string valueStr,char splitSign1 = '&',char splitSign2 = '|')
if(string.IsNullOrEmpty(valueStr))
return null;
string[] strArr1 = valueStr.Split(splitSign1);
if(strArr1.Length == 0)
return null;
IntArr[] arrArr = new IntArr[strArr1.Length];
for(int i = 0; i < strArr1.Length; i++)
arrArr[i] = new IntArr()
array = StringToIntArray(strArr1[i],splitSign2)
;
return arrArr;
//float
public static float StringToFloat(string valueStr)
float value;
if(float.TryParse(valueStr,out value))
return value;
else
return 0;
public static float[] StringToFloatArray(string valueStr,char splitSign = '|')
if(string.IsNullOrEmpty(valueStr))
return null;
string[] valueArr = valueStr.Split(splitSign);
if(valueArr == null || valueArr.Length == 0)
return null;
float[] floatArr = new float[valueArr.Length];
for(int i = 0; i < valueArr.Length; i++)
floatArr[i] = StringToFloat(valueArr[i]);
return floatArr;
public static FloatArr[] StringToFloatArray2D(string valueStr,char splitSign1 = '&',char splitSign2 = '|')
if(string.IsNullOrEmpty(valueStr))
return null;
string[] strArr1 = valueStr.Split(Unity中基于EPPLUS的Excel转换以及Json数据读取
摘要:
主要使用Epplus的的表格转换功能,将表格转换成Json,而Unity中Json的使用要搭配对应结构的类,故顺便将利用表格结构生成对应的类,免除人工创建的麻烦过程。
示例:
表格示例:
编辑模式下:
生成Json:
生成Class:
运行时数据读取:
代码
Json数据读取器以及泛型基类
using UnityEngine;
public class JsonDataManager : Singleton<JsonDataManager>
public JsonDataList<T> LoadData<T>()
string json = Resources.Load<TextAsset>("Json/" + typeof(T).Name).text;
return JsonUtility.FromJson<JsonDataList<T>>(json);
using System.Collections.Generic;
using System;
[Serializable]
public class JsonDataList<T>
public List<T> datas = new List<T>();
Excel转换类
using UnityEngine;
using UnityEditor;
using System.IO;
using OfficeOpenXml;
using System.Collections.Generic;
using System;
using System.Text;
/// <summary>
/// 使用EPPlus获取表格数据,同时导出对应的Json以及Class.
/// </summary>
public class ExcelExporter
/// <summary>
/// Excel表格路径
/// </summary>
private const string excelPath = "../Assets/Excels";
/// <summary>
/// 导出的Json路径
/// </summary>
private const string configPath = "../Assets/Resources/Json";
/// <summary>
/// 导出的类路径
/// </summary>
private const string classPath = "../Assets/Scripts/Configs";
/// <summary>
/// 属性行
/// </summary>
private const int propertyIndex = 2;
/// <summary>
/// 类型行
/// </summary>
private const int typeIndex = 3;
/// <summary>
/// 值行
/// </summary>
private const int valueIndex = 4;
[MenuItem("Tools/ExportExcel")]
private static void ExportConfigs()
try
FileInfo[] files = Files.LoadFiles(excelPath);
foreach (var file in files)
//过滤文件
if (file.Extension != ".xlsx") continue;
ExcelPackage excelPackage = new ExcelPackage(file);
ExcelWorksheets worksheets = excelPackage.Workbook.Worksheets;
//只导表1
ExcelWorksheet worksheet = worksheets[1];
ExportJson(worksheet, Path.GetFileNameWithoutExtension(file.FullName));
ExportClass(worksheet, Path.GetFileNameWithoutExtension(file.FullName));
AssetDatabase.Refresh();
catch (Exception e)
Debug.LogError(e.ToString());
/// <summary>
/// 导出类
/// </summary>
private static void ExportClass(ExcelWorksheet worksheet, string fileName)
string[] properties = GetProperties(worksheet);
StringBuilder sb = new StringBuilder();
sb.Append("using System;\\t\\n");
sb.Append("[Serializable]\\t\\n");
sb.Append($"public class fileNameConfig\\n");//类名
sb.Append("\\n");
for (int col = 1; col <= properties.Length; col++)
string fieldType = GetType(worksheet, col);
string fieldName = properties[col - 1];
sb.Append($"\\tpublic fieldType fieldName;\\n");
sb.Append("\\n\\n");
Files.SaveFile(classPath, string.Format("0Config.cs", fileName), sb.ToString());
/// <summary>
/// 导出JSON
/// </summary>
private static void ExportJson(ExcelWorksheet worksheet, string fileName)
string str = "";
int num = 0;
string[] properties = GetProperties(worksheet);
for (int col = 1; col <= properties.Length; col++)
string[] temp = GetValues(worksheet, col);
num = temp.Length;
foreach (var value in temp)
str += GetJsonK_VFromKeyAndValues(properties[col - 1],
Convert(GetType(worksheet, col), value)) + \',\';
//获取key:value的字符串
str = str.Substring(0, str.Length - 1);
str = GetJsonFromJsonK_V(str, num);
str = GetUnityJsonFromJson(str);
Files.SaveFile(configPath, string.Format("0Config.json", fileName), str);
/// <summary>
/// 获取属性
/// </summary>
private static string[] GetProperties(ExcelWorksheet worksheet)
string[] properties = new string[worksheet.Dimension.End.Column];
for (int col = 1; col <= worksheet.Dimension.End.Column; col++)
if (worksheet.Cells[propertyIndex, col].Text == "")
throw new System.Exception(string.Format("第0行第1列为空", propertyIndex, col));
properties[col - 1] = worksheet.Cells[propertyIndex, col].Text;
return properties;
/// <summary>
/// 获取值
/// </summary>
private static string[] GetValues(ExcelWorksheet worksheet, int col)
//容量减去前三行
string[] values = new string[worksheet.Dimension.End.Row - 3];
for (int row = valueIndex; row <= worksheet.Dimension.End.Row; row++)
values[row - valueIndex] = worksheet.Cells[row, col].Text;
return values;
/// <summary>
/// 获取类型
/// </summary>
private static string GetType(ExcelWorksheet worksheet, int col)
return worksheet.Cells[typeIndex, col].Text;
/// <summary>
/// 通过类型返回对应值
/// </summary>
private static string Convert(string type, string value)
string res = "";
switch (type)
case "int": res = value; break;
case "int32": res = value; break;
case "int64": res = value; break;
case "long": res = value; break;
case "float": res = value; break;
case "double": res = value; break;
case "string": res = $"\\"value\\""; break;
default:
throw new Exception($"不支持此类型: type");
return res;
/// <summary>
/// 返回key:value
/// </summary>
private static string GetJsonK_VFromKeyAndValues(string key, string value)
return string.Format("\\"0\\":1", key, value);
/// <summary>
///获取[key:value]转换为key:value,key:value,再变成[key:value,key:value,key:value,key:value]
/// </summary>
private static string GetJsonFromJsonK_V(string json, int valueNum)
string str = "";
string[] strs;
List<string> listStr = new List<string>();
strs = json.Split(\',\');
listStr.Clear();
for (int j = 0; j < valueNum; j++)
listStr.Add("" + string.Format("0,1", strs[j], strs[j + valueNum]) + "");
str = "[";
foreach (var l in listStr)
str += l + \',\';
str = str.Substring(0, str.Length - 1);
str += \']\';
return str;
/// <summary>
/// 适应JsonUtility.FromJson函数的转换格式
/// </summary>
private static string GetUnityJsonFromJson(string json)
return "" + "\\"datas\\":" + json + "";
文件读写的辅助类
using UnityEngine;
using System.IO;
using System.Collections.Generic;
public class Files
public static FileInfo[] LoadFiles(string path)
path = string.Format("0/1", Application.dataPath, path);
if (Directory.Exists(path))
DirectoryInfo directory = new DirectoryInfo(path);
List<FileInfo> files = new List<FileInfo>();
foreach (var file in directory.GetFiles("*"))
if (file.Name.EndsWith(".meta"))
continue;
if (file.Name.StartsWith("~")) continue;
files.Add(file);
return files.ToArray();
else
throw new System.Exception("路径不存在");
public static void SaveFile(string path, string fileName, string fileContent)
path = string.Format("0/1", Application.dataPath, path);
if (Directory.Exists(path))
path = string.Format("0/1", path, fileName);
if (File.Exists(path))
File.Delete(path);
File.WriteAllText(path, fileContent);
else
throw new System.Exception("路径不存在");
本文来自博客园,作者:C1earF,转载请注明原文链接:https://www.cnblogs.com/ameC1earF/p/17270090.html
以上是关于Unity升级版·Excel数据解析,自动创建对应C#类,自动创建ScriptableObject生成类,自动序列化Asset文件的主要内容,如果未能解决你的问题,请参考以下文章