はじめに
この記事では、私がソーシャルゲーム開発にエンジニアとして、またプランナーとして、個人としても企業としても長年携わってきて、ベストプラクティスと思われるマスターデータ運用について前置きとして紹介させていただきます。また、最後にそのベストプラクティスを個人から企業まで幅広く汎用的に使用できるものとしてツール化したものをご紹介させていただきます。
前置きが長いので、ツール紹介のみを閲覧希望の方はこちらからジャンプしてください。
Unityでのマスターデータ運用に使われるツールについて
Unityを使用したマスターデータ運用において、一次ソースデータとしておそらく下記のようなものがあげられると思います。
- ScriptableObject
- Excel
- Google SpreadSheet
このうちScriptableObjectをマスターデータの手動入稿先として運用している企業はほとんどありませんが、ここ数年でGoogle SpreadSheetを採用し始めた企業も多いのではないでしょうか?
私自身も色々な会社を転々としておりますが、その中でもここ2、3年でGoogle SpreadSheetを使用しているプロダクトに配属されるケースが多くなってきました。
Google SpreadSheetをマスターデータ運用に使用するケース
これはあくまで一例ですが、Google SpreadSheetを使用する場合、下記のようなワークフローになる場合が多いです。
ここでは、動的に変化するシート内のデータをスタティックなものとして扱うため、また差分情報をGitで確認できるように中間的なデータとしてS3にCSVをGASでアップロードして、それをCIが専用リポジトリにコミットする流れを取っております。
実際に運用してみての感想
Excelのようにgitで差分を簡単に確認はできませんが、Google SpreadSheetを使用することで同じシートを複数人で同時に入稿作業してもコンフリクトは発生しませんので、当時私もExcelを使用する以上にGoogle SpreadSheetを推奨していた時がありました。また、Google SpreadSheetで入稿したデータはGoogle App Script(GAS)を使用することでCIによる様々な自動化が可能であると考えておりました。(というより)Excelなんてレガシー、モダンなGoogle SpreadSheet使うでしょ?みたいな感覚でした
ですが、Google SpreadSheetを使用したプロダクトを数ヶ月、数年と運用していくうちに上記のメリットを大きく上回るデメリットがあることに気づきました。個人や極小規模のプロダクトならこれらのデメリットを感じないケースもあるかもしれませんが、実際にある程度の規模のプロダクトで運用されてきた方なら以下のようなデメリットを感じた方も多いのではないでしょうか?
- 誰がどのような変更をしたのかが、過去を遡って特定できない
- フィルタリングが共有されてしまう(個人用フィルタリングも使いづらい)
- ブランチ運用に超絶向いていない(GASで無理やりやってもちゃんとしたマージなんてまーじ無理)
- ちょっとした更新をするにしても時間がかかる(エディターで直ダウンロードしようとしてもパブリッシングデータがいつ更新されているのかがポーリングできない)
- アカウント管理をちゃんとしてない場合、個人アカウントで作られたシートがその人が退職してアカウントが破棄されると、シートも破棄されて絶望する
などなど、他にもあげるとキリがありませんが、この中でも「ちょっとした更新をするにしても時間がかかる」というデメリットはなんども調整に調整を重ねるようなマスターデータ運用には、非常に不向きであると感じました。そして、これらのデメリットがあまりにも大きいため、この時点でだんだんと
「あれ、Excelくんって割と優秀な子だった?」
という思いが強くなってきました。
Excelをマスターデータ運用に使用するケース
では反対にExcelをマスターデータ運用の一次ソースデータとして使用した場合について、今まで私が経験した中でベストプラクティスと感じたワークフローは以下のようなものでした。
S3やマスターデータ専用リポジトリという中間の存在がなくなったため、先ほどのワークフローより幾分かシンプルに見えると思います。実際、Excelはユーザーが直接リポジトリにコミットすることが可能なため、CI側としてはポーリングを容易に実装することができ、ユーザーがわざわざジョブキッカーになる必要は無くなります。
実際に運用してみての感想
ご存知の通り、大きなデメリットとして同じファイルを追加、編集する際に確実に作業者どちらかの方でコンフリクトが発生してしまい、そのコンフリクトの解決が非常に面倒であるというものがあります。また、Google SpreadSheetとは違い、ライセンスを購入する必要もあり、そのままではGitでの差分もバイナリファイル扱いされるため、差分を確認することもできません。
これらのような非常に大きいデメリットを抱えているExcelですが、私はこのデメリットすらも大きく上回るメリットがExcelにはあると感じております。具体的には以下のようなものです。
- 調整がローカルで全て完結する
- 
xlsxファイルであれば、内部がxmlファイルなので、解析ツールが容易に作成できる。また、差分確認などであれば、世の中にたくさんのツールが出回っている
- 昔からよく使われているツールなので、比較的ほとんどのプランナーが使用できる
- それぞれのユーザーがGitに直接コミットするので、誰がいつ何をしたのかがすぐにわかる
- 
pythonなどでマージツールを作成することができるため、ブランチ運用と相性が良い
- ローカルにあるファイルなので、好き勝手にフィルタリングできる(コミットしないでねw)
などがあげられます。もちろん技術的コストがかかりますが、マージツールを作成することでブランチ運用できることや、比較的ほとんどのプランナーが使用できるという点から、チーム開発する場合でもGoogle SpreadSheetより優れているのではないか、というのが個人的な意見です。
それぞれのデメリットの解決方法
Excelを使用するにあたって、大きなデメリットがあるという点について前述いたしましたが、今までの私のプロダクトではこれらを克服するために下記のような対策を行うことで、デメリットを比較的抑えることができました。
作業者間で発生するコンフリクトの対策
GitはSVNとは違い、ファイル単位でのロックは不可能なため、これらの対策をするには第3者からの干渉が必須と考えました。
その最も良かった対策の一つとして、管理ツールで編集者登録をし、JenkinsからPullRequestのマージをユーザーごとにバリデーションするというものがあげられます。図にすると下記のようなものになります。
Slackに関して、強制力はありませんが各ユーザーに通知を行うことで、よりコンフリクトを避けられるように設計します。とはいえ、ソースコードと同じで基本的には同じものを追加、編集するという場合には基本的に当事者間での情報の共有は必須と考えます。
デフォルト設定で差分を確認できない
前述している通り、こちらはパブリックに公開されているツールが複数あります。
仕組みとしては単純で、xlsxという形式のExcelであれば基本的にxmlファイルをzipでアーカイブしているものになり、これら公開されている差分確認ツールは一般にそれらExcelファイルの中身を解析して、tsvファイルとして出力するためのツールとなっております。
これらxlsxファイルをtsvファイルとして出力するツールを.gitconfigや.gitattributesに設定することで、コンソールではもちろん、SourceTreeのようなGUIツールでもバイナリファイルとしてではなく、tsvファイルとして認識され、差分を出力することが可能です。
結論、どちらを使えば良いのか
長々とExcelを使用した場合と、Google SpreadSheetを使用した場合とで、実際に運用した感想について話させていただきましたが、結論としてどちらを使用するべきかは正直プロダクトや企業、開発するものによって、どちらが良いかは分かれると思います。
例えば、これまでGoogle SpreadSheetを運用してきた経験やツールがすでにあり、GASで作成したツールもたくさんある場合は無理にExcelを使用するべきではないかもしれませんし、アクションゲームのようにキャラクターのパラメータを頻繁に変更しながらバランスを調整するような開発スタイルをとる場合、反映速度が遅いGoogle SpreadSheetを無理に使用するべきではないと考えます。
ですが、私個人的な意見を申すと、もしあなたがまだマスターデータの運用をどうするか迷っている、もしくはまだ何も手をつけていない状態であるのであれば、Excelをマスターデータ運用で使用することをお勧めします。
マスターデータ運用ツールの紹介
前置きがかなり長くなりましたが、ここからは私が前述した内容をより汎用的に使用できるようツール化したものの紹介になります。また、ここから紹介する内容はREADMEで公開している内容を詳しく日本語で解説したものになります。英語でのドキュメントになりますが、より簡易的な使用方法については以下のリポジトリを直接参照ください。
特徴
このツールを使用する上で、大きな特徴は以下の通りです。
- 
Excelをマスターデータ一次ソースファイルとして使用する
- 
DTO/DAO/VOの設計思想を導入している
- 
ScriptableObjectをマスターデータアセットとして使用する(AssetBundleとの親和性を高めるため)
- マスターデータ操作用のスクリプトは全てジェネレーターによって生成される(CIとの親和性を高めるため)
- 
Excelファイル内でEnumタイプ定義をすることができる(タイプ定義がコメントの役割をもつ上、コメントとは違い確実にスクリプトでの定義と同一になる)
- マスターデータアセットのロード処理のプロセスのみをユーザーが定義できる(デモではAddressablesの使用を推奨しております)
導入方法
このツールを使用する前に、以下の手順を行い導入してください。
1. NuGetForUnityのパッケージをプロジェクトに取り込む
こちらから最新のパッケージをダウンロードして、Editorからインポートしてください。
https://github.com/GlitchEnzo/NuGetForUnity/releases
2. ExcelDataReaderのパッケージをインストールする
NuGetを使用して、ExcelDataReaderの以下のパッケージをインストールしてください。
- ExcelDataReader
- ExcelDataReader.DataSet
NuGetを使用してパッケージをダウンロードする方法については、こちらで詳しく解説しております。
3. Addressable Assets Systemのパッケージをインストールする
UnityMasterDataのサンプルスクリプトでAddressablesを使用しております。
メニューからWindow/Package ManagerでPackageManagerを開いて、Addressablesをインストールしてください。
4. UnityMasterDataのパッケージをプロジェクトに取り込む
こちらから最新のパッケージをダウンロードして、Editorからインポートしてください。
https://github.com/tani-shi/unity-master-data/releases
UnityMasterDataの導入は以上になります。
ワークフロー
このツールを使用する上で、以下のようなワークフローを想定しております。
1. マスターデータをエクセルに入稿する
ここで入稿する場合以下のようなフォーマットである必要があります。
| A | B... | ||
|---|---|---|---|
| 1 | プライマリーキー名 *1 | フィールド名 | <= 必ず入力してください | 
| 2 | コメント | コメント | <= 空でも可 | 
| 3 | Enum定義 *2 | <= 空でも可 | |
| 4 | プライマリーキータイプ *1 | フィールドタイプ *2 | <= 必ず入力してください | 
| 5〜 | プライマリーキー値 | パラメータ値 | <= タイプがstringであれば空でも可。それ以外は必ず入力してください | 
- *1 プライマリーキー名はidとなり、プライマリーキータイプはintもしくはuintになるのが一般です。
- *2 Enum定義が空でない場合は、そのカラムのフィールドタイプがそのEnumのタイプ名となり、自動的にMasterDataType.csに定義されます。
サンプルファイルを用意しております。参照ください。
2. マスターデータ操作用のスクリプトを生成・更新する
MasterDataClassGenerator.GenerateAllDataScriptsをあなたのプロジェクトのスクリプトから呼び出してください。この時、以下のようなスクリプトが生成されます。
- {0}/MasterData/DAO/Generated/{1}/{2}DAO.cs
- {0}/MasterData/DTO/Generated/{1}/{2}DTO.cs
- {0}/MasterData/VO/Generated/{1}/{2}VO.cs
- {0}/MasterData/Type/Generated/MasterDataType.cs
- {0}/MasterData/Collection/Generated/MasterDataAccessorObjectCollection.cs
- {0}/MasterData/Editor/Exporter/Generated/{1}Exporter.cs
{0} = 設定したスクリプト生成ルートパス
{1} = エクセルファイル名
{2} = シート名
3. マスターデータをScriptableObjectとして書き出す
MasterDataExporter.Exportをあなたのプロジェクトのスクリプトから呼び出してください。この時、以下のようなアセットが生成されます。
- {0}/MasterData/{1}/{2}.asset
{0} = 設定したアセット書き出しルートパス
{1} = エクセルファイル名
{2} = シート名
基本的なワークフローとしては、ここまでの1.〜3.を繰り返してマスターデータの調整を行っていくような想定をしております。
4. MasterDataManagerBaseを継承して、プロジェクト固有のアセットロード処理を実装したクラスを作成する
ワークフローとして紹介しておりますが、このステップは一度実装すれば基本的に更新する必要はありません。
こちらはサンプルとして、Addressablesを使用してロード処理を実装したサンプルスクリプトです。
5. 4.で作成したマネージャークラスを使いマスターデータを操作する
上で紹介したサンプルマネージャークラスを実装したサンプルスクリプトです。
注意することとして、マスターデータを取得する処理を実装する箇所よりも先に、マスターデータアセットをロードする処理を実装した箇所が先に実行されなければなりません。
最後に
今回紹介させていただいたツールは私が完全に個人的な見解で、今まで経験したマスターデータの運用の中で最も効率的にゲーム開発ができる設計と感じたものをツール化したものになります。もちろんこれが最適解かどうかというとケースバイケースですが、個人で開発をしていてチーム開発のワークフローを経験したことがない方や、これからソーシャルゲーム開発を行っていく上で技術を模索しているような企業の方の何か参考になるようなものになれば幸いです。
また、今回はGoogle SpreadSheetについて運用する上でデメリットばかり触れてしまいましたが、実はこういう使い方するとこっちの方が使いやすいよ!といったような意見があれば、ご指摘いただきたいです。
それでは最後まで閲覧いただき、ありがとうございました。



