ゲームで利用するマスターデータ
一般的にゲームデータに採用されている組み込み型マスターデータ(主にゲームのバランスなどが設定されたデータ)の形式はどういった方法で管理しているのだろうか?
今回は私が採用した方式を書き出してみました。
ここで紹介する方法は、本業で開発しているネイティブアプリやリリースした「動物パニックパズル」でも利用している方法です。
尚、ここで言うマスターデータとは、大規模モバイルゲーム運用(ソーシャルゲームの運用)におけるマスターデータのような「定期的な更新」「長期的な運営」を目指す物ではなく、アプリにバンドルしてアップデートのタイミングで更新するような”組み込み型マスターデータ”を想定したものとなっています。
ただし、CDNネットワークからアセットバンドルなどを利用したマスターデータの更新・同期についても運用次第では応用可能なものとはなるはずです。そのあたりは運用方法となるため今回の記事では割愛しています。
独自バイナリ形式
文字通り、独自のバイナリ形式で管理する方法です。
この方法を採用しているゲームの方が多いんじゃないかという印象があります。
具体的な方法としては、C/C++の場合ならデータ構造体(struct)を定義し、それを以下のような方法で読み書きします。
struct GameData {
unsigned short hp;
unsinged short mp;
unsinged char str;
unsinged char spd;
unsigned char mgc;
unsigned char def;
}
int main(int argc, char** argv) {
struct GameData gamedata;
// データを設定する
gamedata.hp = 100;
gamedata.mp = 50;
gamedata.str = 10;
gamedata.spd = 5;
gamedata.mgc = 3;
gamedata.def = 6;
// ファイルにGameData構造体を書き込む
FILE *fp = fopen("gamedata.dat", rb);
fwrite(&gamedata, sizeof(GameData), 1, fp);
// 読み込むときはGameData構造体としてロードする
fread(&gamedata, sizeof(GameData), 1, fp);
return 0;
}
※コードがコンパイル/実行可能であるかどうかは未検証。
上のデータを設定するというコメントを記載したあたりのコードを管理可能なファイル(例えば、エクセルやCSV)からデータを取り出し、それをゲームで使えるフォーマットに変換するツールを作って仕舞えば、ゲーム側のコードでは素直にそのデータ構造に則した構造体でロードするだけなので簡単に実装できるでしょう。
もし、暗号化や難読化を施したい場合は、ファイルを書き込む前にその処理を挟んで仕舞えば実装も簡単です。
メリット
- 独自形式なので管理しやすい
- チューニングしやすい
デメリット
- 一般的なフォーマットではない
- 独自フォーマットを管理するツールを整備する必要がある
主なな利用用途
- マスターデータ
- セーブデータ
JSONフォーマット形式
みんな大好きJSONフォーマットです。
Web界隈ではメジャーな形式であり、API通信する際のフォーマットにもよく使われます。
もちろん、ゲームで利用してもOKです。
OKなのですが、JSONはテキストベースのデータフォーマットであるため、データを解析するために必要な処理時間とそこに割り当てるメモリ量が多くなりがちです。現在のPCやスマートフォンアプリでは割とメモリが潤沢にあるため、あまり問題になりにくいですが、コンソールゲーム等で利用する場合はメモリ使用量やロード時間など次第では採用が厳しい場合があるので取り扱いには注意が必要です。(モバイルゲームを3DSに移植する際にJSONが問題になったプロジェクトもあるようです)
実装は、本当に簡単でUnityの場合は、UnityのJsonシリアライザ・デシリアライザを利用すれば一発です。
[Serializable]
public class GameData {
ushort hp;
ushort mp;
byte str;
byte spd;
byte mgc;
byte def;
}
GameData gamedata = new GameData();
gamedata.hp = 100;
gamedata.mp = 50;
gamedata.str = 10;
gamedata.spd = 5;
gamedata.mgc = 3;
gamedata.def = 6;
// ゲームデータをJSONに変換する
string jsonResult = JsonUtility.ToJSON(gamedata);
// JSONからゲームデータに変換する
gamedata = JsonUtility.FromJSON<GameData>(jsonResult);
とっても簡単!
ただ、このデータは生のテキストデータとなるためデータの改竄がされやすくなります。
データを改竄されたくない場合はやはり暗号化は必須となるでしょう。
メリット
- テキストデータなので管理しやすい(データをすぐに覗いて修正できる等)
- サーバー・クライアント両方で実装が容易
デメリット
- データの解析が遅い
- メモリ使用量が高め
- ファイルサイズが大きくなりがち
主なな利用用途
- マスターデータ(暗号化推奨)
- コンフィグや設定等を保存しておくファイル
- SSLでサーバー通信する時に利用するデータフォーマット
- 改竄されても問題ないファイル
Flatbuffers形式
意外と知られていない形式ですが、スーパーマリオランやFEヒーローズでも使われているようで、権利表示から確認できます。※ただし、どこに使われているかは不明ですが。
Flatbuffers はゲーム用のデータフォーマットとしてGoogleが開発されました。
FlatBuffersは、C ++、C#、C、Go、Java、Kotlin、JavaScript、Lobster、Lua、TypeScript、PHP、Python、Rust、Swift用の効率的なクロスプラットフォームシリアル化ライブラリです。もともとは、ゲーム開発やその他のパフォーマンスが重要なアプリケーションのためにGoogleで作成されました。
このフォーマットは、メモリ使用率、シリアライズ・デシリアライズが高速です。
私はこのフォーマットをベースにしたマスターデータの管理ツールを自作してそれを「動物パニックパズル」でも利用しています。
ただし、Flatbuffersを利用するにはいくつか制約があります。
まず、型が厳格に決められているので、予めのデータ構造を定義する必要があります。
次にデータを作成するのがものすごくめんどくさいです。
正直、めんどくさ過ぎてやりたくありません。
従って、動物パニックパズルでは、ツールで生成したデータをロードしてそれを利用するだけに留めています。
付属のflatcコンパイラを使えば、JSONからバイナリに変換することができるのでこの機能を利用して最終的にFlatbuffers形式に変換しています。
また、バイナリ化後に暗号化も行っています。
Flatbuffersを利用したマスターデータ生成ツールについては、後日公開しようかと思っていますので具体的なコードの転載は割愛します。
メリット
- ゲーム用に開発されたデータフォーマットなのでメモリ使用効率、シリアライズ・デシリアライズ共に性能が良い。
- ネットワーク通信する時のデータフォーマットとしても利用できる。
- データのデシリアライズが楽。
デメリット
- スキーマを定義する必要がある。
- シリアライズ化するプログラムを作るのがかなり面倒くさい。
- イマイチ主流でない
主なな利用用途
- マスターデータ
- SSLでサーバー通信する時に利用するデータフォーマット
MasterMemory形式
UniRx の作者である、河合さんが作成されたオープンソースのマスターデータ管理プログラムです。
UniRx は非同期プログラムを書く際にお世話になっている人も多いと思います。MasterMemoryはその作者が開発されたプログラムなので実用性の高さも期待できるでしょう。
データのフォーマットはMsgPack(バイナリ型のJSON)を利用しているようです。また、ファイルのロードやメモリ使用効率等をみても悪くない性能なので十分ゲームでも利用できそうです。
まだ私は実用していませんが、今後のマスターデータの管理方法に採用する予定です。
番外編:SQLite
ファイルベースデータベースのSQLiteをマスターデータ(またはセーブデータでも)とする方法です。
SQLiteはSQLで書けるので、SQLに慣れている人なら楽な選択肢かもしれません。
ただ、Unityで利用する場合、イマイチこれだというライブラリやアセットがない(知らない)のと、パフォーマンスに関してはあまり良い評判を聞かないので私は使用を見送りました。そもそも、利用している人も少ないのかもしれません。
今後はMasterMemory方式で実装する予定なのであくまで選択肢の一つとして挙げました。