「IDOLY PRIDE」におけるマスターデータについて
はじめに
株式会社QualiArtsでUnityエンジニアをしている吉田です。 2021年6月リリースの「IDOLY PRIDE」(以降、アイプラ)の開発に携わり、主にクライアント側のAPI,マスターデータなど共通基盤系の開発などを担当していました。
本記事では、アイプラでのマスターデータについて解説していきます。
マスターデータとは
GREEさんのEngineers' Blogにいい説明がありましたので、引用させていただきました。
マスターデータとは、ゲーム内で不変の共通パラメータ群のことを指します。モバイルゲームにおいては、アプリのバイナリアップデートをせずにゲームに反映できるよう、起動時に最新のマスターデータを引っ張ってくることが多いです。
出典:https://labs.gree.jp/blog/2015/12/15368/
まさに、アイプラでも同様でゲーム内での不変共通パラメータであり、アプリアップデートとは別にゲームに反映できるように、サーバーからマスターデータを取得しています。
マスター情報の取得
サーバーサイドに対してマスター情報取得APIを実行し、そのレスポンスからマスターファイルのダウンロード先URLなど必要な情報を得ることができます。
ダウンロードしたマスターファイルは端末のローカルストレージに保存されます。
アイプラのマスターファイルは現在132ファイルあります。
1つのマスターファイルに1種類のマスターが対応しており、運用でマスターデータが更新された場合は更新されたマスターファイルのみダウンロードされるようになっています。
マスターファイルについて
マスターファイルはSQLCipherのDBファイルになっています。
下記はキャラクターマスターを例にDBファイルにデータが格納されているイメージになります。
テーブルのカラムはデータを一意に識別するためのPrimaryKeyカラム部とdataカラムで構成されています。
dataカラムにはProtocol Buffers(以降proto)形式でシリアリズされたバイトデータが格納されています。
キャラクターマスターのテーブルイメージ
Id | data |
---|---|
char-kotono | byte[] |
char-sakura | byte[] |
: | : |
// テーブル定義DDL:キャラクターマスター
CREATE TABLE Character(
Id STRING, // データを一意に識別するためのID
data BLOB, // protoのシリアリズされたバイトデータが格納される
PRIMARY KEY(Id)
)
// proto定義:キャラクターマスター
message Character {
option (master.table) = true;
// id
string id = 1 [(master.pk) = true];
// アセットID
string assetId = 2;
// 所属グループID
string characterGroupId = 3 [(master.fk) = "CharacterGroup"];
// 順番
int32 order = 4;
// キャラクター名
string name = 5;
:
}
MasterManger
クライントでのマスターデータの管理はMasterMangaerクラスが責任をもっています。
マスターファイルのダウンロードや、マスターデータ取得もこのクラスを経由することになります。
マスターデータ取得方法
Key(データを一意に識別できる値)でのデータ取得メソッド(FindByKey系)と、全件取得メソッド(GetAll系)の2つのタイプのメソッドが用意されています。
GetAll系のメソッドは対象条件があるメソッドでも、全データ取得してからフィルターされるのでコストが高いメソッドになります。
下記キャラクターマスターからのデータ取得例になります。
// Keyで取得
var kotono = MasterManager.CharacterMaster.FindByKey("char-kotono");
// Keyを複数で取得
var chars = MasterManager.CharacterMaster.FindByKey(new []{"char-kotono","char-sakura"});
// 全データ取得
var allChars = MasterManager.CharacterMaster.GetAll();
// 対象条件とソート設定して取得
var filterChars = MasterManager.CharacterMaster.GetAll(x=> x.CharacterGroupId = "sun", (a,b)=> a.Order.CompareTo(b.Order));
// 全データ取得かつKeyでソート
var allCharsSortById = MasterManager.CharacterMaster.GetAllWithSortByKey(CharacterSortType.Id_Asc);
MasterManager.xxxxMaster
のxxxx
部分は各マスター毎に変化します。
マスターデータのリレーション
取得したマスターデータが別のマスターデータとリレーションしている場合があります。
リレーションされたデータの取得コードは冗長になるのでなるので、シンプルに取得できるような工夫をしています。
下記例はキャラクターマスターからキャラクターグループマスターを取得する例になります。
// Keyで取得
var kotono = MasterManager.CharacterMaster.FindByKey("char-kotono");
// リレーションデータの取得
// この方法だと面倒でコードが冗長すぎる
var charGroup = MasterManager.CharacterGroupMaster.FindByKey(kotono.CharacterGroupId);
// 上記の方法よりコードがスッキリ( プロパティ内部では上記と同じコードになっている )
var charGroup2 = kotono.CharacterGroup;
自動生成
これらのコードはproto定義に設定されているoption情報(マスター用custom option)を利用して自動生成されており、新規マスターの追加、既存マスターの更新された場合に、手動でのコードのメンテナンスは不要になっています。
protoで独自のマスター用custom optionを定義するにあたり、Qiitaのprotocプラグインとカスタムオプションを参考にさせていただきました。
// proto定義:キャラクターマスター
message Character {
option (master.table) = true; // マスター判定用option
// id
string id = 1 [(master.pk) = true]; // PrimaryKey判定用option
// アセットID
string assetId = 2;
// 所属グループID
string characterGroupId = 3 [(master.fk) = "CharacterGroup"]; // マスターリレーション判定用option
// 順番
int32 order = 4;
// キャラクター名
string name = 5;
:
}