flatBuffer for unity c#高效序列化与反序列化

1-第一步是获取“ flatc”编译器和“ FlatBuffers.dll”

变异dll的源文件:不推荐)https://github.com/google/flatbuffers源代码中的“ \ net \ FlatBuffers”文件夹,打开“ FlatBuffers.csproj”并编译为“ FlatBuffers.dll”将需要放入项目中

将“ 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)));
        }

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值