cocos2d-xでデータを保存するにはUserDefaultかsqlite3を利用していたが、汎用性のあるライブラリがあったのでその調査メモ。
FlatBuffers
Googleがオープンソース化したC++シリアライズライブラリとして2014/6に公開。
ゲーム開発者のためのライブラリとして高いパフォーマンスを発揮する模様。
C++だけでなくGoやJavaでも利用可能。
C++だとヘッダのみで利用できるので色々な環境に対応できる。
特徴
- パーズ/アンパッキングなしにシリアル化されたデータにアクセスできる
- メモリ効率と速度
- 柔軟性
- ライブラリのコード量の少なさ
- 型安全性
- 利便性
- クロスプラットフォーム
RapidJsonはJSONのパーサで高速であるがFlatBuffersは更に高速に動作する模様。
URL
C++でFlatBuffersを利用できるようにする
FlatBuffersは以下の手順で利用できる。
- スキーマファイルを作成する
- flatcコマンドでスキーマファイルをコンパイルする
- 生成されたヘッダとflatbuffers.h(場合によってはflatbuffersのidl_parser.cppなどのcppもコンパイルしてリンクする)をincludeする
- FlatBufferBuilderでファイルの読み書きを行なう
スキーマファイルをコンパイルできるようにする
- 環境
- Mac OS X 10.9.4
- XCode 5.1.1
- GitHubからリポジトリをcloneする
git clone https://github.com/google/flatbuffers.git
- XCodeでコンパイルする
cd flatbuffers
open build/Xcode/FlatBuffers.xcodeproj
schemeにflatcを選択してビルドする。
flatbuffersディレクトリにflatcというバイナリができる。
以下はflatcのヘルプ
usage: ./flatc [OPTION]... FILE... [-- FILE...]
-b Generate wire format binaries for any data definitions.
-t Generate text output for any data definitions.
-c Generate C++ headers for tables/structs.
-g Generate Go files for tables/structs.
-j Generate Java classes for tables/structs.
-o PATH Prefix PATH to all generated files.
-S Strict JSON: add quotes to field names.
FILEs may depend on declarations in earlier files.
FILEs after the -- must be binary flatbuffer format files.
Output files are named using the base file name of the input,and written to the current directory or the path given by -o.
example: ./flatc -c -b schema1.fbs schema2.fbs data.json
スキーマファイルを書いてC++用ヘッダを作成する
以下ではサンプルをそのまま利用する。独自クラスでも同様にスキーマを書けば良い。
以下のコマンドでスキーマファイルをコンパイルする。
cd samples
../flatc -c monster.fbs
monster_generated.hというヘッダが生成されるのでこれをincludeして利用する。
データの読み書きをしてみる
JSONはサンプルにあるので、バイナリデータを利用してみる。
flatbuffers関連ヘッダはincludeディレクトリに配置。
monster_generated.hはsamplesディレクトリに配置.
C++のコンパイルコマンドはこんな感じ
clang++ -g -Wall -std=c++11 -I include main.cc
以下、main.ccのコード。
#include <iostream>
#include "samples/monster_generated.h"
#include "flatbuffers/flatbuffers.h"
#include "flatbuffers/util.h"
int main(int /*argc*/, const char * /*argv*/[]) {
using namespace MyGame::Sample;
{
// 新規にデータを作成
flatbuffers::FlatBufferBuilder builder;
auto vec = Vec3(1, 2, 3);
auto name = builder.CreateString("Test");
unsigned char inv_data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
auto inventory = builder.CreateVector(inv_data, 10);
auto mloc = CreateMonster(builder, &vec, 100, 50, name, inventory, Color_Blue);
builder.Finish(mloc);
auto monster = GetMonster(builder.GetBufferPointer());
printf("### 作成したデータ: hp=%d, mana=%d, name=%s\n",
monster->hp(), monster->mana(), monster->name()->c_str());
// データファイル書き込み
auto ok = flatbuffers::SaveFile("samples/monsterdata.bin",
reinterpret_cast<const char *>(builder.GetBufferPointer()),
builder.GetSize(),
true);
if (!ok) {
printf("couldn't save files!\n");
return 1;
}
}
{
// データファイル読み込み
std::string binfile;
auto ok = flatbuffers::LoadFile("samples/monsterdata.bin", true, &binfile);
if (!ok) {
printf("couldn't load files!\n");
return 1;
}
auto monster = GetMonster(binfile.data());
printf("### ロードしたデータ: hp=%d, mana=%d, name=%s\n",
monster->hp(), monster->mana(), monster->name()->c_str());
}
return 0;
}
スキーマに関する更なる情報は、flatbuffers/testsディレクトリにあるmonster_test.fbsを参照