添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

在制作游戏中需要管理各种各样的项目资源,其中游戏中的剧情文字也是一种需要管理的资源。自己刚开始接触游戏开发的时候,第一次看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!"