【Unity-Tool】Excel数据转游戏内可用数据(字典/已验证可用)

0 前言

5月份参加了机核的Booom,和机组组到的神仙队友们一起完成了《恶魔真探》这款游戏:游戏地址,感兴趣的朋友可以在首页简介链接里下载,如果能够给我们点个关注投个票就太感谢了!

在这里插入图片描述

其中为了方便策划进行调整修改,很多数据的存储我们都采用了Excel表的方式,如恶魔属性部分:

在这里插入图片描述

那么这样就自然涉及到对于Excel表格数据的管理。

我们很容易发现一个Excel表的属性加起来其实可以抽象为一个类,比如上述的表格,他描述的是恶魔的属性数值,对于单只恶魔,我们可以把它抽象为这样一个类:

class Devil{
	public int ID;
	public string Name;
	public int[] Skills;
}

而一个Excel表的数据加起来显然是类数据的集合,它可以抽象为key为ID,value为类实例的字典,如上述的Excel表可以描述为:

Dictionary<int,Devil> DevilnDataDict;

这样显然是比较理想的管理数据的方式,在游戏里,我们也可以比较方便地通过序号去读取某只特定恶魔的数据。

那么我们如何做到呢?

我采用的方式是通过工具读取Excel表并生成一个特定的脚本,这个脚本包含了类的定义以及一个读取Json文件并存储为字典的方法。(是的,这个工具还将Excel表格转换为Json文件以便读取)

1 工具相关代码

1.1 ExcelUtility

表格内不同字段类型的数据应该如何填写,可以看这个类有一段switch-cash的代码里对于数据是如何分割的,然后根据自己需要进行修改。

/// <summary>
/// Excel工具类
/// </summary>
public class ExcelUtility
{
    /// <summary>
    /// 表格数据集合
    /// </summary>
    private DataSet mResultSet;
    private string TempexcelFile;
    List<string> dataName = new List<string>();
    List<string> dataType = new List<string>();

    List<string> enumName = new List<string>();
    Dictionary<string, List<string>> enumType = new Dictionary<string, List<string>>();

    /// <summary>
    /// 构造函数
    /// </summary>
    public ExcelUtility(string excelFile) {
        TempexcelFile = excelFile;
        using (FileStream mStream = File.Open(excelFile, FileMode.Open, FileAccess.Read)) {
            IExcelDataReader mExcelReader = ExcelReaderFactory.CreateOpenXmlReader(mStream);
            mResultSet = mExcelReader.AsDataSet();
        }
    }

    /// <summary>
    /// 转换为Json
    /// </summary>
    public void ConvertToJsonForGameData(string JsonPath, Encoding encoding, string CSharpPath) {
        //判断Excel文件中是否存在数据表
        if (mResultSet == null || mResultSet.Tables == null || mResultSet.Tables.Count <= 0) {
            Debug.Log("不存在数据表,直接退出");
            return;
        }
        //读取Excel文件中的数据表
        for (int x = 0; x < mResultSet.Tables.Count; x++) {
            //默认读取第一个数据表
            var mSheet = mResultSet.Tables[x];
            var outname = mSheet.TableName;
            if (outname.IndexOf('#') >= 0 && outname.LastIndexOf('#') != outname.IndexOf('#')) {
                Debug.Log("无法导出名 " + outname + "  请确定#书写正确!");
                continue;
            }
            //判断数据表内是否存在数据
            if (mSheet.Rows.Count < 1) continue;
            //读取数据表行数和列数
            int rowCount = mSheet.Rows.Count;
            int colCount = mSheet.Columns.Count;
            //准备一个列表存储整个表的数据
            List<Dictionary<string, object>> table = new List<Dictionary<string, object>>();
            //List<object> tempvaluestrList = null;
            string tempfield = null;
            string temptypestring = null;
            //读取数据
            for (int i = 3; i < rowCount; i++) {
                //标记当前行是否为空行
                var isEmptyRow = true;
                //准备一个字典存储每一行的数据
                Dictionary<string, object> row = new Dictionary<string, object>();
                for (int j = 1; j < colCount; j++) {
                    //读取第1行数据作为表头字段
                    string field = mSheet.Rows[1][j].ToString();
                    field = field.Trim();
                    if (field != "") {
                        tempfield = field;
                        if (!dataName.Contains(field)) {
                            dataName.Add(field);
                        }
                    }
                    else if (tempfield != "" && field == "") {
                        field = tempfield;
                    }
                    string typestring = mSheet.Rows[2][j].ToString();
                    typestring = typestring.Trim();
                    if (typestring != "") {
                        temptypestring = typestring;
                        dataType.Add(typestring);
                    }
                    else if (typestring == "" && temptypestring != "") {
                        typestring = temptypestring;
                    }
                    string valuestr = mSheet.Rows[i][j].ToString();
                    valuestr = valuestr.Trim();
                    if (!string.IsNullOrEmpty(valuestr)) isEmptyRow = false;
                    //Key-Value对应 按类型存放
                    switch (typestring) {
                        case "int":
                            if (valuestr != "") {
                                row[field] = int.Parse(valuestr);
                            }
                            else {
                                row[field] = 0;
                            }
                            break;
                        case "int[]":
                            if (!string.IsNullOrEmpty(valuestr)) {
                                var strs = valuestr.Split(",");
                                var list = new List<int>();
                                for (var k = 0; k < strs.Length; k++) {
                                    list.Add(int.Parse(strs[k]));
                                }
                                row[field] = list.ToArray();
                            }
                            else {
                                row[field] = new int();
                            }
                            break;
                        case "float":
                            if (valuestr != "") {
                                row[field] = float.Parse(valuestr);
                            }
                            else {
                                row[field] = 0;
                            }
                            break;
                        case "float[]":
                            if (!string.IsNullOrEmpty(valuestr)) {
                                var strs = valuestr.Split(",");
                                var list = new List<float>();
                                for (var k = 0; k < strs.Length; k++) {
                                    list.Add(float.Parse(strs[k]));
                                }
                                row[field] = list.ToArray();
                            }
                            else {
                                row[field] = new float();
                            }
                            break;
                        case "double":
                            if (valuestr != "") {
                                row[field] = double.Parse(valuestr);
                            }
                            else {
                                row[field] = 0;
                            }
                            break;
                        case "double[]":
                            if (!string.IsNullOrEmpty(valuestr)) {
                                var strs = valuestr.Split(",");
                                var list = new List<double>();
                                for (var k = 0; k < strs.Length; k++) {
                                    list.Add(double.Parse(strs[k]));
                                }
                                row[field] = list.ToArray();
                            }
                            else {
                                row[field] = new double();
                            }
                            break;
                        case "bool":
                            if (valuestr == "0" || string.Equals(valuestr, "false", StringComparison.OrdinalIgnoreCase) || valuestr == "") {
                                row[field] = false;
                            }
                            else {
                                row[field] = true;
                            }
                            break;
                        case "List<int>":
                            var tempintList = new List<int>();
                            if (!string.IsNullOrEmpty(valuestr)) {
                                var strs1 = valuestr.Substring(1, valuestr.Length - 2).Split(',');
                                for (var k = 0; k < strs1.Length; k++) {
                                    tempintList.Add(int.Parse(strs1[k]));
                                }
                            }
                            row[field] = tempintList;
                            break;
                        case "List<int[]>":
                            var tempintintList = new List<int[]>();
                            if (!string.IsNullOrEmpty(valuestr)) {
                                var strs2 = valuestr.Split('、');
                                for (var k = 0; k < strs2.Length; k++) {
                                    var tempStrs = strs2[k].Substring(1, strs2[k].Length - 2).Split(',');
                                    var tempList = new List<int>();
                                    for (var p = 0; p < tempStrs.Length; p++) {
                                        tempList.Add(int.Parse(tempStrs[p]));
                                    }
                                    tempintintList.Add(tempList.ToArray());
                                }
                            }
                            row[field] = tempintintList;
                            break;
                        case "Dictionary<string,float>":
                            var tempstringfloatDict = new Dictionary<string, float>();
                            if (!string.IsNullOrEmpty(valuestr)) {
                                var dictStrs1 = valuestr.Split('、');
                                for (var k = 0; k < dictStrs1.Length; k++) {
                                    var str = dictStrs1[k];
                                    var strs = str.Substring(1, str.Length - 2).Split(',');
                                    tempstringfloatDict.Add(strs[0], float.Parse(strs[1]));
                                }
                            }
                            row[field] = tempstringfloatDict;
                            break;
                        case "enum":
                            if (!enumName.Contains(field)) {
                                enumName.Add(field);
                            }
                            if (!string.IsNullOrEmpty(valuestr)) {
                                if (!enumType.ContainsKey(field)) {
                                    enumType[field] = new List<string>();
                                }
                                if (!enumType[field].Contains(valuestr)) {
                                    enumType[field].Add(valuestr);
                                }
                            }
                            row[field] = valuestr;
                            break;
                        default:
                            row[field] = valuestr.Trim();
                            break;
                    }
                }
                //添加到表数据中
                if (!isEmptyRow) table.Add(row);
            }
            //生成Json字符串
            string json = JsonConvert.SerializeObject(table);
            //修复中文乱码
            Regex reg = new Regex(@"(?i)\\[uU]([0-9a-f]{4})");
            json = reg.Replace(json, delegate (Match m) { return ((char)Convert.ToInt32(m.Groups[1].Value, 16)).ToString(); });
            string JsonFilePath = JsonPath + Path.GetFileNameWithoutExtension(TempexcelFile) + ".json";
            //删除原来的同名文件
            if (File.Exists(JsonFilePath)) {
                File.Delete(JsonFilePath);
            }
            //写入文件
            using (FileStream fileStream = new FileStream(JsonFilePath, FileMode.Create, FileAccess.Write)) {
                using (TextWriter textWriter = new StreamWriter(fileStream, encoding)) {
                    textWriter.Write(json);
                }
            }
            CreatCSharpForGameData(Path.GetFileNameWithoutExtension(TempexcelFile), CSharpPath);
            dataName.Clear();
            dataType.Clear();
            enumName.Clear();
            enumType.Clear();
            //刷新本地资源
            AssetDatabase.Refresh();
        }
    }

    /// <summary>
    /// 创建C#代码
    /// </summary>
    private void CreatCSharpForGameData(string name, string CSharpPath) {
        //创建一个StringBuilder来构建C#脚本
        StringBuilder scriptStr = new StringBuilder();

        //从提供的文件名中提取类名
        string className = new FileInfo(name).Name.Split('.')[0];

        //在脚本开头添加必要的using语句
        scriptStr.AppendLine("using Newtonsoft.Json;");
        scriptStr.AppendLine("using System;");
        scriptStr.AppendLine("using System.Collections.Generic;");
        scriptStr.AppendLine("using System.IO;");
        scriptStr.AppendLine("using System.Linq;");
        scriptStr.AppendLine("using UnityEngine;");

        //枚举
        if (enumName.Count > 0) {
            for (var i = 0; i < enumName.Count; i++) {
                var list = enumType[enumName[i]];
                scriptStr.AppendLine("\n[Serializable]");
                scriptStr.AppendLine("\npublic enum " + enumName[i]);
                scriptStr.AppendLine("{");
                for (var j = 0; j < list.Count; j++) {
                    scriptStr.AppendLine($"\t{list[j]},");
                }
                scriptStr.AppendLine("}");
            }
        }

        scriptStr.AppendLine("\n[Serializable]");
        scriptStr.AppendLine("\npublic class " + className);
        scriptStr.AppendLine("{");

        //遍历dataName和dataType列表以生成类的属性
        for (int i = 0; i < dataName.Count; ++i) {
            if (dataType[i] == "enum") {
                var tmpName = enumName[0];
                enumName.Remove(tmpName);
                var tmpName2 = char.ToLower(tmpName[0]) + tmpName.Substring(1);
                scriptStr.AppendLine($"\tpublic {tmpName} {tmpName2};");
            }
            else {
                scriptStr.AppendLine($"\tpublic {dataType[i].PadRight(10, ' ')} {dataName[i]};");
            }
        }

        //添加一个Json数据转Dictionary字典数据的方法
        scriptStr.AppendLine($"\tpublic static Dictionary<int, {className}> LoadJson()");
        scriptStr.AppendLine("\t{");
        string pathToLoad = "/Json/GameData/" + $"{className}.json";
        scriptStr.AppendLine($"\t\tstring jsonPath = Application.streamingAssetsPath + \"{pathToLoad}\";");
        // scriptStr.AppendLine($"\t\tjsonPath = jsonPath.Replace('/','\\\\');");
        scriptStr.AppendLine($"\t\tif (Application.platform == RuntimePlatform.WindowsPlayer || Application.platform == RuntimePlatform.WindowsEditor) {{ jsonPath = jsonPath.Replace('/','\\\\'); }}");
        scriptStr.AppendLine($"\t\tstring jsonContent = File.ReadAllText(jsonPath, System.Text.Encoding.UTF8);");
        scriptStr.AppendLine($"\t\tList<{className}> classInfo = JsonConvert.DeserializeObject<List<{className}>>(jsonContent);");
        scriptStr.AppendLine($"\t\tDictionary<int, {className}> idClassDict = classInfo.ToDictionary(classInfo => classInfo.ID, classInfo => classInfo);");
        scriptStr.AppendLine($"\t\treturn idClassDict;");
        scriptStr.AppendLine("\t}");

        scriptStr.AppendLine("}");

        string path = $"{CSharpPath}/{className}.cs";
        if (File.Exists(path)) {
            File.Delete(path);
        }
        File.WriteAllText(path, scriptStr.ToString());
    }

1.2 ExcelToGameData

Excel表格需要放置的路径以及其他文件的生成路径,可以看这个类一开始的文件路径定义,然后根据自己需要进行修改。

using System.IO;
using System.Text;
using UnityEditor;
using UnityEngine;

/// <summary>
/// Excel文件处理器
/// </summary>
public class ExcelToGameData
{
    //Excel文件路径
    public static string ExcelPath = Application.dataPath+"/Excel/GameData";
    //Json文件路径:
    //对于需要在运行时访问的二进制文件,可以将它们放在 "Assets/StreamingAssets" 文件夹中。
    //这个文件夹中的内容在构建后会被包含在应用程序中,并且可以通过 Application.streamingAssetsPath访问。
    public static string JsonPath = Application.streamingAssetsPath + "/Json/GameData/";
    //创建的Script脚本路径
    public static string CSharpPath = Application.dataPath + "/Scripts/Excel/GameData";
    
    /// <summary>
    /// Excel转GameData工具入口
    /// </summary>
    [MenuItem("Tools/Excel/ExcelToGameData")]
    public static void StartExcelToJson()
    {
        if (Directory.Exists(ExcelPath))
        {
            //拿到所有的Excel文件
            var directoryInfo = new DirectoryInfo(ExcelPath);
            var FilesAll = directoryInfo.GetFiles("*.xlsx");
            //循环转换所有Excel表格并刷新本地资源
            for (int i = 0; i < FilesAll.Length; i++)
            {
                var ExcelTempPath = FilesAll[i].ToString();
                var excelUtility = new ExcelUtility(ExcelTempPath);
                excelUtility.ConvertToJsonForGameData(JsonPath, Encoding.GetEncoding("utf-8"), CSharpPath);
                AssetDatabase.Refresh();
            }
            Debug.Log("Excel转换已完成");
        }
        else
        {
            Debug.Log("未找到Excel文件");
        }
    }
}

2 结果

现在用我们一开始的示例类验证一下工具是否可行

2.1 生成

Excel表

在这里插入图片描述
生成的C#脚本

在这里插入图片描述
生成的Json文件

在这里插入图片描述

2.2 使用

测试脚本

在这里插入图片描述
结果

在这里插入图片描述

  • 11
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值