【Unity】解析Excel数据,并自动创建对应的C#类

参考:Unity C#配置表工具

解析流程:

1. 配置Excel

2. 读取解析Excel (引用Excel.dll, ICSharpCode.SharpZipLib.dll)

3. 重新整理解析到的数据

4. 创建Excel对应的C#类,声明变量

5. 在类的构建函数中给变量赋值

6. 保存类文件,可在游戏直接使用

优点:

1. 每个Excel对应一个类,使用灵活,对Excel限制少

2. 自动创建脚本,不需要对每个Excel手动写代码

3. 数据值直接写到脚本里,方便查看,可以手动修改调整,不需要每次改动都在Excel里操作

4. 在游戏内直接调用类,不需要额外操作

缺点:

1. 表格格式确定后不能轻易修改,改动可能影响类的结构,代码报错

2. 因为是把数据直接写入到脚本内,数据量较大时可能会有问题

具体流程:

1. 配置Excel

 格式:

预留前两行,可以写一些备注;第三行为数据格式;第四行为变量名字,第五行以后是数据内容

第一列为id,后面每一列为一个变量

Sheet页的名字,是创建类的类名

2. 解析Excel

3. 整理数据

解析Excel时,是按照行顺序,一行一行解析的,将每行数据记录下来,方式有很多,能满足使用要求即可

//注释说明

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

public class ExcelReader
{
    const int excelTypeRow = 3;
    const int excelNameRow = 4;
    static string excelFilePath = Application.dataPath + "/Excel";
    static string excelCodePath = Application.dataPath + "/Script/Excel/AutoCreate";

    public static void Read()
    {
        //读取所有Excel文件
        string[] excelFiles = Directory.GetFiles(excelFilePath, "*.xlsx");

        if (excelFiles.Length == 0)
        {
            Debug.Log("Excel file count == 0");
            return;
        }

        //代码类
        List<KeyValuePair<string, string>> codeList = new List<KeyValuePair<string, string>>();

        //遍历所有Excel
        for (int i = 0; i < excelFiles.Length; i++)
        {
            //打开Excel
            string excelFile = excelFiles[i];
            FileStream stream = File.Open(excelFile, FileMode.Open, FileAccess.Read);
            //解析Excel
            IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream);
            if (!excelReader.IsValid)
            {
                Debug.Log("Invalid excel : " + excelFile);
                continue;
            }

            string[] propertyTypes = null;
            string[] propertyNames = null;
            List<List<KeyValuePair<string, string>>> allItemRowList = new List<List<KeyValuePair<string, string>>>();

            int rowIndex = 1;
            //开始读取
            while (excelReader.Read())
            {
                //读取每一行的数据
                string[] datas = new string[excelReader.FieldCount];
                for (int j = 0; j < excelReader.FieldCount; ++j)
                {
                    datas[j] = excelReader.GetString(j);
                }

                //空行不处理
                if (datas.Length == 0 || string.IsNullOrEmpty(datas[0]))
                {
                    rowIndex++;
                    continue;
                }

                if (rowIndex == excelTypeRow)
                {
                    //行数据表示类型
                    propertyTypes = datas;
                }
                else if (rowIndex == excelNameRow)
                {
                    //行数据表示变量名
                    propertyNames = datas;
                }
                else if (rowIndex > excelNameRow)
                {
                    //行数据表示变量值
                    List<KeyValuePair<string, string>> itemList = new List<KeyValuePair<string, string>>();
                    //遍历一行里的每个数据
                    //for (int j = 0; j < datas.Length; ++j)
                    //{
                    //    if (j >= names.Length)
                    //        continue;
                    //    //读取数据
                    //    itemList.Add(new KeyValuePair<string, string>(names[j], datas[j]));
                    //}
                    for (int j = 0; j < propertyNames.Length; j++)
                    {
                        if (j < datas.Length)
                            itemList.Add(new KeyValuePair<string, string>(propertyNames[j], datas[j]));
                        else
                            itemList.Add(new KeyValuePair<string, string>(propertyNames[j], null));
                    }
                    allItemRowList.Add(itemList);
                }
                rowIndex++;
            }

            if (propertyNames == null || propertyNames.Length == 0
                || propertyTypes == null || propertyTypes.Length == 0
                || propertyNames.Length != propertyTypes.Length)
                continue;

            //变量对应的所有值
            Dictionary<string, List<string>> propertyValueDic = new Dictionary<string, List<string>>();
            for (int k = 0; k < propertyNames.Length; k++)
            {
                propertyValueDic.Add(propertyNames[k], new List<string>());
            }
            for (int m = 0; m < allItemRowList.Count; m++)
            {
                for (int n = 0; n < allItemRowList[m].Count; n++)
                {
                    propertyValueDic[allItemRowList[m][n].Key].Add(allItemRowList[m][n].Value);
                }
            }

            //类名
            string className = excelReader.Name + "Table";
            //根据数据生成C#脚本
            ScriptGenerator generator = new ScriptGenerator(className, propertyNames, propertyTypes, propertyValueDic);
            string codeStr = generator.CreateCode();
            //生成的类
            codeList.Add(new KeyValuePair<string, string>(className, codeStr));
        }

        if (!Directory.Exists(excelCodePath))
            Directory.CreateDirectory(excelCodePath);
   
        for (int i = 0; i < codeList.Count; i++)
        {
            string className = codeList[i].Key;
            string codeStr = codeList[i].Value;

            StreamWriter sw = new StreamWriter(excelCodePath + "/" + className + ".cs");
            sw.WriteLine(codeStr);
            sw.Close();
        }

        UnityEditor.AssetDatabase.Refresh();
        Debug.Log("<color=green>Auto Scripts Success : </color>" + codeList.Count);
    }
}

4. 创建C#类

5. 变量赋值

 创建脚本,这里采用拼接字符串的方式,变量赋值也是拼接的字符串,然后将拼接好的文本保存为".cs"文件

在参考链接中是生成dll文件,这种方式感觉高级一些

方法有很多,不局限于这两种,能实现功能就好

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

public class ScriptGenerator
{
//脚本生成器

    string className;
    string[] propertyNames;
    string[] propertyTypes;
    Dictionary<string, List<string>> propertyValueDic;

    public ScriptGenerator(string tableName, string[] names, string[] types, Dictionary<string, List<string>> valueDic)
    {
        className = tableName;
        propertyNames = names;
        propertyTypes = types;
        propertyValueDic = valueDic;
    }

    //创建代码
    public string CreateCode()
    {
        //生成类
        StringBuilder classSource = new StringBuilder();
        classSource.Append("/*Auto create, Don't Edit it*/\n");
        classSource.Append("\n");
        classSource.Append("using System;\n");
        classSource.Append("using System.Reflection;\n");
        classSource.Append("using System.Collections.Generic;\n");
        classSource.Append("\n");
        classSource.Append("[Serializable]\n");
        classSource.Append("public class " + className + " : TableBase\n");
        classSource.Append("{\n");
        //声明变量
        for (int i = 0; i < propertyNames.Length; i++)
        {
            classSource.Append(PropertyString(propertyNames[i], propertyTypes[i]));
        }
        //构造函数内赋值
        classSource.Append("\n");
        classSource.Append("\tpublic " + className + "()\n");
        classSource.Append("\t{\n");
        for (int i = 0; i < propertyNames.Length; i++)
        {
            classSource.Append(Assignment(propertyNames[i], propertyTypes[i], propertyValueDic[propertyNames[i]]));
        }
        classSource.Append("\t}\n");
        //Get方法
        classSource.Append("\n");
        classSource.Append("\tint targetIndex;\n");
        classSource.Append("\n");
        for (int i = 0; i < propertyNames.Length; i++)
        {
            if (propertyNames[i] == "id")
                continue;
            classSource.Append("\tpublic " + propertyTypes[i] + " Get" + propertyNames[i].ToUpper() + "(string targetId)\n");
            classSource.Append("\t{\n");
            classSource.Append("\t\ttargetIndex = id.IndexOf(targetId);\n");
            classSource.Append("\t\tif (targetIndex < 0)\n");
            classSource.Append("\t\t\treturn default;\n");
            classSource.Append("\t\treturn " + propertyNames[i] + "[targetIndex];\n");
            classSource.Append("\t}\n");
        }
        //
        classSource.Append("}\n");

        return classSource.ToString();
    }

    private string PropertyString(string name, string type)
    {
        if (string.IsNullOrEmpty(name))
            return null;

        if (type == "int")
            type = "List<int>";
        else if (type == "float")
            type = "List<float>";
        else
            type = "List<string>";

        string propertyStr = "\tpublic " + type + " " + name + ";\n";
        return propertyStr;
    }

    private string Assignment(string name, string type, List<string> valueList)
    {
        StringBuilder source = new StringBuilder();

        source.Append("\t\t" + name + " = new List<" + type + ">()\n");
        source.Append("\t\t{\n");
        source.Append("\t\t\t");

        Func<string, string> CheckPropertyAct;
        if (type == "int")
            CheckPropertyAct = CheckPropertyInt;
        else if (type == "float")
            CheckPropertyAct = CheckPropertyFloat;
        else
            CheckPropertyAct = CheckPropertyString;

        for (int i = 0; i < valueList.Count; i++)
        {
            source.Append(CheckPropertyAct(valueList[i]) + ", ");
        }

        source.Append("\n");
        source.Append("\t\t};\n");

        return source.ToString();
    }

    private string CheckPropertyInt(string value)
    {
        return Convert.ToInt32(value).ToString();
    }
    private string CheckPropertyFloat(string value)
    {
        return Convert.ToSingle(value) + "f";
    }
    private string CheckPropertyString(string value)
    {
        return "\"" + value + "\"";
    }

}

 6. 把脚本文本保存为".cs"文件,在游戏里就可以直接使用咯~

-------------------- 分割线 --------------------

理论改良版:

1. 还是将Excel转为对应的C#类,不过只创建数据List(或者Dictionary),不赋值,即不保存数据,只记录数据类型

2. 将Excel数据用txt、json或byte二进制保存到本地(json的话可以对应类的格式)

3. 读取数据时初始化对应的类,获取Excel数据进行赋值

-------------------- 分割线 --------------------

改良进阶版:

【Unity】解析Excel数据,自动创建对应的C#类,创建ScriptableObject的Asset文件并赋值

https://blog.csdn.net/qq_39108767/article/details/105607347

发布了104 篇原创文章 · 获赞 74 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/qq_39108767/article/details/103036286