转载自:烟雨迷离半世殇 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
,所以比较适用于哪些表哥很多,但不常改动的数据类型.