1-第一步是获取“ flatc”编译器和“ FlatBuffers.dll”
- 首先官方下载链接:flatc.exe文件 https://github.com/google/flatbuffers/releases
变异dll的源文件:不推荐):https://github.com/google/flatbuffers源代码中的“ \ net \ FlatBuffers”文件夹,打开“ FlatBuffers.csproj”并编译为“ FlatBuffers.dll”将需要放入项目中
- 或者直接下载:https://pan.baidu.com/s/1pFFAOGnjxPwOLeGBJY6Y_Q 提取码:14hp
将“ FlatBuffers.dll”文件放入asset文件夹下。现在可以在您的工程中愉快的使用了。
首先SaveSchema.fbs文件
// example save file
namespace CompanyNamespaceWhatever;
enum Color : byte { Red = 1, Green, Blue }
union WeaponClassesOrWhatever { Sword, Gun }
struct Vec3 {
x:float;
y:float;
z:float;
}
table GameDataWhatever {
pos:Vec3;
mana:short = 150;
hp:short = 100;
name:string;
inventory:[ubyte];
color:Color = Blue;
weapon:WeaponClassesOrWhatever;
}
table Sword {
damage:int = 10;
distance:short = 5;
}
table Gun {
damage:int = 500;
reloadspeed:short = 2;
}
root_type GameDataWhatever;
file_identifier "WHAT";
更多具体怎么编写,参考:https://google.github.io/flatbuffers/md__schemas.html
然后新建一个批处理文件compile.bat
flatc -n SaveSchema.fbs --gen-onefile
@pause
@pause 主要是让批处理完成不要自动关闭窗口
关于可以生产哪些文件
随后正确的执行.bat文件,会在同目录下生成对应的SaveSchema.cs文件:
// <auto-generated>
// automatically generated by the FlatBuffers compiler, do not modify
// </auto-generated>
namespace CompanyNamespaceWhatever
{
using global::System;
using global::FlatBuffers;
public enum Color : sbyte
{
Red = 1,
Green = 2,
Blue = 3,
};
public enum WeaponClassesOrWhatever : byte
{
NONE = 0,
Sword = 1,
Gun = 2,
};
public struct Vec3 : IFlatbufferObject
{
private Struct __p;
public ByteBuffer ByteBuffer { get { return __p.bb; } }
public void __init(int _i, ByteBuffer _bb) { __p.bb_pos = _i; __p.bb = _bb; }
public Vec3 __assign(int _i, ByteBuffer _bb) { __init(_i, _bb); return this; }
public float X { get { return __p.bb.GetFloat(__p.bb_pos + 0); } }
public float Y { get { return __p.bb.GetFloat(__p.bb_pos + 4); } }
public float Z { get { return __p.bb.GetFloat(__p.bb_pos + 8); } }
public static Offset<Vec3> CreateVec3(FlatBufferBuilder builder, float X, float Y, float Z) {
builder.Prep(4, 12);
builder.PutFloat(Z);
builder.PutFloat(Y);
builder.PutFloat(X);
return new Offset<Vec3>(builder.Offset);
}
};
public struct GameDataWhatever : IFlatbufferObject
{
private Table __p;
public ByteBuffer ByteBuffer { get { return __p.bb; } }
public static GameDataWhatever GetRootAsGameDataWhatever(ByteBuffer _bb) { return GetRootAsGameDataWhatever(_bb, new GameDataWhatever()); }
public static GameDataWhatever GetRootAsGameDataWhatever(ByteBuffer _bb, GameDataWhatever obj) { return (obj.__assign(_bb.GetInt(_bb.Position) + _bb.Position, _bb)); }
public static bool GameDataWhateverBufferHasIdentifier(ByteBuffer _bb) { return Table.__has_identifier(_bb, "WHAT"); }
public void __init(int _i, ByteBuffer _bb) { __p.bb_pos = _i; __p.bb = _bb; }
public GameDataWhatever __assign(int _i, ByteBuffer _bb) { __init(_i, _bb); return this; }
public Vec3? Pos { get { int o = __p.__offset(4); return o != 0 ? (Vec3?)(new Vec3()).__assign(o + __p.bb_pos, __p.bb) : null; } }
public short Mana { get { int o = __p.__offset(6); return o != 0 ? __p.bb.GetShort(o + __p.bb_pos) : (short)150; } }
public short Hp { get { int o = __p.__offset(8); return o != 0 ? __p.bb.GetShort(o + __p.bb_pos) : (short)100; } }
public string Name { get { int o = __p.__offset(10); return o != 0 ? __p.__string(o + __p.bb_pos) : null; } }
#if ENABLE_SPAN_T
public Span<byte> GetNameBytes() { return __p.__vector_as_span(10); }
#else
public ArraySegment<byte>? GetNameBytes() { return __p.__vector_as_arraysegment(10); }
#endif
public byte[] GetNameArray() { return __p.__vector_as_array<byte>(10); }
public byte Inventory(int j) { int o = __p.__offset(12); return o != 0 ? __p.bb.Get(__p.__vector(o) + j * 1) : (byte)0; }
public int InventoryLength { get { int o = __p.__offset(12); return o != 0 ? __p.__vector_len(o) : 0; } }
#if ENABLE_SPAN_T
public Span<byte> GetInventoryBytes() { return __p.__vector_as_span(12); }
#else
public ArraySegment<byte>? GetInventoryBytes() { return __p.__vector_as_arraysegment(12); }
#endif
public byte[] GetInventoryArray() { return __p.__vector_as_array<byte>(12); }
public Color Color { get { int o = __p.__offset(14); return o != 0 ? (Color)__p.bb.GetSbyte(o + __p.bb_pos) : Color.Blue; } }
public WeaponClassesOrWhatever WeaponType { get { int o = __p.__offset(16); return o != 0 ? (WeaponClassesOrWhatever)__p.bb.Get(o + __p.bb_pos) : WeaponClassesOrWhatever.NONE; } }
public TTable? Weapon<TTable>() where TTable : struct, IFlatbufferObject { int o = __p.__offset(18); return o != 0 ? (TTable?)__p.__union<TTable>(o) : null; }
public static void StartGameDataWhatever(FlatBufferBuilder builder) { builder.StartObject(8); }
public static void AddPos(FlatBufferBuilder builder, Offset<Vec3> posOffset) { builder.AddStruct(0, posOffset.Value, 0); }
public static void AddMana(FlatBufferBuilder builder, short mana) { builder.AddShort(1, mana, 150); }
public static void AddHp(FlatBufferBuilder builder, short hp) { builder.AddShort(2, hp, 100); }
public static void AddName(FlatBufferBuilder builder, StringOffset nameOffset) { builder.AddOffset(3, nameOffset.Value, 0); }
public static void AddInventory(FlatBufferBuilder builder, VectorOffset inventoryOffset) { builder.AddOffset(4, inventoryOffset.Value, 0); }
public static VectorOffset CreateInventoryVector(FlatBufferBuilder builder, byte[] data) { builder.StartVector(1, data.Length, 1); for (int i = data.Length - 1; i >= 0; i--) builder.AddByte(data[i]); return builder.EndVector(); }
public static VectorOffset CreateInventoryVectorBlock(FlatBufferBuilder builder, byte[] data) { builder.StartVector(1, data.Length, 1); builder.Add(data); return builder.EndVector(); }
public static void StartInventoryVector(FlatBufferBuilder builder, int numElems) { builder.StartVector(1, numElems, 1); }
public static void AddColor(FlatBufferBuilder builder, Color color) { builder.AddSbyte(5, (sbyte)color, 3); }
public static void AddWeaponType(FlatBufferBuilder builder, WeaponClassesOrWhatever weaponType) { builder.AddByte(6, (byte)weaponType, 0); }
public static void AddWeapon(FlatBufferBuilder builder, int weaponOffset) { builder.AddOffset(7, weaponOffset, 0); }
public static Offset<GameDataWhatever> EndGameDataWhatever(FlatBufferBuilder builder) {
int o = builder.EndObject();
return new Offset<GameDataWhatever>(o);
}
public static void FinishGameDataWhateverBuffer(FlatBufferBuilder builder, Offset<GameDataWhatever> offset) { builder.Finish(offset.Value, "WHAT"); }
public static void FinishSizePrefixedGameDataWhateverBuffer(FlatBufferBuilder builder, Offset<GameDataWhatever> offset) { builder.FinishSizePrefixed(offset.Value, "WHAT"); }
};
public struct Sword : IFlatbufferObject
{
private Table __p;
public ByteBuffer ByteBuffer { get { return __p.bb; } }
public static Sword GetRootAsSword(ByteBuffer _bb) { return GetRootAsSword(_bb, new Sword()); }
public static Sword GetRootAsSword(ByteBuffer _bb, Sword obj) { return (obj.__assign(_bb.GetInt(_bb.Position) + _bb.Position, _bb)); }
public void __init(int _i, ByteBuffer _bb) { __p.bb_pos = _i; __p.bb = _bb; }
public Sword __assign(int _i, ByteBuffer _bb) { __init(_i, _bb); return this; }
public int Damage { get { int o = __p.__offset(4); return o != 0 ? __p.bb.GetInt(o + __p.bb_pos) : (int)10; } }
public short Distance { get { int o = __p.__offset(6); return o != 0 ? __p.bb.GetShort(o + __p.bb_pos) : (short)5; } }
public static Offset<Sword> CreateSword(FlatBufferBuilder builder,
int damage = 10,
short distance = 5) {
builder.StartObject(2);
Sword.AddDamage(builder, damage);
Sword.AddDistance(builder, distance);
return Sword.EndSword(builder);
}
public static void StartSword(FlatBufferBuilder builder) { builder.StartObject(2); }
public static void AddDamage(FlatBufferBuilder builder, int damage) { builder.AddInt(0, damage, 10); }
public static void AddDistance(FlatBufferBuilder builder, short distance) { builder.AddShort(1, distance, 5); }
public static Offset<Sword> EndSword(FlatBufferBuilder builder) {
int o = builder.EndObject();
return new Offset<Sword>(o);
}
};
public struct Gun : IFlatbufferObject
{
private Table __p;
public ByteBuffer ByteBuffer { get { return __p.bb; } }
public static Gun GetRootAsGun(ByteBuffer _bb) { return GetRootAsGun(_bb, new Gun()); }
public static Gun GetRootAsGun(ByteBuffer _bb, Gun obj) { return (obj.__assign(_bb.GetInt(_bb.Position) + _bb.Position, _bb)); }
public void __init(int _i, ByteBuffer _bb) { __p.bb_pos = _i; __p.bb = _bb; }
public Gun __assign(int _i, ByteBuffer _bb) { __init(_i, _bb); return this; }
public int Damage { get { int o = __p.__offset(4); return o != 0 ? __p.bb.GetInt(o + __p.bb_pos) : (int)500; } }
public short Reloadspeed { get { int o = __p.__offset(6); return o != 0 ? __p.bb.GetShort(o + __p.bb_pos) : (short)2; } }
public static Offset<Gun> CreateGun(FlatBufferBuilder builder,
int damage = 500,
short reloadspeed = 2) {
builder.StartObject(2);
Gun.AddDamage(builder, damage);
Gun.AddReloadspeed(builder, reloadspeed);
return Gun.EndGun(builder);
}
public static void StartGun(FlatBufferBuilder builder) { builder.StartObject(2); }
public static void AddDamage(FlatBufferBuilder builder, int damage) { builder.AddInt(0, damage, 500); }
public static void AddReloadspeed(FlatBufferBuilder builder, short reloadspeed) { builder.AddShort(1, reloadspeed, 2); }
public static Offset<Gun> EndGun(FlatBufferBuilder builder) {
int o = builder.EndObject();
return new Offset<Gun>(o);
}
};
}
把对应的.cs文件放到Assets下,就可以使用了,下面加上测试代码:
public class Test: MonoBehaviour
{
void Start()
{
// Create flatbuffer class
FlatBufferBuilder fbb = new FlatBufferBuilder(1);
// Create our sword for GameDataWhatever
//------------------------------------------------------
WeaponClassesOrWhatever weaponType = WeaponClassesOrWhatever.Sword;
Sword.StartSword(fbb);
Sword.AddDamage(fbb, 123);
Sword.AddDistance(fbb, 999);
Offset<Sword> offsetWeapon = Sword.EndSword(fbb);
/*
// For gun uncomment this one and remove the sword one
WeaponClassesOrWhatever weaponType = WeaponClassesOrWhatever.Gun;
Gun.StartGun(fbb);
Gun.AddDamage(fbb, 123);
Gun.AddReloadspeed(fbb, 999);
Offset<Gun> offsetWeapon = Gun.EndGun(fbb);
*/
//------------------------------------------------------
// Create strings for GameDataWhatever
//------------------------------------------------------
StringOffset cname = fbb.CreateString("Test String ! time : " + DateTime.Now);
//------------------------------------------------------
// Create GameDataWhatever object we will store string and weapon in
//------------------------------------------------------
GameDataWhatever.StartGameDataWhatever(fbb);
GameDataWhatever.AddName(fbb, cname);
GameDataWhatever.AddPos(fbb, Vec3.CreateVec3(fbb, 1, 2, 1)); // structs can be inserted directly, no need to be defined earlier
GameDataWhatever.AddColor(fbb, CompanyNamespaceWhatever.Color.Red);
//Store weapon
GameDataWhatever.AddWeaponType(fbb, weaponType);
GameDataWhatever.AddWeapon(fbb, offsetWeapon.Value);
var offset = GameDataWhatever.EndGameDataWhatever(fbb);
//------------------------------------------------------
GameDataWhatever.FinishGameDataWhateverBuffer(fbb, offset);
// Save the data into "SAVE_FILENAME.whatever" file, name doesn't matter obviously
using (var ms = new MemoryStream(fbb.DataBuffer.ToArray(0, fbb.DataBuffer.Length), fbb.DataBuffer.Position, fbb.Offset))
{
File.WriteAllBytes("SAVE_FILENAME.whatever", ms.ToArray());
Debug.Log("SAVED !");
}
}
void ReadClass()
{
ByteBuffer bb = new ByteBuffer(File.ReadAllBytes("SAVE_FILENAME.whatever"));
if (!GameDataWhatever.GameDataWhateverBufferHasIdentifier(bb))
{
throw new Exception("Identifier test failed, you sure the identifier is identical to the generated schema's one?");
}
GameDataWhatever data = GameDataWhatever.GetRootAsGameDataWhatever(bb);
Debug.Log("LOADED DATA : ");
Debug.Log("NAME : " + data.Name);
Debug.Log("POS : " + data.Pos.Value.X + ", " + data.Pos.Value.Y + ", " + data.Pos.Value.Z);
Debug.Log("COLOR : " + data.Color);
Debug.Log("WEAPON TYPE : " + data.WeaponType);
}
private void Update()
{
if (Input.GetKeyDown("l"))
{
ReadClass();
}
}
}
File.WriteAllBytes("SAVE_FILENAME.whatever", ms.ToArray()); 会在工程目录下生成SAVE_FILENAME.whatever文件。这就是我们保存的二进制文件。
参考:https://exiin.com/blog/flatbuffers-for-unity-sample-code/ Flatbuffers for Unity + Sample Code,修正了文章的一些错误。
关于数组的存取:
首先定义schama:
// example save file
namespace D11Game;
struct Vec3 {
x:float;
y:float;
z:float;
}
table Monster {
pos:Vec3;
hp:short = 100;
name:string;
}
table AllList {
monsterArr:[Monster];
}
root_type AllList;
file_identifier "WHAT";
然后代码读写如下:
void WriteClass()
{
long timenow = DateTime.Now.Millisecond;
FlatBufferBuilder fbb = new FlatBufferBuilder(1);
Offset<Monster> []offset = new Offset<Monster>[size];
for (int i = 0; i < size; i++)
{
var str = fbb.CreateString("time" + i);
Monster.StartMonster(fbb);
var vec3 = Vec3.CreateVec3(fbb, i, 1f, 2f);
Monster.AddPos(fbb, vec3);
Monster.AddName(fbb, str);
Monster.AddHp(fbb, 100);
offset[i] = Monster.EndMonster(fbb);
//Monster.FinishMonsterBuffer(fbb, offset[i]);
}
VectorOffset vec = AllList.CreateMonsterArrVector(fbb, offset);
Offset<AllList> offset_list = AllList.CreateAllList(fbb, vec);
fbb.Finish(offset_list.Value);
UnityEngine.Debug.Log(("write time is:" + (DateTime.Now.Millisecond - timenow)));
using (var ms = new MemoryStream(fbb.DataBuffer.ToArray(0, fbb.DataBuffer.Length), fbb.DataBuffer.Position, fbb.Offset))
{
File.WriteAllBytes("SAVE_FILENAME.whatever", ms.ToArray());
UnityEngine.Debug.Log("SAVED !");
}
void ReadClass()
{
if (!File.Exists("SAVE_FILENAME.whatever")) throw new Exception("Load failed : 'SAVE_FILENAME.whatever' not exis, something went wrong");
float timenow = DateTime.Now.Millisecond;
ByteBuffer bb = new ByteBuffer(File.ReadAllBytes("SAVE_FILENAME.whatever"));
if (!AllList.AllListBufferHasIdentifier(bb))
{
throw new Exception("Load failed : 'SAVE_FILENAME.whatever' not exis, something went wrong");
}
UnityEngine.Debug.Log(bb.Length + ",,,," + bb.Position + ",,,," + bb.GetInt(bb.Position));
AllList monster_list = AllList.GetRootAsAllList(bb);
float pos;
string name;
int hp;
for (int i = 0; i < monster_list.MonsterArrLength; i++)
{
Monster monster = (Monster)monster_list.MonsterArr(i);
//pos = monster.Pos.Value.X;
//name = monster.Name;
//hp = monster.Hp;
}
UnityEngine.Debug.Log(("read time is:" + (DateTime.Now.Millisecond - timenow)));
}