2018年9月6日に「Unity Zenject完全に理解した」というイベントがあったので参加してきました。
全体的に高度なので、Zenjectとはなんぞや?というような方は、4、7、おまけ辺りを先に見るのをオススメします。
目次
ライブ配信
ゲーム案件にZenject導入した経験を語る @KaseliaePenguin
導入した経緯
データが増えてく、初期化フローや依存関係が汚くなる、initialize祭り
→zenjectで解決
シーン設計
本番とデバッグの差し替えを容易にしたい
SceneContextの親子付けを活用
→シーンの読み込み時、親となるSceneContextを自動で取得するようにする
→シーンを機能で切り分けておけばデバッグ時の差し替えが楽にできる
クラス設計
MVRP+CleanArcitecture(一部)
modelはinterfaceで区切ってAsSingleでBind
Bindの方式
AsSingle…シングルトン、破棄できない
AsCached…キャッシュされる、破棄されるまで使い回される
AsTransient…毎回新規作成、デフォルト
詳細はブログにて
(AsSingleだったので)取り回しがつらくなった
Containerから生成されるクラスのメソッドはnew()→[Inject]で行われる
→すごい罠、注意
インゲームへの適用
ゲーム内キャラには不敵だった
→イテレーションが遅い、Injectする情報が多い
適用基準
する→よく参照されるもの、デバッグで差し替えたいもの
しない→細かいもの、Zenjectへの理解が浅い人が触るもの
使い所を見極めるのは大変、ハイリスクハイリターン
zenjectを使わなくなっても困らないようにする(≒zenjectに依存しない)
文脈を操る美しきZenjectプロジェクトからの眺め 〜Contextの扱い方と活用方法〜 @mikito0521
contextとは
これを理解できたらzenject理解できる
依存関係を解決してくれるいいやつ
使いこなそうとするとわけわからん
context=文脈
iOSで/Androidで/UnitEditorで
本番サーバで/開発サーバで/モックデータで
キャラ強化シーンを/バトルシーンを
通常プレイ/テストプレイ
→contextは条件や状態を明示的に分割したもの
分割しているので
本番環境とテスト環境を差し替えたりできる=contextのスイッチ
Contextの種類
Project Context…Project全体で利用するものに使う
Scene Context…基本、シーンに1つ
Scene Decorator Context…Scene Contextの機能を追加変更する
Game Object Context…GameObject1つにつき1つ
Context親子関係
親を持てる
親から子へはアクセスできない
Project Context
└Scene Context─Scene Decorator Context
└Game Object Context
Contextと動的生成
動的生成かつDI Containerを利用するものはFactory
Scene Context…シーンのロード、アンロードで制御
Game Object Context…プレファブ化しておいてFactory
FromSubContainerResolve…指定したコンテナの窓口となる
Contextの構築
Contextに対し、Installerを差し替えることで状態を変化
Interface設計がしっかりしてると扱いやすい
実装例(スライドより転載)
インタフェース完全に理解した @toRisouP
クラスの継承
属性を引き継ぐ
元のルールは変えられない
インタフェース
クラスと比較して自由
利用者がいる(定義する)
インタフェースの利点
疎結合にできる=依存関係を整理できる
利用側、実装側を同時に作れる
依存の解決
- ServiceLocator
- シングルトンに問い合わせる
- 簡単だが依存がきつい
- DI Container
- zenject
- ハイリスクハイリターン
小ネタ
プロパティ…getオンリーがよい
getcomponent…実はインターフェイスを指定できる
拡張メソッドを追加できる
structにインターフェースを被せられる
R#やRiderだと勝手に書いてくれる
- 明示的な実装が可能
- 暗黙的な実装→アクセスレベルはpublic
- 明示的な実装→インタフェース経由じゃないと呼べない
Zenject完全には理解できなかった〜実運用におけるアンチパターンとその回避策〜 @notargs
公開資料:Zenject完全には理解できなかった
ZenAutoInjector
同じGameObjectのComponentに自動でInject(的なこと)をしてくれる
MethodInjection
[inject]に依存しすぎないようにする機能
MonoBehaviourのInject
動的に生成されるObjectをSceneContextでInjectしてはならない
Zenjectの機能をご紹介 〜テストの巻〜@nozomin770
資料は後日公開予定
ZenjectUnitTestFixture/ZenjectIntegrationTestFixture
テストができる機能
SceneTestFixture
DIを上書きできるテスト
自動テストができると強力
Zenjectはじめの一歩:ゲームデータをScriptableObjectでInject! @lucifuges
公開資料:Zenjectはじめの一歩
設計が間違ってるとzenjectは使いこなせない
ただ使い所が無いわけではない
使い所
Scriptable objectでアイテムやキャラのステータスを持つ
→GameObjectに手動で参照つける必要がある→めんどい
必要なもの
Scriptable Object Installer…Scriptable Objectの代わりに継承する
Project Context…Resourcesフォルダ内に作り、↑のScriptable Objectを登録する
Scene Context…zenjectを動かすやつ。シーンに1つ置いておく
呼ぶ側…[SerializeField]を[Zenject.Inject]に変更
注意
Injectはそこそこ重い
→大量に複製されるObjectは避ける(弾とか)
ScriptableObjectをロードした時に全部先読みするので、重いリソースは避ける
→画像モリモリとかにしない(アセットバンドルに逃がしたりする)
おまけ
- Unity開発で使える設計の話+Zenjectの紹介
- Zenject入門その1 疎結合とDI
- 日本ANDROIDの会 UNITY部 UNIBOOK10 情報
- Zenjectでのテスト
まとめ
Zenjectは銀の弾丸ではありません。おおよそどの登壇者も「ハイリスクハイリターン」というようなことを言っていました。
個人、小規模、売り切りタイプではほとんど恩恵は無く、逆に大規模、大人数、運営系のゲームで、企画立ち上げ時に検討した場合に真価を発揮しそうです。プロジェクトの設計をいかにしっかり出来るか、Zenject導入の効果的な範囲を見極められるか、そのあたりがキモではないかと。
最近のゲームは長期運営が前提になることが多く、自然と求められる設計は「変更やテストに強い」ものとなります。それを満たすための設計をしていく上で発生する問題を解決するのがzenjectということです。(この辺りはおまけの「Zenject入門」に詳しく書いてあります)
なんとなくですが、どういう状況で必要になるか、どういう前提能力が必要になるかは分かってきたかなと思います。
まずはC#のInterface、UnityのScriptableObject辺りから習得していくことにします…