マスターデータ管理の悩み
ゲームエンジニアの皆さん、マスターデータの管理ってどうしてますか?
開発の初期段階で必要になるマスターデータの仕組み。
とりあえずエクセルで管理して、ゲーム側はエクセルから出力したcsvを読み込むようにしとこう・・・なんてすると、後になって面倒なことになります。
例えば、エクセルとCSVを使ったやり方だと、
- エクセルファイルはgitで差分が取れない。マージできない。
- カラムを追加したり変更するたびに、ゲームプログラム側のcsvパース処理も書き換えないといけない
こんな問題が出てきます。これが後になればなるほど、開発イテレーションの遅れとなって足を引っ張るようになってくるんですね。
運営タイトルだと、複数のブランチを並行で作業するのが日常なので、マージできない事による不便さMaxですし、データのミスも多発します。
いろいろと悩み多く、これだ!という解決策がなかなか見つからないのがマスターデータ。
今回は、エクセルを使いつつも、エクセルファイルは使わない、そんなやり方を考案したので、紹介します。
エンジニアはエクセルが嫌い、プランナーは大好き
マスターデータの仕組みを考えたとき、大体以下の様な要件があると思います。
- エクセルで編集したい(プランナーが使いやすいから)
- エクセルファイルを管理したくない(gitで差分がとれない、マージできないから)
- VBAは使いたくない(ソースがエクセルファイルに埋め込まれるので、これまた差分が取れない)
- スキーマ定義からゲームプログラムのソースを自動生成したい
エクセルを使いたい、でも使いたくない・・・矛盾している!
と思うかも知れません。
ではどうしたらよいか?
私の携わったプロジェクトでは、前述の要件を満たすために、以下の方法を取ることにしました。
- gitで管理するマスターデータはyamlで表現する(差分取れるし、マージもできる)
- yamlを編集するために、エクセルを エディターとして 使う
- エディターの開発には、VBAじゃなくWindows PowerShellを使う
- スキーマ定義とデータのシリアライズ・でシリアライズには Protocol Buffersを使う
これで完璧です。
Windows PowerShellは、Windowsで使えるスクリプト言語で、エクセルなどのオフィスソフトを外から操作することができます。
Protocol Buffersは、google製のシリアライザ・デシリアライザで、.protoというデータの定義を書くと、シリアライズ・デシリアライズ処理のソースをいろんな言語で出力してくれます。
今回作ったものの構成を下図に示します。例として、敵キャラのテーブルEnemyTable
で見てみます。
上の図で、enemy_table.proto
, enemy_table.yaml
, EnemyTableバイナリデータ
はgitで管理されます。エクセルは単なるエディタとして使うので、テーブル毎のエクセルファイルはありません。
また、EnemyTableバイナリデータは、.yamlをProtocol Buffersのバイナリデータに変換したものです。
細かく処理を見ていきましょう。
ロード処理
ロードの処理は、
- .protoファイルをロードしてエディタのヘッダ部分を表示する
- .yamlファイルをロードしてエディタのデータ(ボディ)部分を表示する
この2つを行います。
ヘッダのロード
ヘッダをロードするためには、.protoファイルをパースしなければなりません。
そのために、protoc-gen-jsonを使うことにしました。
protoc-gen-jsonは、その名の通り、protocファイルをパースして、結果をjsonとして出力してくれます。
そのjsonデータを読み込んで、エクセル上にカラム名などのヘッダを表示します。
.protocファイル内のコメントもjsonに出力されるので、書式を決めて適切に処理すれば、ゲームの処理とは直接関係ない情報(例では、カラムの説明文)も、ヘッダに反映させることができます。
ボディのロード
ボディ部分は、.yamlを読み込んでExcel上に展開します。
そのために、yamlを一度csvに変換し、それをexcelの機能を利用してロードしています。
yamlをcsvに変換する処理は、C#で書きました。
PowerShellで直接yamlをパースし、エクセルの1つ1つのセルに直接データを書き込むやり方もありますが、これだとすごく処理が重たくなるので、csvを経由する方法を採っています。
セーブ処理
エディタ上のデータを.yamlに変換します。
これも高速化のために、一度csvに保存した後に、yamlに変換しています。
変換処理
yamlをProtocol Buffersのバイナリに変換します。
yamlをパースし、protocによって自動生成されたソースコードを使って、シリアライズされたバイナリデータとして書き出します。
まとめ
以上、私が作ったマスターデータ管理の仕組みと、関連するツールを簡単に紹介しました。
同じ様な事で悩んでいる方の参考になれば、幸いです。
最後に、図中のテキストを貼り付けておきます。
- enable: true
id: 1000
name: slime
hp: 100
_memo: スライム
- enable: true
id: 1001
name: ghost
hp: 120
_memo: ゴースト
//1行分のデータ定義
message Enemy {
uint32 id = 1; //id:敵ID
string name = 2; //name:名前
uint32 hp = 3; //hp:HP
}
//テーブルの定義
message EnemyTable {
repeated Enemy records = 1;
}