Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ゲーム案件にZenjectを導入した経験を語る

7d9c75df48ec1f8a0579ea32ef842473?s=47 kase
September 06, 2018
8.7k

 ゲーム案件にZenjectを導入した経験を語る

Unity Zenject完全に理解した
https://connpass.com/event/96928/

セッション枠:かせ
https://twitter.com/KaseliaePenguin

7d9c75df48ec1f8a0579ea32ef842473?s=128

kase

September 06, 2018
Tweet

Transcript

  1. ゲーム案件に Zenject を導入した経験を語る

  2. 自己紹介 名前 : かせ 職業 : Unity エンジニア Twitter :

    @KaseliaePenguin ゲーム開発案件を複数、今はUnity向けライブラリ開発のお仕事を メインでやってます。
  3. 少し書きました

  4. こんなのも作ってます

  5. アジェンダ ・Zenjectを導入した経緯 ・導入時の試行錯誤と結果 ・Zenject導入時のやるべきこと ・まとめ

  6. この話のターゲット ・Zenjectを一通り使ったことがあり、ステップアップしたい人 ・案件導入の実例を知り、導入するかどうかを検討したい人

  7. Zenjectを導入した経緯

  8. Zenjectを導入した経緯1 ゲーム開発を複数案件やった時 共通して 様々な問題を抱えていた

  9. Zenjectを導入した経緯2 開発・運営が進むごとに 増えるデータ 汚くなる 初期化フロー、依存関係の定義 各クラスに対しての 怒涛のInitialize祭り

  10. Zenjectを導入した経緯3 もういや!

  11. Zenjectを導入した経緯4 その時現れた救いの手が・・・ わしを使うのじゃ

  12. Zenjectを導入した経緯5 彼、Zenjectはとても魅力的に見えた。 彼は依存関係を解決するソリューションとしてトップクラスに有 能であった。 Unityに寄り添い、TestRunnerにも寄り添う彼の姿はまさに神話の 神ゼウスのように神々しく感じられたのだった。

  13. Zenjectを導入した経緯6 そんな中、完全新規の大型開発案件の話が出てきた。 とりあえず「やらせてくれ!!!」と直談判して導入した。 その案件で自分はアーキテクチャをメインにやっていた。 なので、導入は気軽?に決まった。

  14. 導入時の試行錯誤 シーン設計

  15. 導入時の試行錯誤・シーン設計1 目指したものは 本番とデバッグの差し替えが容易で、データの生成フローも同じ という状態のシーン設計だった。

  16. 導入時の試行錯誤・シーン設計2 ZenjectはSceneContextをシーン上に配置する必要がある。 SceneContextに関連する機能は数多くあるが、その中で Contextの親子付けを行う機能に注目した。

  17. 親子付けの属性設定項目

  18. 自身の属性名

  19. 親の属性名

  20. 導入時の試行錯誤・シーン設計3 マルチシーンでSceneContextが読み込まれる度に、親の属性名の SceneContextを検索して、Bindされているオブジェクトを子が使 用できるようになる マルチシーンでContextを1つずつ読み込み、シーンを差し替える ことでデバッグに対応できるようにすれば・・・

  21. 導入時の試行錯誤・シーン設計4 シーンを読み込んだ際に、他の親シーンが初期化されているかの バリデーションを行いつつSceneContextを初期化する AutoRunをfalseにしておき SceneContext.Run()を呼ぶタイミングを制御する

  22. None
  23. 導入時の試行錯誤・シーン設計5 Canvasの制御や、インプットの制御を行うクラスをBindした Systemシーン

  24. 導入時の試行錯誤・シーン設計6 サーバー側との通信や、データクラスをBindしたDataシーン

  25. 導入時の試行錯誤・シーン設計7 ゲームの背景やキャラクターなど、操作を”される”側になる オブジェクトをBindしたEnvシーン

  26. 導入時の試行錯誤・シーン設計8 ゲームの背景やキャラクター、UIなどを操作”する”ロジックク ラスをBindしたEnvシーン

  27. 導入時の試行錯誤・シーン設計8 各シーンの依存関係

  28. 導入時の試行錯誤・シーン設計9 シーンごとにContextの状態が紐づいているため、各シーンで Bindされているオブジェクトを変更することで容易に 全体に変更がかけられるようになった 子になるLogicシーンのみを読み替えることで、画面 に影響を出さずにシームレスに状態を切り替えることも副次的に できるようになった。

  29. 導入時の試行錯誤・シーン設計10 各シーンで必ず生成されるRootクラスをそれぞれのSceneContext にバインドさせておき そこからロジッククラスをnewすることでシーンごとのエントリー ポイントを作成した。

  30. 導入時の試行錯誤 クラス設計

  31. 導入時の試行錯誤・クラス設計1 設計思想は大枠としてMV(R)Pで開発を進めることにしていた。 後々CleanArchitectureの一部のレイヤーを導入した。

  32. Modelはデータを持ち Viewは入力を受け取り Presenterが繋いで処理するやつ 出典元 : https://www.quora.com/How-do-I- learn-MVP-architecture-for-Android- Development

  33. 導入時の試行錯誤・クラス設計2 クラスの依存関係を解決し、解決時の初期化も自動で行なってく れるので 依存解決に困っていたModelは全てinterfaceで区 切ってAsSingleでBindした。

  34. 閑話・Injectについて Bindされているオブジェクトを、使用するクラスに対してインス タンスとして注入する一連の動作をInjectを呼びます。

  35. 閑話・Bindの方式で変わるInjectの動作 ・AsSingle シングルトンとしてインスタンスを生成し、Inject 実質インスタンスを破棄できず、破棄したと見せかけて内部的に はデータを保持している

  36. 閑話・Bindの方式で変わるInjectの動作 ・AsCached インスタンスを生成してInject、生成されたインスタンスはキャッ シュされて破棄しない限り使い回される

  37. 閑話・Bindの方式で変わるInjectの動作 ・AsTransient 毎回新しく生成してInject、Bind時のデフォルト動作

  38. 閑話・Bindの方式で変わるInjectの動作 http://yutakaseda3216.hatenablog.com/entry/2017/04/21/153555 詳細はこちらにまとめてあります。

  39. 導入時の試行錯誤・クラス設計2(再) Zenjectを導入するにあたり、彼に何をしてもらうのかを考えた 彼はクラスの依存関係を解決し、解決時の初期化も自動で行なっ てくれた。 なので、依存解決に困っていたModelは全て AsSingleでBindした。

  40. 導入時の試行錯誤・クラス設計3 BindされているModelの参照を使用するのはPresenterなので 何も考えずに PresenterもAsSingleでBindした。

  41. 導入時の試行錯誤・クラス設計4 PresenterもAsSingleでBind・・・してしまった結果 同じPresenterを複数インスタンス生成することができなくなり、 取り回しがしづらくなった。 また他のクラスで使い回さない(Bindされている意味がない)ので PresenterはFactoryで生成するようにした。

  42. 導入時の試行錯誤・クラス設計5 Containerから生成されるクラスのメソッド呼び出し順は ・new() -> [Inject] 変数の[Inject]はnew()のタイミングで行われない new()は基本的に初期化に使わず基底クラスに[Inject]アトリビュー トをつけた初期化メソッドを用意した。

  43. 閑話・Factoryの記述の手間を減らす Factoryを使用する際はこういった形になることが多かったので

  44. 閑話・Factoryの記述の手間を減らす スクリプトテンプレートで作成しつつ、Editor拡張などから自動で InstallerにBindFactory記述を追加すると手間が減るので楽です。

  45. 導入時の試行錯誤 インゲームへの適用

  46. 導入時の試行錯誤・インゲームへの適用1 BindとInjectのシンプルな依存性解決が素晴らしく インゲームで動くキャラクターなどにもZenjectの恩恵を 与えようとした。

  47. 導入時の試行錯誤・インゲームへの適用2 地獄の始まり

  48. 導入時の試行錯誤・インゲームへの適用3 Presenterからインゲームで動くキャラクターなど全てをFactoryで 生成 必要な情報は各自Injectで引っ張ってくるようにした

  49. 導入時の試行錯誤・インゲームへの適用4 子供の各コンポーネントのみで必要な情報もInjectする状態に 親のクラスが参照置き場化、更にコンポーネントの参照解決のた めに引数大量に ある意味で健全だが、イテレーションの速さが求められる インゲームでは悪手だった

  50. 導入時の試行錯誤・インゲームへの適用5 インゲームは従来の依存解決の設計の方が良い場合もある 柔軟に設計を変えられるように、(当たり前だが)参照解決を行う部分 とインゲームのロジック部分は切り分けた方が良い Zenjectを使わなくなっても困らないようにしておく

  51. Zenject導入時にやるべきこと

  52. やるべきこと・適用範囲の選定1 Zenjectの依存性解決を行う部分をどこからどこまでにする のかを最初に決定し、それ以外の場所には適用をしない

  53. やるべきこと・適用範囲の選定2 適用する範囲の選定基準は ・他クラスへの依存性が高く、初期化順番が大きな影響を与える ・他のコンポーネントに多く参照される ・デバッグなどで差し替えを行いたい

  54. やるべきこと・適用範囲の選定3 適用しない範囲の選定基準は ・Bindするのが難しい細かなコンポーネントを扱う ・あまりZenjectへの理解がない人が触る

  55. やるべきこと・SceneContextへの理解 ZenjectはSceneContextに何をBindするのか?が全て データフローが全てZenjectを考慮したものに置き換わるので SceneContextを丁重に扱い、動作は必ず確認すること

  56. やるべきこと・interfaceへの切り分け インスタンスを差し替え可能、テスト可能にするため 必要なクラスに対しては必ずinterfaceを切り分ける また、interfaceをBind,Injectすることが大事

  57. やるべきこと・パフォーマンスを考える Zenjectの依存性解決はリフレクションを使用して行われている 依存解決には一定のコストがかかるため、タイトなパフォーマン スが要求される部分は参照解決の頻度を落とすことが必要

  58. まとめ

  59. 導入してよかったこと ・データのやりとりがスッキリ、コード量も減って楽になった ・デバッグ用の機能や新機能を付け足すのが楽 ・副次的にinterfaceを切り分けることが常になるので、テストフレ ンドリー

  60. 導入して悪かったこと ・設計者はZenjectへの完全理解が必要、Injectの仕組みは難しい ・依存性解決のための設計難易度が高く、適用範囲の取捨選択に 時間を要する

  61. 最後に Zenjectは開発中にも良い影響があるが、依存性解決の真価は運営 フェーズに移行した際に発揮されるソリューションだと”思う” (運営フェーズを経験してないのでわからないけど)

  62. ありがとうございました。