{"id":3893,"date":"2021-05-02T01:05:28","date_gmt":"2021-05-01T17:05:28","guid":{"rendered":"http:\/\/blog.coolcoding.cn\/?p=3893"},"modified":"2021-05-02T01:07:32","modified_gmt":"2021-05-01T17:07:32","slug":"%e4%bd%bf%e7%94%a8flatbuffers%e6%9b%bf%e4%bb%a3protobuff","status":"publish","type":"post","link":"https:\/\/blog.coolcoding.cn\/?p=3893","title":{"rendered":"\u4f7f\u7528FlatBuffers\u66ff\u4ee3ProtoBuff"},"content":{"rendered":"\n<h1 class=\"wp-block-heading\">1- First step is to get&nbsp;the \u201cflatc\u201d compiler and \u201cFlatBuffers.dll\u201d<\/h1>\n\n\n\n<p>Flatc&nbsp;is used to convert schemas into c# code which I\u2019ll explain later, and the dll is the main library.<\/p>\n\n\n\n<p>You can download the latest release at :<br><a href=\"https:\/\/github.com\/google\/flatbuffers\/releases\">https:\/\/github.com\/google\/flatbuffers\/releases<\/a><\/p>\n\n\n\n<p>or you can simply compile the bleeding edge version yourself like a pro&nbsp;( not recommended )&nbsp;:<br><a href=\"https:\/\/github.com\/google\/flatbuffers\">https:\/\/github.com\/google\/flatbuffers<\/a><\/p>\n\n\n\n<p>You first either compile or simply download&nbsp;the latest&nbsp;<strong>flatc.exe<\/strong>&nbsp;file,<br>then you go to \u201c\\net\\FlatBuffers\u201d folder inside the source and open up \u201cFlatBuffers.csproj\u201d<br>and compile it to get the \u201c<strong>FlatBuffers.dll\u201d&nbsp;which you\u2019ll need to put inside your unity project<\/strong>&nbsp;( I put it inside assets\/plugins folder ).<\/p>\n\n\n\n<p>What I did next was create a separate folder for my compiler&nbsp;and schema like this :<\/p>\n\n\n\n<figure class=\"wp-block-image\"><a href=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2021\/05\/Captur52423e.png\"><img decoding=\"async\" src=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2021\/05\/Captur52423e.png\" alt=\"Captur52423e\" class=\"wp-image-2427\"\/><\/a><\/figure>\n\n\n\n<p> And instead of hurting my fingers by writing same command to compile over and over again,\u00a0I simply made this dumb batch script (\u00a0<strong>compile.bat<\/strong>\u00a0) which contains these lines : <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>flatc -n SaveSchema.txt --gen-onefile\n@pause<\/code><\/pre>\n\n\n\n<p>This executes flatc with parameters such as \u201c-n\u201d for c# code generation, \u201c\u2013gen-onefile\u201d for generating all classes inside one .cs file which is optional,&nbsp;and finally \u201c@pause\u201d which is also completely optional but it prevents from the console window from closing automatically ( useful when you have compile errors ).<\/p>\n\n\n\n<p>And for demonstration purposes, I\u2019ll use a demo\u00a0SaveSchema.txt file :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ example save file\n\nnamespace CompanyNamespaceWhatever;\n\nenum Color : byte { Red = 1, Green, Blue }\n\nunion WeaponClassesOrWhatever { Sword, Gun }\n\nstruct Vec3 {\n  x:float;\n  y:float;\n  z:float;\n}\n\ntable GameDataWhatever {\n  pos:Vec3;\n  mana:short = 150;\n  hp:short = 100;\n  name:string;\n  inventory:[ubyte];\n  color:Color = Blue;\n  weapon:WeaponClassesOrWhatever;\n}\n\ntable Sword {\n  damage:int = 10;\n  distance:short = 5;\n}\n\ntable Gun {\n  damage:int = 500;\n  reloadspeed:short = 2;\n}\n\nroot_type GameDataWhatever;\nfile_identifier \"WHAT\";<\/code><\/pre>\n\n\n\n<p>The schema file is what defines the structure of the save, and what values are gonna be saved.<br><em>For more info on the&nbsp;schema syntax&nbsp;please&nbsp;<a href=\"https:\/\/google.github.io\/flatbuffers\/md__schemas.html\">read this official doc page<\/a>.<\/em><\/p>\n\n\n\n<p>Once you execute compile.bat it\u2019ll create a new file named \u201cSavedSchema.cs\u201d<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2021\/05\/Captu4545re.png\" alt=\"Captu4545re\"\/><\/figure>\n\n\n\n<p>latc now generated several classes with names such as \u201cWeaponClassesOrWhatever\u201d<\/p>\n\n\n\n<p>This file is your whole loading and saving system for that schema, this file is now your ultimate file.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">2- Next step, how do we save our data?<\/h1>\n\n\n\n<p>The generated .cs file contains all classes and functions required to save and load data from and to flatbuffers.&nbsp;But in order to load, we\u2019ll need to save something first.<\/p>\n\n\n\n<p><strong>Place that generated file into your project<\/strong><br>( Also don\u2019t forget to place&nbsp;\u201c<strong>FlatBuffers.dll\u201d&nbsp;<\/strong>in your project or else you\u2019re gonna see some errors )<\/p>\n\n\n\n<p>Then place this code :<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Create flatbuffer class\nFlatBufferBuilder fbb = new FlatBufferBuilder(1);\n\n\/\/ Create our sword for GameDataWhatever\n\/\/------------------------------------------------------\n        \nWeaponClassesOrWhatever weaponType = WeaponClassesOrWhatever.Sword;\nSword.StartSword(fbb);\nSword.AddDamage(fbb, 123);\nSword.AddDistance(fbb, 999);\nOffset&lt;Sword> offsetWeapon = Sword.EndSword(fbb);\n        \n\/*\n\/\/ For gun uncomment this one and remove the sword one\nWeaponClassesOrWhatever weaponType = WeaponClassesOrWhatever.Gun;\nGun.StartGun(fbb);\nGun.AddDamage(fbb, 123);\nGun.AddReloadspeed(fbb, 999);\nOffset&lt;Gun> offsetWeapon = Gun.EndGun(fbb);\n*\/\n\/\/------------------------------------------------------\n\n\/\/ Create strings for GameDataWhatever\n\/\/------------------------------------------------------\nStringOffset cname = fbb.CreateString(\"Test String ! time : \" + DateTime.Now);\n\/\/------------------------------------------------------\n\n\/\/ Create GameDataWhatever object we will store string and weapon in\n\/\/------------------------------------------------------\nGameDataWhatever.StartGameDataWhatever(fbb);\n\nGameDataWhatever.AddName(fbb, cname);\nGameDataWhatever.AddPos(fbb, Vec3.CreateVec3(fbb, 1, 2, 1)); \/\/ structs can be inserted directly, no need to be defined earlier\nGameDataWhatever.AddColor(fbb, CompanyNamespaceWhatever.Color.Red);\n\n\/\/Store weapon\nGameDataWhatever.AddWeaponType(fbb, weaponType);\nGameDataWhatever.AddWeapon(fbb, offsetWeapon.Value);\n\nvar offset = GameDataWhatever.EndGameDataWhatever(fbb);\n\/\/------------------------------------------------------\n\nGameDataWhatever.FinishGameDataWhateverBuffer(fbb, offset);\n\n\/\/ Save the data into \"SAVE_FILENAME.whatever\" file, name doesn't matter obviously\nusing (var ms = new MemoryStream(fbb.DataBuffer.Data, fbb.DataBuffer.Position, fbb.Offset)) {\n    File.WriteAllBytes(\"SAVE_FILENAME.whatever\", ms.ToArray());\n    Debug.Log(\"SAVED !\");\n}<\/code><\/pre>\n\n\n\n<p>The way you&nbsp;write your data&nbsp;is&nbsp;<strong>ORDER&nbsp;<\/strong><strong>DEPENDENT<\/strong>.<br><strong>You always have to create items from inside out<\/strong>.<br>Starting from&nbsp;everything that object contains ( such as strings, arrays, other objects ) to the object itself.<br>Basically, you have to create objects upfront before setting them inside another object.<\/p>\n\n\n\n<p>So what happens here is :<br>We create&nbsp;the weapon and string first because it\u2019s gonna be inside the GameDataWhatever.<br>Everything that can have a variable length needs to be setup first before it is inserted in the buffer.<br>And only then once we have them setup, we create&nbsp;GameDataWhatever in which we\u2019re gonna set those values.<\/p>\n\n\n\n<p><em>The saving part can be really tricky, I advise you to&nbsp;<a href=\"https:\/\/google.github.io\/flatbuffers\/md__java_usage.html\">read this page&nbsp;<\/a>to have a better understanding on how&nbsp;to store data.<\/em><\/p>\n\n\n\n<h1 class=\"wp-block-heading\">3- Finally, reading the file is a piece of cake.<\/h1>\n\n\n\n<p>Reading can be done in any order you want, if you want you don\u2019t even need to go through\u00a0all values because flatbuffers work like magic !<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ByteBuffer bb = new ByteBuffer(File.ReadAllBytes(\"SAVE_FILENAME.whatever\"));\n\nif (!GameDataWhatever.GameDataWhateverBufferHasIdentifier(bb)) {\n    throw new Exception(\"Identifier test failed, you sure the identifier is identical to the generated schema's one?\");\n}\n\nGameDataWhatever data = GameDataWhatever.GetRootAsGameDataWhatever(bb);\n\nDebug.Log(\"LOADED DATA : \");\nDebug.Log(\"NAME : \" + data.Name);\nDebug.Log(\"POS : \" + data.Pos.X + \", \" + data.Pos.Y + \", \" + data.Pos.Z);\nDebug.Log(\"COLOR : \" + data.Color);\n\nDebug.Log(\"WEAPON TYPE : \" + data.WeaponType);\n\nswitch (data.WeaponType) {\n    case WeaponClassesOrWhatever.Sword:\n        Sword sword = new Sword();\n        data.GetWeapon&lt;Sword>(sword);\n        Debug.Log(\"SWORD DAMAGE  : \" + sword.Damage);\n        break;\n    case WeaponClassesOrWhatever.Gun:\n        Gun gun = new Gun();\n        data.GetWeapon&lt;Gun>(gun);\n        Debug.Log(\"GUN RELOAD SPEED  : \" + gun.Reloadspeed);\n        break;\n    default:\n        break;\n}<\/code><\/pre>\n\n\n\n<p>There you go !<\/p>\n\n\n\n<p>We tested flatbuffers on all major mobile platforms ( iOS, Android, Amazon Os, Windows Phone ) we\u2019re building&nbsp;on&nbsp;and it works pretty well.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">4- Source Code for Unity<\/h1>\n\n\n\n<p>If you want the full working sample&nbsp;code along with compiled flatbuffer files flatc.exe and flatbuffers.dll, then download this file :<\/p>\n\n\n\n<div class=\"wp-block-file\"><a href=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2021\/05\/FlatBuffersTest.zip\">FlatBuffersTest<\/a><a href=\"http:\/\/blog.coolcoding.cn\/wp-content\/uploads\/2021\/05\/FlatBuffersTest.zip\" class=\"wp-block-file__button\" download>\u4e0b\u8f7d<\/a><\/div>\n","protected":false},"excerpt":{"rendered":"<p>1- First step is to get&nbsp;the \u201cflatc\u201d compiler and \u201c [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"_links":{"self":[{"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/posts\/3893"}],"collection":[{"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=3893"}],"version-history":[{"count":2,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/posts\/3893\/revisions"}],"predecessor-version":[{"id":3898,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=\/wp\/v2\/posts\/3893\/revisions\/3898"}],"wp:attachment":[{"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3893"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3893"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.coolcoding.cn\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3893"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}