マスターデータ運用のベストプラクティスをツール化してみた【UnityMasterData】

# はじめに

この記事では、私がソーシャルゲーム開発にエンジニアとして、またプランナーとして、個人としても企業としても長年携わってきて、ベストプラクティスと思われるマスターデータ運用について前置きとして紹介させていただきます。また、最後にそのベストプラクティスを個人から企業まで幅広く汎用的に使用できるものとしてツール化したものをご紹介させていただきます。

前置きが長いので、ツール紹介のみを閲覧希望の方は[こちら](https://qiita.com/tani-shi/items/c3841af724b354ba4d88#%E3%83%9E%E3%82%B9%E3%82%BF%E3%83%BC%E3%83%87%E3%83%BC%E3%82%BF%E9%81%8B%E7%94%A8%E3%83%84%E3%83%BC%E3%83%AB%E3%81%AE%E7%B4%B9%E4%BB%8B)からジャンプしてください。

## Unityでのマスターデータ運用に使われるツールについて

Unityを使用したマスターデータ運用において、一次ソースデータとしておそらく下記のようなものがあげられると思います。

- ScriptableObject
- Excel
- Google SpreadSheet

このうち`ScriptableObject`をマスターデータの手動入稿先として運用している企業はほとんどありませんが、ここ数年で`Google SpreadSheet`を採用し始めた企業も多いのではないでしょうか？
私自身も色々な会社を転々としておりますが、その中でもここ２、３年で`Google SpreadSheet`を使用しているプロダクトに配属されるケースが多くなってきました。

## Google SpreadSheetをマスターデータ運用に使用するケース

これはあくまで一例ですが、`Google SpreadSheet`を使用する場合、下記のようなワークフローになる場合が多いです。

![The workflow using Google SpreadSheet (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132627/67e55145-12a2-9ebd-e998-8a495e6d53ed.png)

ここでは、動的に変化するシート内のデータをスタティックなものとして扱うため、また差分情報を`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`をマスターデータ運用の一次ソースデータとして使用した場合について、今まで私が経験した中でベストプラクティスと感じたワークフローは以下のようなものでした。

![The workflow using Excel.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132627/bc2317d8-841d-1df1-4cb3-28f25857a675.png)

`S3`やマスターデータ専用リポジトリという中間の存在がなくなったため、先ほどのワークフローより幾分かシンプルに見えると思います。実際、`Excel`はユーザーが直接リポジトリにコミットすることが可能なため、`CI`側としてはポーリングを容易に実装することができ、ユーザーがわざわざジョブキッカーになる必要は無くなります。

### 実際に運用してみての感想

ご存知の通り、大きなデメリットとして同じファイルを追加、編集する際に確実に作業者どちらかの方でコンフリクトが発生してしまい、そのコンフリクトの解決が非常に面倒であるというものがあります。また、`Google SpreadSheet`とは違い、ライセンスを購入する必要もあり、そのままでは`Git`での差分もバイナリファイル扱いされるため、差分を確認することもできません。

これらのような非常に大きいデメリットを抱えている`Excel`ですが、私はこのデメリットすらも大きく上回るメリットが`Excel`にはあると感じております。具体的には以下のようなものです。

- 調整がローカルで全て完結する
- `xlsx`ファイルであれば、内部が`xml`ファイルなので、解析ツールが容易に作成できる。また、差分確認などであれば、世の中にたくさんのツールが出回っている
- 昔からよく使われているツールなので、比較的ほとんどのプランナーが使用できる
- それぞれのユーザーが`Git`に直接コミットするので、誰がいつ何をしたのかがすぐにわかる
- `python`などでマージツールを作成することができるため、ブランチ運用と相性が良い
- ローカルにあるファイルなので、好き勝手にフィルタリングできる（コミットしないでねw）

などがあげられます。もちろん技術的コストがかかりますが、マージツールを作成することでブランチ運用できることや、比較的ほとんどのプランナーが使用できるという点から、**チーム開発する場合でも`Google SpreadSheet`より優れている**のではないか、というのが個人的な意見です。

### それぞれのデメリットの解決方法

`Excel`を使用するにあたって、大きなデメリットがあるという点について前述いたしましたが、今までの私のプロダクトではこれらを克服するために下記のような対策を行うことで、デメリットを比較的抑えることができました。

#### 作業者間で発生するコンフリクトの対策

`Git`は`SVN`とは違い、ファイル単位でのロックは不可能なため、これらの対策をするには第３者からの干渉が必須と考えました。

その最も良かった対策の一つとして、**管理ツールで編集者登録をし、JenkinsからPullRequestのマージをユーザーごとにバリデーションする**というものがあげられます。図にすると下記のようなものになります。

![The workflow to prevent conflicts at excel files.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132627/f5a6cd18-cb92-8667-1eb2-7222d6403a57.png)

`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`で公開している内容を詳しく日本語で解説したものになります。英語でのドキュメントになりますが、より簡易的な使用方法については以下のリポジトリを直接参照ください。

https://github.com/tani-shi/unity-master-data

![How works UnityMasterData (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132627/08de1108-ab2e-891d-fde5-66e847ab4abe.png)

## 特徴

このツールを使用する上で、大きな特徴は以下の通りです。

- `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`を使用してパッケージをダウンロードする方法については、[こちら](https://qiita.com/tani-shi/items/b155858f07c7350d3e2d)で詳しく解説しております。

### 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`に定義されます。

[サンプルファイル](https://github.com/tani-shi/unity-master-data/blob/master/Assets/UnityMasterDataDemo/Excels/Sample.xlsx?raw=true)を用意しております。参照ください。

### 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`を使用してロード処理を実装した[サンプルスクリプト](https://github.com/tani-shi/unity-master-data/blob/master/Assets/UnityMasterDataDemo/Scripts/DemoMasterDataManager.cs)です。

### 5. 4.で作成したマネージャークラスを使いマスターデータを操作する

上で紹介したサンプルマネージャークラスを実装した[サンプルスクリプト](https://github.com/tani-shi/unity-master-data/blob/master/Assets/UnityMasterDataDemo/Scripts/DemoScene.cs)です。
注意することとして、マスターデータを取得する処理を実装する箇所よりも先に、マスターデータアセットをロードする処理を実装した箇所が先に実行されなければなりません。

# 最後に

今回紹介させていただいたツールは私が完全に個人的な見解で、今まで経験したマスターデータの運用の中で最も効率的にゲーム開発ができる設計と感じたものをツール化したものになります。もちろんこれが最適解かどうかというとケースバイケースですが、個人で開発をしていてチーム開発のワークフローを経験したことがない方や、これからソーシャルゲーム開発を行っていく上で技術を模索しているような企業の方の何か参考になるようなものになれば幸いです。

また、今回は`Google SpreadSheet`について運用する上でデメリットばかり触れてしまいましたが、実はこういう使い方するとこっちの方が使いやすいよ！といったような意見があれば、ご指摘いただきたいです。

それでは最後まで閲覧いただき、ありがとうございました。