Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

自己紹介 名前 : かせ 職業 : Unity エンジニア Twitter : @KaseliaePenguin ゲーム開発案件を複数、今はUnity向けライブラリ開発のお仕事を メインでやってます。

Slide 3

Slide 3 text

少し書きました

Slide 4

Slide 4 text

こんなのも作ってます

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

Zenjectを導入した経緯

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

親子付けの属性設定項目

Slide 18

Slide 18 text

自身の属性名

Slide 19

Slide 19 text

親の属性名

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

Zenject導入時にやるべきこと

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

まとめ

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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