?什么是FlatBuffer
官網:?
GitHub - google/flatbuffers: FlatBuffers: Memory Efficient Serialization LibraryFlatBuffers: Memory Efficient Serialization Library - google/flatbuffershttps://github.com/google/flatbuffers
為什么用FloatBuffer,優勢在哪?
下圖是常規使用的各種數據存儲類型的性能對比。?
- 對序列化數據的訪問不需要打包和拆包——它將序列化數據存儲在緩存中,這些數據既可以存儲在文件中,又可以通過網絡原樣傳輸,而沒有任何解析開銷;(這是最主要的原因,ProtoBuffer、JSON等均需要拆包和解包)
- 內存效率和速度——訪問數據時的唯一內存需求就是緩沖區,不需要額外的內存分配。 這里可查看詳細的基準測試;
- 擴展性、靈活性——它支持的可選字段意味著不僅能獲得很好的前向/后向兼容性(對于長生命周期的游戲來說尤其重要,因為不需要每個新版本都更新所有數據);
- 最小代碼依賴——僅僅需要自動生成的少量代碼和一個單一的頭文件依賴,很容易集成到現有系統中。再次,看基準部分細節;
- 強類型設計——盡可能使錯誤出現在編譯期,而不是等到運行期才手動檢查和修正;
- 使用簡單——生成的C++代碼提供了簡單的訪問和構造接口;而且如果需要,通過一個可選功能可以用來在運行時高效解析Schema和類JSON格式的文本;
- 跨平臺——支持C++11、Java,而不需要任何依賴庫;在最新的gcc、clang、vs2010等編譯器上工作良好;
?除了性能上的優勢,FlatBuffer還支持把數據序列化成明文Json供開發者校驗數據正確性。
游戲開發什么情況選擇使用FlatBuffer?
?大量的數據配表,想要高效加載大量數據
FlatBuffer使用流程
- 定義FlatBuffer數據格式文件schema
- 序列化數據成bytes文件
- Unity工程中加入FlatBuffer解析源碼
- 使用FlatC.exe工具生成C#數據解析代碼
- 加載bytes數據解析成數據對象。
Schema
FlatBuffer的自定義數據格式文件叫Schema。文件后綴是.fbs,和protobuffer的.proto后綴類似。
Schema支持的語法有
創建MyTestData.fbs文件,放到新建文件夾TestFlatBuffer內
//統計一下所有使用類型
//namespace的作用:1生成C#代碼有命名空間 2生成文件夾
namespace MyGame;//attribute字段暫時沒看啥作用
attribute "priority";//枚舉使用方式1
enum Color : byte { Red = 1, Green, Blue }//枚舉使用方式2
enum PhoneType : int {MOBILE = 0,HOME = 1,WORK = 2,
}table Monster {number:string (required);type:int;
}table Weapon {number:string (required);type:int;
}table Pickup {number:string (required);type:int;
}//數據對象可以是Monster、Weapon、Pickup中的任何一個
union TestUnion { Monster, Weapon, Pickup }//自定義三維數據
struct Vec3 {x:float;y:float;z:float;
}//Monster結構中展示了常規值類型數據的使用方法
//展示了在table內使用struct、數組、union、枚舉
table DataTable {pos:Vec3;//常規值類型damage:int = 500;hp:short = 100;name:string;friendly:bool = false;//數組的使用方式intArr:[int];//枚舉的使用方式,支持寫默認值color:Color = Blue;unionTarget:TestUnion;
}table MyTestData {dataTable:[DataTable];
}//root_type字段非常重要
root_type MyTestData;
Flatc.exe文件
flatc文件是干嘛的?它是把schema語法文件生成目標語言代碼的程序。
Releases · google/flatbuffers · GitHubFlatBuffers: Memory Efficient Serialization Library - Releases · google/flatbuffershttps://github.com/google/flatbuffers/releases?
?新建一個批處理文件exportCSharp.bat去運行這個exe程序
flatc.exe --csharp -o Sample MyTestData.fbs
--csharp代表生成目標語言
-o代表輸出文件路徑為 Sample文件夾
在PowerSheel中運行這個bat,不在powerSheel運行就雙擊運行Bat文件,發現有語法錯誤?
修改53行語法錯誤繼續運行bat,成功后沒啥日志
到輸出文件夾去看一下
通過圖片可以看出,我們在Schema文件里定義的數據類型都生成了一份C#文件,把MyGame文件夾拷貝到Unity項目中就可以解析這些數據啦
打開MyTestData文件簡單看一下,反正都是數據格式咱們不需要太關心。
?下載FlatBuffer源碼
為何前面已經導出了schema數據代碼,還要下載FlatBuffer源碼??
因為schema生成的代碼是純數據相關的代碼,需要源碼去驅動數據代碼序列化和反序列化功能。
https://github.com/google/flatbuffershttps://github.com/google/flatbuffers?
?把這些C#腳本拷貝到Unity項目中,多余文件可以刪掉。
把源碼拷貝到項目中后,編譯報錯
原因是我的FlatBuffer源碼使用的是老版本,和flatc.exe代碼的版本號不配套,你們的Flatc和FlatBuffers源碼都從官網下載,肯定是配套的。
在Unity中使用FlatBuffer
完成前面的工作就可以正式使用FlatBuffer啦?
這是官方文檔里推薦的Unity使用FlatBuffer案例
Flatbuffers for Unity + Sample Code | eXiinhttp://exiin.com/blog/flatbuffers-for-unity-sample-code/
測試工程就只有FlatBuffers源碼和我們生成的MyGame數據腳本
整倆按鈕測試
創建一個測試文件UseFlatBuffer.cs
using System.IO;
using UnityEngine;
using MyGame;
using Google.FlatBuffers;
using Color = MyGame.Color;public class UseFlatBuffer : MonoBehaviour
{public void Serilized(){//MyTestDatas是個數組,我們假定數據有5條int dataCount = 5;FlatBufferBuilder builder = new FlatBufferBuilder(1);//創建數組對象Offset<DataTable>[] dataTables = new Offset<DataTable>[dataCount];for (int i = 0; i < dataCount; i++){StringOffset testName = builder.CreateString(i.ToString());VectorOffset inventoryVector = DataTable.CreateIntArrVector(builder,new int[]{i * 10 + 1,i * 10 + 2,i * 10 + 3,i * 10 + 4});//創建Union類型和對象TestUnion unionType = TestUnion.Monster;Offset<Monster> monster = Monster.CreateMonster(builder, builder.CreateString("怪物的名字"), 100);//---------------------開始寫入數據-------------------------------------------------DataTable.StartDataTable(builder);//創建、寫入posOffset<Vec3> pos = Vec3.CreateVec3(builder, 100 * i, 100 * i, 100 * i);DataTable.AddPos(builder,pos);DataTable.AddHp(builder, 2);//short類型DataTable.AddFriendly(builder,true);DataTable.AddName(builder,testName);//string類型//數組的用法DataTable.AddIntArr(builder,inventoryVector);//添加整數數組到對象//枚舉的用法DataTable.AddColor(builder,Color.Red);//Union的使用方法DataTable.AddUnionTargetType(builder,unionType);DataTable.AddUnionTarget(builder, monster.Value);dataTables[i] = DataTable.EndDataTable(builder);}//把5條數據塞入VectorOffset,因為最終數據是一個數組VectorOffset dtArr = MyTestData.CreateDataTableVector(builder, dataTables);//開始寫入數據MyTestData.StartMyTestData(builder);MyTestData.AddDataTable(builder,dtArr);//結束寫入Offset<MyTestData> dtOffset = MyTestData.EndMyTestData(builder);//序列化數據MyTestData.FinishMyTestDataBuffer(builder,dtOffset);byte[] bytes = builder.DataBuffer.ToSizedArray();File.WriteAllBytes("E:\\MyTestData.bytes",bytes);}//反序列化,把數據都打印出來public void Deserilized(){byte[] datas = File.ReadAllBytes("E:\\MyTestData.bytes");ByteBuffer buffer = new ByteBuffer(datas);MyTestData myTestData = MyTestData.GetRootAsMyTestData(buffer);int dtCount = myTestData.DataTableLength;Debug.Log("數據量:" + dtCount.ToString());for (int i = 0; i < dtCount; i++){MyGame.DataTable dt = myTestData.DataTable(i).Value;Vec3 targetPos = dt.Pos.Value;Debug.Log(string.Format("x:{0},y:{1},z{2}",targetPos.X,targetPos.Y,targetPos.Z));for (int j = 0; j < dt.IntArrLength; j++)Debug.Log("intArr: " + dt.IntArr(j).ToString());MyGame.Color color = dt.Color;Debug.Log("color: " + color);}}
}
注意點
1、數據結構支持嵌套自身
table MyTestData {damage:int = 500;data:MyTestData;
}
2、如果你只有一個數據對象,用下面的寫法,根數據就是這個結構體,如果你有多個重復對象,則用上面的寫法,把它寫成一個數組。
table MyTestData {dataTable:[DataTable];
}
root_type MyTestData;
table MyTestData {damage:int = 500;hp:short = 100;name:string;
}
root_type MyTestData;
3、三維數組有簡寫方式,官網里有寫
struct Vec3 {v:[float:3]; }