unity中 Excel序列化转换为Asset遇到的一些坑

转载自:烟雨迷离半世殇 Unity实战篇:读取Excel数据并转换成Asset
本文链接:https://blog.csdn.net/qq_15020543/article/details/83098127

在开发游戏过程中我们不可避免的会遇到要给游戏数据配表的问题,(毕竟一个一个string写太累了啊喂!),而且配表还有利于数据的观察和策略性修改,也有利于游戏的维护与更新。这篇博客讲的是读取Excel表格数据,并且转化为Asset文件。
它具有以下优点

我们可以不用将Excel文件放到工程里,只需要生成一次Asset文件即可删除
解决了项目打包成EXE文件无法读取Excel表格的问题
解决了项目打包成安卓平台无法读取Excel表格的问题
不需要考虑跨平台问题
以下内容部分参考自这位大神博客,我在其基础上加以细节说明。(大神们总是这样,不够体谅我这种菜鸡的感受。)

先放一下表结构图和生成的Asset图

在这里插入图片描述
在这里插入图片描述

然后开始实现

1.下载读取Excel文件必要的dll文件。
https://download.csdn.net/download/qq_15020543/10725145
把下载的三个dll文件放到Unity的Plugins文件夹下面,如果没有就创建。
在这里插入图片描述

2.创建Excel文件,名称随意。我们这里取名为Demo,注意后缀一定是xlsx,放在Excel文件夹下面
在这里插入图片描述
在这里插入图片描述

3.创建名为Item和ItemManager的代码(随意),但是注意,你的文件名要和类名相对应,并且下面的ExcelReader里的Item和ItemManager要改为你的类名。不然最后生成Asset的Script会为MISS
在这里插入图片描述

using UnityEngine;
 
namespace Data
{
    //这里根据自己的表结构来
 
    [System.Serializable]
    public class Item
    {
        public string itemId;
        public string itemLink;
        public string itemDamage;
    }
 
}

using UnityEngine;
using UnityEditor;
 
namespace Data
{
    [System.Serializable]
    public class ItemManager : ScriptableObject
    {
        public Item[] dataArray;
    }
}
 

创建代码名为ExcelReader(随意),读取Excel文件,并做编辑器拓展,转化成Asset文件。注意一定要放在Editor文件夹下面!!!

在这里插入图片描述

using System.Collections;
using System.Collections.Generic;
using System.Data;
using Excel;
using System.IO;
using UnityEngine;
using UnityEditor;
using Data;
 
 
public class ExcelReader  {
 
 
 
    public class ExcelConfig
    {
        /// <summary>
        /// 存放excel表文件夹的的路径,本例xecel表放在了"Assets/Excels/"当中
        /// </summary>
        public static readonly string excelsFolderPath = Application.dataPath + "/Excels/";
 
        /// <summary>
        /// 存放Excel转化CS文件的文件夹路径
        /// </summary>
        public static readonly string assetPath = "Assets/Resources/";
    }
 
    public class ExcelTool
    {
 
        /// <summary>
        /// 读取表数据,生成对应的数组
        /// </summary>
        /// <param name="filePath">excel文件全路径</param>
        /// <returns>Item数组</returns>
        public static Item[] CreateItemArrayWithExcel(string filePath)
        {
            //获得表数据
            int columnNum = 0, rowNum = 0;
            DataRowCollection collect = ReadExcel(filePath, ref columnNum, ref rowNum);
 
            //根据excel的定义,第二行开始才是数据
            Item[] array = new Item[rowNum - 1];
            for (int i = 1; i < rowNum; i++)
            {
                Item item = new Item();
                //解析每列的数据
                item.itemId = collect[i][0].ToString();
                item.itemLink = collect[i][1].ToString();
                item.itemDamage = collect[i][2].ToString();
                array[i - 1] = item;
            }
            return array;
        }
 
        /// <summary>
        /// 读取excel文件内容
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <param name="columnNum">行数</param>
        /// <param name="rowNum">列数</param>
        /// <returns></returns>
        static DataRowCollection ReadExcel(string filePath, ref int columnNum, ref int rowNum)
        {
            FileStream stream = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
            IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream);
 
            DataSet result = excelReader.AsDataSet();
            //Tables[0] 下标0表示excel文件中第一张表的数据
            columnNum = result.Tables[0].Columns.Count;
            rowNum = result.Tables[0].Rows.Count;
            return result.Tables[0].Rows;
        }
    }
 
    public class ExcelBuild : Editor
    {
 
        [MenuItem("CustomEditor/CreateItemAsset")]
        public static void CreateItemAsset()
        {
            ItemManager manager = ScriptableObject.CreateInstance<ItemManager>();
            //赋值
            manager.dataArray = ExcelTool.CreateItemArrayWithExcel(ExcelConfig.excelsFolderPath + "Demo.xlsx");
 
            //确保文件夹存在
            if (!Directory.Exists(ExcelConfig.assetPath))
            {
                Directory.CreateDirectory(ExcelConfig.assetPath);
            }
 
            //asset文件的路径 要以"Assets/..."开始,否则CreateAsset会报错
            string assetPath = string.Format("{0}{1}.asset", ExcelConfig.assetPath, "Item");
            //生成一个Asset文件
            AssetDatabase.CreateAsset(manager, assetPath);
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();
        }
    }
 
 
}

4.在Unity菜单项,选择我们自定义的菜单CustomEditor->CreateItemAsset。即可生成我们需要的Asset。
在这里插入图片描述
5.读取使用Asset
创建另一个脚本,挂载到任意游戏物体,运行游戏

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Data;
 
public class DataDAO : MonoBehaviour {
    private void Start()
    {
        ItemManager manager = Resources.Load<ItemManager>("Item");
        foreach (Item i in manager.dataArray)
        {
            Debug.Log(i.itemId + "---" + i.itemLink + "---" + i.itemDamage);
        }
 
 
    }
 }

在这里插入图片描述

以上为转载内容
以下为个人总结

在开发中时常会遇到很多数据要处理,如果每个数据都在代码中声明字段,显得很繁琐,而且修改起来也不方.故可将数据用excel列好,然后一键生成unity程序数据,减轻数据的处理量,以及后续需要修改的重复工作在excel中修改后,重新导入一遍就好了.

于是我按照上述内容开始操作遇到了无数个坑.①代码看懂了,准备下载dll插件,需要积分或C币,于是我去找积分,发布博客,做评论,但无一例外都不能增加积分.
②没办法获得积分,好吧,那我买一点好了,打开购买链接,我去,119元,98元,298元,什么鬼,这么贵,我只要3个积分,你买不买>? 可想而知 不卖,难受了,就想要一点点资源就得买100积分,这也太坑了吧,滚球不买,我去百度找.
③百度找dll插件,点一个链接跳到CSDN,点一个就跳一个,大家都好像万物归一,终于花了不少力气,找到一部分dll, sysytem.data.dll 找到了,然后找别人要了另外的, 凑齐了所需要的三份, 以为能召唤神龙,结果是个坑.
④ 按照上述的代码写好了内容,结果报错,生成为null, 百思不得其解,写的没问题啊,为啥一直报空,去查了一下,原来是API调用转换出出来,是 dlll文件有问题
⑤dll有问题,只能换了,换了之后,ExcelDataReader.DataSet.dll /ExcelDataReader.dll /ICSharpCode.SharpZipLib.dll,代码照常写,引用空间报错,这是因为,dll
文件中封装的方法核心是一样的,不同时的是命名空间,把对应的命名空间 引用下就好了.

当用这种方式 读取数据时发现和自己之前的方式有很大不同,于是我就混乱了.

我原先的方式是,1,建立excel表,2转SCV,3,转JSON, 4,用 litjson转成CS,最终将 字符流存到playerprefabs中.
这样做的好处①可以将数据类型分开,并合并,静态数据 动态数据分别倒入,合并,运行
②方便从服务器发数据过来,也方便将数据传到服务器.
③用json的格式,层级关系清晰,和CS有很强的映射关系,方便转换

不好的地方 ① 数据读取相对繁琐②数据存储需要不停的转换③数据不可见④开发过程中不方便调试数据,想要调试数据,得把数据从本地调取出来,改过后还得再存麻烦

于是我就想换成上述的方法,直接excel转换数据,对象化脚本
这样一来我就需要重新搭建excel表,搭建对应CS脚本, 修改原先的数据获取方式和数据存储方式,而且我的数据不方便都采用excel的方式展示,应为它不具备良好的层级关系.
于是我就两者相结合, 数据采用json格式,导到CS,再对象化脚本,可这样 json的编写工作量也很大,还是要用excel,这样一来,还不如不转,直接 excel 转CS为好,只是这样做的话,没一次转换都需要些相应法的方法,去对照属性或数组,编写十分麻烦.为了省这部分力,我想数据不要你转换,你负责对象化就好了. 数据直接在CS脚本中填写,对象化,然后想修改数据,在inspect面板修改即可.
这样的话是方便了很多, excel 不用列了 , json也不用了, 直接 CS就好, 可是这样子不方便 策划去阅读和修改,他们最擅长 excel,如果是一个人开发单机游戏,这种方式简单快捷,最后把这些数字放在游戏面板里面会更好,更方便调试.

我现在是和老大合作,数值肯定会多次修改,老大当然用excel修改,那我怎么办,我的数值要读取excel 表吗, 如果不读, 老大每次修改excel表后,得查看修改了那些地方,然后在CS中找到对象的内容进行修改,很可能遗漏.老大新增一个项目,我就在CS中添加一个属性,老大修改一个数据,我就在CS中修改对应数据.这样做起来,我开始的开发难度是降低了,可是后续的调试会很艰难.可不可以让老大修改asset资源数据,可是可以,但是显得麻烦,首先我的资源肯定不能单独存在,我的把哦工程都弄好了才行.其次面板不方便查阅观看.
那如果我的数据通过 json转换过来,那 直接修改json表是可以的,但也是这个问题,json表不便于策划阅读,同样 XML 也是这个问题,最便于策划或老大进行修改的方式就是使用Excel表呈现数据的形式.
使用我一直以来用的方式, Excel表是可以修改,可是要把修改后的Excel转换,再转换才能读取.很麻烦.
使用的是LitJson, 程序能读的是 Json数据.不方便.
所以对于现在的工程而言,要和老大合作,我需要将Excel表修改后,能一键处理数据.这样的话,还是要用读取Excel数据转换的方式, 将需要的数据都列成表格,并建立对应的CS类,还有逐字段的书写,读取方法.

在对象化脚本的过程中遇到的坑
①对象化的脚本,有部分内容格式无法识别,我开始以为是,类中类不一一赋值是无法识别的,尝试了用List< > 用数组 ,都不行, 问题在于, 类中类的声明,被声明的类不能是继承: ScriptableObject的,这回导致无法识别.
把: ScriptableObject去掉就好了, 和 list 或是 数组 形式 没关系.
②脚本对象化后,和直接在Insoector里面修改, 而且修改后的数据是持久化的, 所以用这种方式调属性是挺方便的
③在代码中 调用对象话的脚本, 也可对数据进行修改,而且能够持久化, 停止运行,后 数据都是 被修改过的, 但是,退出unity后再进入,数据又恢复到原先的了, 代码中修改的内容没有生效,这需要引起注意.不能用 运行状态下修改数值来保存数据, 脚本对象化应是适用于静态数据库的 ,就像是一张列好的表格,从动态数据里面获取关键key,来返回对应值.
④如果要保存本地数据,还是用 playerprefabs来存储.

总结1, 找资源最好是找直接关联的资源,自己另外去找耗费时间不说,还可能不对.那找直接关联的资源需要付出成本较高怎么办,那就找他人索要,如果索要不到,就想办法获得兑换资源的成本,或者狠一狠心直接花钱.
2,有问题舔着脸皮也要问,我的思路是问出来的,如果不问,闷着一个人想,容易混乱,当你去问了,思路就被理了一遍,就像当时开发 樱落的数据一样,如果不问的话不讨论的话,数据库很难完善,虽然花了不少时间,但是值得的.
3.下次如果还是个人开发的形式,首先应明确,这时什么类型的游戏,包含哪些对象,需要用到哪些数据,游戏流程是怎样的,数据交互是怎样的,对象分类是怎样的, 框架怎么搭建,UML图学起来.
4.外部数据的读取转换无非四个步骤
a,外部表
b,取表数据
c,转换表数据
d,存储转换后的数据
5.unity编辑器确实有很多方便的功能,只是自己还不太熟练.当然这不是最重要的,最重要的是开发思路,工具只是辅助作用,核心还是思路和 代码.
6.Excel转换最适合用于静态表读取,而且是单表式,因为这种方式转换需要在C#中一个一个属性书写赋值,过程很繁琐,要是表很多,那就需要创建很多个映射方法,效率不高,但是它的优点就是直观性强,和Excel很方便转换.
7.litjson的方便之处在于 把 对应字段名建好了,会自动映射,转换的过程会节省很多操作,如果表格很多的话用这种方式是很好的,不好的点就是想要调节数值或增加属性,需要多次操作,Excel中要操作一次,然后要多次转换成json
,所以比较适用于哪些表哥很多,但不常改动的数据类型.

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值