現在グレンジでUnityを用いたゲーム開発を行っているみくりやと申します。
グレンジ Advent Calendar 2017 3日目の記事になります。
Unityについてのホットな話題を探していたところ Zenject にたどり着き、面白そうだったので
少し触ってみようと思います。
→*Zenject Dependency Injection IOC* (AssetStore)
#Zenjectとは
一言で言うと「 Unityを対象としたDIフレームワーク 」となります。そして無料(MITライセンス)。
ポケモンGOでも採用されているようです。
詳しい説明はgithubのREADMEを読めばわかりやすいかもしれません。
→*github*
#DIとは
Dependency Injection。しばしば日本語で「 依存性の注入 」と訳されます。
正直DIという単語自体「入社直後の研修でやったな」くらいの記憶しかなく、依存性の注入というワードのわかりにくさもあいまってあっちいけと敬遠したくなります。
改めて調べてみましたが、簡単に言うと「 部品同士を疎結合化し依存性を無くす 」ということみたいです。疎結合化って言われると途端にわかりやすく感じます。
その結果メンテしやすくなったり再利用しやすくなったりテストしやすくなったりするようですね。
なんだか良いやつっぽいです。君とは仲良くなれそうだ。
#触ってみる
##簡単な例
まずDIパターンでない場合。
public class ClassA : MonoBehaviour {
private ClassB _classB;
void Start () {
_classB = new ClassB ();
_classB.CreateLog ();
}
}
public class ClassB : MonoBehaviour {
public void CreateLog () {
Debug.Log ("hoge");
}
}
このときClassAはClassBをnewしなければ動作できないのでClassAはClassBに依存している状態となります。
この例をZenjectを用いて疎結合化します。
SceneContextのInstallerプロパティにInstallerを追加
Installerスクリプトには以下を記述します。
public class TestInstaller : MonoInstaller<TestInstaller>
{
public override void InstallBindings() {
Container.Bind<ClassA>().AsSingle();
}
}
ClassAを以下のように修正します。
public class ClassA : MonoBehaviour {
[Inject]
private ClassB _classB;
void Start () {
_classB.CreateLog ();
}
}
ClassBオブジェクトにZenjectBindingをAddComponentしComponentsプロパティにClassBを追加します
するとZenjectがごにょごにょしてくれてClassBをnewしなくてもログを吐き出すことができるようになります。
##Singletonで書いてたところに使ってみる
以下のようなシングルトンのGameManagerを
public class GameManager : Singleton<GameManager> {
public int hoge0;
public int hoge1;
public int hoge2;
}
単なるMonoBehaviour継承クラスにしてしまい
public class GameManager : MonoBehaviour {
public int hoge0;
public int hoge1;
public int hoge2;
}
ZenjectBindingをAddComponentします。
先ほど作成したClassAを改変します。
public class ClassA : MonoBehaviour {
[Inject]
private ClassB _classB;
[Inject]
private GameManager _manager;
void Start () {
_classB.CreateLog ();
Debug.Log (_manager.hoge0 + " " + _manager.hoge1 + " " + _manager.hoge2);
}
}
##Installerをprefab化してみる
InstallerのprefabをSceneContextに追加できるのでシーン上に配置しなくても使用することができ、シーンをキレイにすることができます。
PrefabInstallersに追加するだけ。
##ScriptableObjectで使ってみる
ScriptableObjectを扱うことも可能です。
SciptableObjectInstallerが生成されます。
SciptableObjectInstallerを編集
[CreateAssetMenu(fileName = "SettingInstaller", menuName = "Installers/SettingInstaller")]
public class SettingInstaller : ScriptableObjectInstaller<SettingInstaller> {
public Setting setting;
public override void InstallBindings () {
Container.BindInstances(setting);
}
}
[Serializable]
public class Setting {
public int hoge3;
}
ProjectのメニューにInstallers/SettingInstallerが増えているのでそこからScriptableObjectをAssetsファイルとして出力
ClassAを改変します。
public class ClassA : MonoBehaviour {
[Inject]
private ClassB _classB;
[Inject]
private GameManager _manager;
[Inject]
private Setting _setting;
void Start () {
_classB.CreateLog ();
Debug.Log (_manager.hoge0 + " " + _manager.hoge1 + " " + _manager.hoge2);
Debug.Log (_setting.hoge3);
}
}
#まとめ
Unityでルールを決めず自由にコードを書いてしまうとしっちゃかめっちゃか
してしまいがちなので今回とりあげたZenjectのようなフレームワークは闇を生まないためにも有効な手段だと感じました。Zenjectを使うとかなりシンプルにまとめあげることができそうな気がします。
まだまだ簡単なことしかできていないので引き続きZenjectを触ってDIの真理へと近づいて行きたいと思います。