在制作游戏中需要管理各种各样的项目资源,其中游戏中的剧情文字也是一种需要管理的资源。自己刚开始接触游戏开发的时候,第一次看MStudio里面的对话系统教学,只讲了怎么写脚本同步UI的设置,并没有讲有什么方式去管理这些对话数据,视频里拿的是txt来演示存储对话数据,导致我制作的第一个游戏也是那txt来写的,一个对话就是一个txt,管理起来肯定是不方便的。
如何存储文字数据呢?当然我们可以专门写一个系统去对接数据库,而对话系统的网络上也有很多插件,其中就有很多节点化,可分支的对话系统,但是系统太过庞大,如果只是对一个小项目显得的有点用牛刀杀鸡的感觉了。
这里我们可以想到拿ScriptableObject管理数据,ScriptableObject是一种很方便存储不可变数据的数据结构。但是ScriptableObject是面向Unity的,有时候只是比赛的小组队的情况下,并不方便策划进编辑器里数据(策划不会用unity?/版本同步?)
这时候就可以使用Excel表格了,让策划在表格中编写完数据后我们就可以一键导入成ScriptableObject,有需要再编辑是也可以导出到excel中发回给策划,或者策划直接进编辑器修改scrpitableObject也是可以的。这可以比较轻松的实现数据管理,excel也很方便策划的理解和使用,在比赛的一些中小项目中还是很实用的。
效果展示:
功能实现:
本文章使用了C# ExcelNPOI,这是一个开源的C#读写Excel、WORD等微软OLE2组件文档的项目,可以自行去网上下载,或者在本文结尾也会放出github网址,直接在里面下载即可。
(这东西其实就是两个DLL,放入Plugins文件夹中里即可生效)
这里拿我的项目中的对话系统举例,首先假设我们已经在unity中实现了一个对话系统,并使用了ScriptableObject进行数据管理,其中对话数据的代码如下:
[CreateAssetMenu(fileName = "DialogueSo")]
public class DialogueData : ScriptableObject
public List<Sentence> data = new List<Sentence>();
[System.Serializable]
public class Sentence
public string character;
[TextArea]
public string content;
这里的一个对话数据由一个句子的List构成,句子有角色名和对话内容组成,组成都为string,我们excel里也可以定义约束一下格式,在第一列中为该ScriptableObject的name,意味着一个新的对话数据开始,第二列为该角色名,第三列为具体的对话内容,其中第二第三列是不能为空的,第一列为空待变该对话仍在上一个定义的对话中,有内容代表新起了一段新对话。
Excel里如下:
对于的ScriptableObject如下:
这里只是简单展示,当然也可以使用其他的规范,或者在第四第五行再添加一些额外的数据也是可以的(比如表示对话关联的其他事件,对话分支什么的)。确定了规范,就可以实现与Excel的同步了。
在开始将同步前,先简单介绍一下一些NPOI的基本用法。
首先需要将NPOI两个DLL放入Unity的Plugins文件夹,这个文件夹会被当做插件载入项目,就可以直接使用对应的命名空间了。
在使用前,需要引入对应的两个命名空间,一般为NPOI.HSSF.UserModel和NPOI.SS.UserModel。需要注意NPOI的API只能在编辑器中使用,游戏最后打包是无法将逻辑带出去的(打包时会提示编译错误),所以需要加上#if UNITY_EDITOR的宏定义,或者将使用Excel的代码放入Unity的Editor文件夹。
#if UNITY_EDITOR
using NPOI.SS.UserModel;
using NPOI.HSSF.UserModel;
#endif
从逻辑上讲,放入Editor文件夹更方便,因为数据同步本身就是在Unity编辑器中完成的。
接着就是API的使用了,这里介绍一下本文章中使用的。更多详细的API可自行上网查找。
//创建一个excel文件实例,fs这里指为文件路径
var wk = new HSSFWorkbook(fs);
//将表格内容写入回路径
wk.Write(fs);
//获取excel中第index个sheet
var sheet = wk.GetSheetAt(0);
//在excel中创建一个新的sheet
var sheet = wk.CreateSheet();
//sheet的行数
sheet.LastRowNum
//读取第i行的内容,下标从0开始
var row = sheet.GetRow(i)
//读取该行中的第i个元素(可以用toString转换为字符串内容),下标从0开始
row.GetCell(i)
//创建行和行中元素
var row = sheet.CreateRow(i);
var cell = row.CreateCell(0);
cell.SetCellValue(string);
然后我们就可以开始编写Excel脚本逻辑了。首先我们需要两个string,去表示ScriptableObject的保存文件夹路径和excel的文件路径。注意这里一个是文件夹的路径,一个是文件路径。
[FolderPath]
public string saveScriptableObjectPath; //对话数据文件夹存储路径
[FilePath]
public string excelPath; //excel表格文件路径
Excel 同步分为写出和加载两种操作,一种是将所有的ScriptableObject写到Excel,另一种是Excel的数据载入进Unity,写出机制逻辑比较简单,就是先读取所有对话文件,然后对着行列一个个写入即可,代码如下。
public void WriteExcel()
//空值判断
if (string.IsNullOrEmpty(excelPath))
return;
if (!excelPath.Contains(".xlsx"))
Debug.Log("路径不是excel文件");
return;
//打开文件流
FileStream fs = File.Exists(excelPath) ? File.Open(excelPath, FileMode.Open) : File.Create(excelPath);
//新建excel和sheet
var wk = new HSSFWorkbook();
var sheet = wk.CreateSheet();
int i = 1; //省略掉第0行
//遍历所有对话文件
foreach (var dialogue in dialogueData.datas)
var row = sheet.CreateRow(i);
var cell = row.CreateCell(0);
//讲ScriptableObject的name写在第一列
cell.SetCellValue(dialogue.ID.name);
//遍历句子内容
for (int j = 0; j < dialogue.ID.data.Count; j++)
if (j != 0)
row = sheet.CreateRow(i);
//将名字写在第二列
row.CreateCell(1).SetCellValue(dialogue.ID.data[j].character.ToString());
//将对话内容写在第三列
row.CreateCell(2).SetCellValue(dialogue.ID.data[j].content);
wk.Write(fs);
fs.Close();
fs.Dispose();
Debug.Log("写入成功");
加载机制同理,这里有一点不用的是,加载需要动Unity中的ScriptableObject的数据,所以只有新的ID数据(name)时需要创建ScriptableObject的实例并保存,如果已经存在的ID就不用删除再创建新的文件了,否者会导致资源的meta文件被刷新,原本游戏逻辑中已经引用了这个ScriptableObject数据的地方就会丢失(当然你要使用string做数据索引这点就不用担心了)
最后不要忘记使用AssetDatabase.SaveAssets();AssetDatabase.Refresh();刷新一下unity资源列表。
public void ReadExcel()
//空值判断
if (string.IsNullOrEmpty(excelPath))
return;
if (!excelPath.Contains(".xlsx"))
Debug.Log("路径不是excel文件");
return;
//打开文件流
FileStream fs = File.Open(excelPath, FileMode.Open);
//打开excel和sheet
var wk = new HSSFWorkbook(fs);
var sheet = wk.GetSheetAt(0);
//这是ScriptableObject的实例
DialogueData so = null;
//开始遍历sheet每一行的数据,注意这里i=1跳过了第一行
for (int i = 1; i < sheet.LastRowNum; i++)
Debug.Log("读取EXCEL 行数:" + i);
var row = sheet.GetRow(i);
//如果当前行第一列元素不为空,证明需要保存当前so,然后创建新的so
if (row.GetCell(0) != null && !string.IsNullOrEmpty(row.GetCell(0).ToString()))
//只有第一次so为null
if (so) //保存路径
//是否原来已经创建了ScriptableObject资源
if (File.Exists(saveScriptableObjectPath + "/" + so.name + ".asset"))
dialogueData.datas.Find(x => { return x.ID.name == so.name; }).ID.data = so.data;
AssetDatabase.CreateAsset(so, saveScriptableObjectPath + "/" + so.name + ".asset");
//创建实例,开始新的对话数据记录
so = ScriptableObject.CreateInstance<DialogueData>();
so.name = row.GetCell(0).ToString();
//加载excel第二第三列的数据
if (so != null)
var se = new DialogueData.Sentence();
se.character = row.GetCell(1).ToString();
se.content = row.GetCell(2).ToString();
so.data.Add(se);
//退出循环时记得还有一个so的数据
if (so)
if (File.Exists(saveScriptableObjectPath + "/" + so.name + ".asset"))
File.Delete(saveScriptableObjectPath + "/" + so.name + ".meta");
File.Delete(saveScriptableObjectPath + "/" + so.name + ".asset");
AssetDatabase.CreateAsset(so, saveScriptableObjectPath + "/" + so.name + ".asset");
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
本文完结!最后本文章出现的代码和NPOI需要用到的两个DLL已放在github上,欢迎下载和讨论
GitHub - sugarzo/Unity_ExcelFrame: 使用NPOI实现在unity中的ScriptableObject和Excel表格数据同步
在制作游戏中需要管理各种各样的项目资源,其中游戏中的剧情文字也是一种需要管理的资源。自己刚开始接触游戏开发的时候,第一次看MStudio里面的对话系统教学,只讲了怎么写脚本同步UI的设置,并没有讲有什么方式去管理这些对话数据,视频里拿的是txt来演示存储对话数据,导致我制作的第一个游戏也是那txt来写的,一个对话就是一个txt,管理起来肯定是不方便的。如何存储文字数据呢?这时候就可以使用Excel表格了,让策划在表格中编写完数据后我们就可以一键导入成ScriptableObject,有需要再编辑是也可以导出
Unity 使用NPOI,模板替换Excel中的关键字(针对.xlsx)
需求:项目中要用到生成Excel来打印文件,只需要替换其中的值,保留原模板,生成新的Excel
第一步:在unity中导入一下的dll
新建一个Plugin的文件夹,把dll全部放进去
以上选中的这些文件在unity的安装目录下Unity\Editor\Data\Mono\lib\mono\unity可以找到
还有一个System.Data.dll,我放进去它会显示重复引用,所以我就没放上去,你要是想试试也可以在安装路径下找到,然
4. 自动创建对应的C#类,继承ScriptableObject,声明变量,保存.cs文件
5. 自动创建ScriptableObject的Asset文件,并赋值,保存Asset文件
6. 在游戏直接使用ScriptableObject类
1. 每个Excel对应一个类,使用灵活,对Exce.........
1、
Unity ScriptObject生成的asset文件,关闭
unity时
数据丢失:需要先setdirty
EditorUtility.SetDirty(obj);
AssetDatabase.SaveAssets();
可以使用Unity提供的JsonUtility类来将ScriptableObject序列化为JSON格式的字符串,然后将其存储到本地文件或数据库中。反序列化时,可以使用JsonUtility类将JSON字符串转换为ScriptableObject对象。以下是示例代码:
// 定义一个ScriptableObject类
public class MyScriptableObject : ScriptableObject
public int myInt;
public string myString;
// 将ScriptableObject序列化为JSON字符串并存储到本地文件
MyScriptableObject myObject = ScriptableObject.CreateInstance<MyScriptableObject>();
myObject.myInt = 42;
myObject.myString = "Hello, world!";
string json = JsonUtility.ToJson(myObject);
File.WriteAllText(Application.persistentDataPath + "/myObject.json", json);
// 从本地文件读取JSON字符串并反序列化为ScriptableObject对象
string json = File.ReadAllText(Application.persistentDataPath + "/myObject.json");
MyScriptableObject myObject = JsonUtility.FromJson<MyScriptableObject>(json);
Debug.Log(myObject.myInt); // 输出 42
Debug.Log(myObject.myString); // 输出 "Hello, world!"