FlatBuffers學習筆記二——生成二進制流文件

       很早之前我已經講了如何生成表格解析代碼點這裏,本次講一下如何將表格數據生成二進制流。

 

一、添加FlatBuffers插件

       如果我們要使用FlatBuffers技術,那麼必須向工程中添加FlatBuffers插件,步驟很簡單,我們新建一個Unity3D的空工程,然後在Asset目錄下新建Plugins文件夾,並且將之前生成的FlatBuffers.dll文件放到此目錄下即可。

 

二、設計數據表格

       我們做開發遊戲項目時,經常要爲策劃設計表格,然後由策劃來填寫表格內容,程序來解析表格數據 用於業務邏輯。那麼我們也從設計一個表格開始,比如我們設計一個怪物表Monster.txt,此文件爲普通的文本文件,編碼爲UTF-8格式,內容如下:

                   #Id    Name    Desc    Level    HP    Attack    Depense
                   #INT    STRING    STRING    INT    INT    INT    INT
                   #怪物ID    怪物名稱    描述    等級    血量    攻擊力    防禦力
                   1    大地之熊    大地之熊    1    100    2    0
                   2    烈焰土熊    烈焰土熊    2    180    5    1
                   3    噬魂蟻王    噬魂蟻王    3    200    8    2
                   4    玄冰毒蟻    玄冰毒蟻    4    250    12    3
                   5    九尾天狐    九尾天狐    5    300    18    3
                   6    幽冥火狐    幽冥火狐    6    380    20    5
                   7    不死雪狐    不死雪狐    7    420    25    6
                   8    極地冰狐    極地冰狐    8    480    28    8
                   9    八翼雷鷹王    八翼雷鷹王    9    600    30    10

        這個表每一行的數據之間都以製表符"Tab"相隔,第一行爲表的列名,第二行爲每一列的數據類型,第三行爲對每一列的釋義,從第四行開始就是怪物表的屬性值了,那麼我們就從這樣一張表開始講解如何將此表數據轉化爲FlatBuffers格式的二進制流文件.

 

三、製作fbs文件

       根據上面Monster的表結構,我們可以製作如下的fbs文件,也可以自己寫代碼來生成fbs文件,生成後的Table_Monster.fbs文件內容如下:

namespace FlatBuffersDemo;

table Table_Monster {
  data:[Monster];
}

table Monster {
  Id:int;
  Name:string;
  Desc:string;
  Level:int;
  HP:int;
  Attack:int;
  Depense:int;
}

root_type Table_Monster;

 

四、生成解析表數據的代碼文件

       按照上一節中講的步驟使用flatc.exe工具來生成解析代碼文件Table_Monster.cs,使用如下的命令:

flatc.exe --csharp --gen-onefile Table_Monster.fbs

      使用上述指令後,會生成如下的代碼:

// <auto-generated>
//  automatically generated by the FlatBuffers compiler, do not modify
// </auto-generated>

namespace FlatBuffersDemo
{

using global::System;
using global::FlatBuffers;

public struct Table_Monster : IFlatbufferObject
{
  private Table __p;
  public ByteBuffer ByteBuffer { get { return __p.bb; } }
  public static Table_Monster GetRootAsTable_Monster(ByteBuffer _bb) { return GetRootAsTable_Monster(_bb, new Table_Monster()); }
  public static Table_Monster GetRootAsTable_Monster(ByteBuffer _bb, Table_Monster 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 Table_Monster __assign(int _i, ByteBuffer _bb) { __init(_i, _bb); return this; }

  public Monster? Data(int j) { int o = __p.__offset(4); return o != 0 ? (Monster?)(new Monster()).__assign(__p.__indirect(__p.__vector(o) + j * 4), __p.bb) : null; }
  public int DataLength { get { int o = __p.__offset(4); return o != 0 ? __p.__vector_len(o) : 0; } }

  public static Offset<Table_Monster> CreateTable_Monster(FlatBufferBuilder builder,
      VectorOffset dataOffset = default(VectorOffset)) {
    builder.StartObject(1);
    Table_Monster.AddData(builder, dataOffset);
    return Table_Monster.EndTable_Monster(builder);
  }

  public static void StartTable_Monster(FlatBufferBuilder builder) { builder.StartObject(1); }
  public static void AddData(FlatBufferBuilder builder, VectorOffset dataOffset) { builder.AddOffset(0, dataOffset.Value, 0); }
  public static VectorOffset CreateDataVector(FlatBufferBuilder builder, Offset<Monster>[] data) { builder.StartVector(4, data.Length, 4); for (int i = data.Length - 1; i >= 0; i--) builder.AddOffset(data[i].Value); return builder.EndVector(); }
  public static VectorOffset CreateDataVectorBlock(FlatBufferBuilder builder, Offset<Monster>[] data) { builder.StartVector(4, data.Length, 4); builder.Add(data); return builder.EndVector(); }
  public static void StartDataVector(FlatBufferBuilder builder, int numElems) { builder.StartVector(4, numElems, 4); }
  public static Offset<Table_Monster> EndTable_Monster(FlatBufferBuilder builder) {
    int o = builder.EndObject();
    return new Offset<Table_Monster>(o);
  }
  public static void FinishTable_MonsterBuffer(FlatBufferBuilder builder, Offset<Table_Monster> offset) { builder.Finish(offset.Value); }
  public static void FinishSizePrefixedTable_MonsterBuffer(FlatBufferBuilder builder, Offset<Table_Monster> offset) { builder.FinishSizePrefixed(offset.Value); }
};

public struct Monster : IFlatbufferObject
{
  private Table __p;
  public ByteBuffer ByteBuffer { get { return __p.bb; } }
  public static Monster GetRootAsMonster(ByteBuffer _bb) { return GetRootAsMonster(_bb, new Monster()); }
  public static Monster GetRootAsMonster(ByteBuffer _bb, Monster 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 Monster __assign(int _i, ByteBuffer _bb) { __init(_i, _bb); return this; }

  public int Id { get { int o = __p.__offset(4); return o != 0 ? __p.bb.GetInt(o + __p.bb_pos) : (int)0; } }
  public string Name { get { int o = __p.__offset(6); return o != 0 ? __p.__string(o + __p.bb_pos) : null; } }
#if ENABLE_SPAN_T
  public Span<byte> GetNameBytes() { return __p.__vector_as_span(6); }
#else
  public ArraySegment<byte>? GetNameBytes() { return __p.__vector_as_arraysegment(6); }
#endif
  public byte[] GetNameArray() { return __p.__vector_as_array<byte>(6); }
  public string Desc { get { int o = __p.__offset(8); return o != 0 ? __p.__string(o + __p.bb_pos) : null; } }
#if ENABLE_SPAN_T
  public Span<byte> GetDescBytes() { return __p.__vector_as_span(8); }
#else
  public ArraySegment<byte>? GetDescBytes() { return __p.__vector_as_arraysegment(8); }
#endif
  public byte[] GetDescArray() { return __p.__vector_as_array<byte>(8); }
  public int Level { get { int o = __p.__offset(10); return o != 0 ? __p.bb.GetInt(o + __p.bb_pos) : (int)0; } }
  public int HP { get { int o = __p.__offset(12); return o != 0 ? __p.bb.GetInt(o + __p.bb_pos) : (int)0; } }
  public int Attack { get { int o = __p.__offset(14); return o != 0 ? __p.bb.GetInt(o + __p.bb_pos) : (int)0; } }
  public int Depense { get { int o = __p.__offset(16); return o != 0 ? __p.bb.GetInt(o + __p.bb_pos) : (int)0; } }

  public static Offset<Monster> CreateMonster(FlatBufferBuilder builder,
      int Id = 0,
      StringOffset NameOffset = default(StringOffset),
      StringOffset DescOffset = default(StringOffset),
      int Level = 0,
      int HP = 0,
      int Attack = 0,
      int Depense = 0) {
    builder.StartObject(7);
    Monster.AddDepense(builder, Depense);
    Monster.AddAttack(builder, Attack);
    Monster.AddHP(builder, HP);
    Monster.AddLevel(builder, Level);
    Monster.AddDesc(builder, DescOffset);
    Monster.AddName(builder, NameOffset);
    Monster.AddId(builder, Id);
    return Monster.EndMonster(builder);
  }

  public static void StartMonster(FlatBufferBuilder builder) { builder.StartObject(7); }
  public static void AddId(FlatBufferBuilder builder, int Id) { builder.AddInt(0, Id, 0); }
  public static void AddName(FlatBufferBuilder builder, StringOffset NameOffset) { builder.AddOffset(1, NameOffset.Value, 0); }
  public static void AddDesc(FlatBufferBuilder builder, StringOffset DescOffset) { builder.AddOffset(2, DescOffset.Value, 0); }
  public static void AddLevel(FlatBufferBuilder builder, int Level) { builder.AddInt(3, Level, 0); }
  public static void AddHP(FlatBufferBuilder builder, int HP) { builder.AddInt(4, HP, 0); }
  public static void AddAttack(FlatBufferBuilder builder, int Attack) { builder.AddInt(5, Attack, 0); }
  public static void AddDepense(FlatBufferBuilder builder, int Depense) { builder.AddInt(6, Depense, 0); }
  public static Offset<Monster> EndMonster(FlatBufferBuilder builder) {
    int o = builder.EndObject();
    return new Offset<Monster>(o);
  }
};


}

五、生成二進制文件

       解析表數據的代碼文件生成之後,就可以生成二進制文件了。

       我們可以先來解析Monster.txt文件的數據,然後通過生成的代碼再來生成 二進制文件,主要代碼段如下:

    public static void Encode()
    {
        FlatBufferBuilder fbBuilder = new FlatBufferBuilder(1024);

        Offset<Monster>[] monsters = new Offset<Monster>[9];

        FileStream fileStream = new FileStream("./Assets/DataTable/Monster.txt", FileMode.Open, FileAccess.Read);
        StreamReader streamReader = new StreamReader(fileStream, System.Text.Encoding.UTF8);

        try
        {
            fileStream.Seek(0, SeekOrigin.Begin);

            int index = 0;
            string line = streamReader.ReadLine();
            while (!string.IsNullOrEmpty(line))
            {
                if (!line.StartsWith("#"))
                {
                    string[] array = line.Split(new char[] { '\t' });

                    int id = int.Parse(array[0]);
                    StringOffset name = fbBuilder.CreateString(array[1]);
                    StringOffset desc = fbBuilder.CreateString(array[2]);
                    int level = int.Parse(array[3]);
                    int hp = int.Parse(array[4]);
                    int attack = int.Parse(array[5]);
                    int depense = int.Parse(array[6]);

                    monsters[index] = Monster.CreateMonster(fbBuilder, id, name, desc, level, hp, attack, depense);

                    index++;
                }

                line = streamReader.ReadLine();
            }
        }
        catch(IOException e)
        {
            Debug.LogErrorFormat("IO Error: {0}", e.Message);
        }
        finally
        {
            streamReader.Close();
            streamReader.Dispose();

            fileStream.Close();
            fileStream.Dispose();
        }

        VectorOffset table_Monster = Table_Monster.CreateDataVector(fbBuilder, monsters);

        Table_Monster.StartTable_Monster(fbBuilder);
        Table_Monster.AddData(fbBuilder, table_Monster);
        var eab = Table_Monster.EndTable_Monster(fbBuilder);
        Table_Monster.FinishTable_MonsterBuffer(fbBuilder, eab);

        byte[] buffers = fbBuilder.SizedByteArray();

        FileStream fileWrite = new FileStream("./Assets/DataFlatBuffer/Monster.bytes", FileMode.OpenOrCreate, FileAccess.ReadWrite);
        fileWrite.Write(buffers, 0, buffers.Length);
        fileWrite.Flush();
        fileWrite.Close();
        fileWrite.Dispose();
    }

    調用上面的函數之後,我們就把Monster.txt中的源數據生成了二進制字節數據Monster.bytes

 

     至此,我們生成二進制流文件就大功告成了,下一講我會在此基數上講解如何解析二進制文件,最終達到我們遊戲項目中要使用數據的目的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章