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

三種の神器とMVRPパターン / xrdnk-three-sacred-treasures-a...

Denik
April 20, 2021

三種の神器とMVRPパターン / xrdnk-three-sacred-treasures-and-mvrp-pattern

クローズドコミュニティ内で行った三種の神器(UniRx, UniTask, Extenject)とMVRPパターンの簡単なハンズオンです.(Bonus Track : VContainer)

Denik

April 20, 2021
Tweet

More Decks by Denik

Other Decks in Programming

Transcript

  1. アジェンダ ➢ 軽く自己紹介 ➢ 本ハンズオンの目的 ➢ UniRx (Unity Reactive Extension)

    and MV(R)P Pattern ➢ UniTask (an efficient allocation free async/await integration for Unity) ➢ Extenject (Zenject Dependency Injection IOC / ex: Zenject) and DI Pattern ➢ 終わりに ➢ Bonus Track : VContainer (extra fast Dependency Injection for Unity)
  2. 問題提起 1クラスにロジック処理・入出力処理・リソース処理…. こういうクラスは神クラス (God Classes) と呼ばれます. 実体は悪クラス (Evil Classes) です.

    Unity 初心者本は大方神クラスで説明を済ませることが多く,「密結合」になりがちです. 個人開発ならいいですが,実際のチーム開発では「密結合」である場合は困ることが多いです. ワシに任せろ かかったな アホが ! 神クラスに見えて 実際は悪クラスである
  3. What is UniRx Unity でReactive Extensions(Rx)を実現するためのライブラリ イベントや値の変化を自動で伝播させることができる 作者は neuecc さん.

    C# で言う event をストリーム化した概念のような感じ. Operator (LINQ) が使えるので 中身の加工も容易に出来て柔軟性がある 今回は「使い方」の簡単な説明しかしません. 詳しい機能の中身や概念を深堀したい場合は, 最後に載せる参考資料を読んでください.
  4. UniRxで出来ること(一部) ➢ Method Chain でイベントに対する処理をキレイに書ける ➢ Plain C# Class で

    Update などの MonoBehaviour ライフサイクル処理が実行できる ➢ Scheduler を利用すれば, Coroutine を使わなくても Timer 処理が可能 ➢ ReactiveProperty を利用すれば, 普段の変数感覚でイベントの通知が実装出来る ➢ uGUI のイベント処理が簡潔にキレイに書ける ➢ イベントに対する加工が出来る (Where, First, Select, SelectMany...)
  5. Twitter で覚える UniRx の概念 (4/8) 流れを整理します. ① アイドルのTwitterアカウントが存在する ② ファンがアイドルをフォローする

    ③ ファンはアイドルのフォロワーになる ④ アイドルがツイートする ⑤ フォロワーはTLにツイート内容が流れてくる この時,アイドルはフォロワーの存在がいる,いないにかかわらず, 「Tweet」というイベントを行うことが出来ますよね ? そして, フォロワーはその「Tweet」内容をTLで拾うことが出来,読むことが出来ます. (ここでアイドルが鍵垢ではないこと,アイドルにブロックされてないことを考慮しない) これを UniRx の用語で当てはめます(アナロジー)
  6. Twitter で覚える UniRx の概念 (5/8) 流れを整理します. ① アイドルのTwitterアカウントが存在する (Observable:観測可能) ②

    ファンがアイドルをフォローする (Subscribe:購読する) ③ ファンはアイドルのフォロワーになる (Observer:観測者) ④ アイドルがツイートする (OnNext:発行する・通知する) ⑤ フォロワーはTLにツイート内容が流れてくる (SubscribeしているのでOnNextを検知出来る)
  7. Twitter で覚える UniRx の概念 (6/8) Observer Observable Subscribe Subscribe おはよう

    ^^ みんな元気~!? Tweet OnNext Subscribe Tweet Message
  8. Subject Subject は IObservable と IObserver を実装するクラスです. 通知側で Subject を作成し,

    IObservable で公開します. 通知を行う際は, OnNext を行います. 購読側は IObservable を参照し, 通知が届いたら Subscribe で購読します. Subscribe は IObserver を引数に持つので, ここで購読時に行う処理を行います. 何言ってるんだコイツは…? となると思うので,例題を出します. 例題1 は私が実演します.例題2 ~ 4 は各自で実装を行ってもらいます.
  9. 問題2|問題1の責務範囲を分ける 1クラスに Observable と Observer がいることになるので, Observable と Observer を分けましょう.

    Observable でボタン発火のイベントを発行し, Observer でボタン発火の通知を購読し, Debug.Logで Hello World!を表示します.
  10. Reactive Property Reactive Property は Subject を 通常の変数感覚で行えるようにした変数のようなものです. // int型のReactiveProperty

    var rp = new ReactiveProperty<int>(10); //初期値を指定可能 // Value プロパティを通して,普通に代入したり、値を読み取ることができる rp.Value = 20; var currentValue = rp.Value; //20 // Subscribeもできる(Subscribe時に現在の値も発行される) rp.Subscribe(x => Debug.Log(x)); // 値を書き換えた時にOnNextが飛ぶ rp.Value = 30; quoted by https://qiita.com/toRisouP/items/86fea641982e6e16dac6
  11. 問題4|問題3 を MV(R)P パターンで実現する 問題 3 を MV(R)P パターンで実現しましょう.(復習の図) quoted

    by https://developers.cyberagent.co.jp/blog/archives/4262/ view. xxxxAsObservable() Subscribe (model.method) Subscribe (view.method) model. xxxxAsObservable()
  12. 解答4|問題3 を MV(R)P パターンで実現する Presenter Model と View の橋渡しをする 橋渡しの際に色々加工をしてもよい.

    (例えば遅延処理,ライフサイクル調整など) UniRx の Operator で 渡ってくる値のフィルタリングも行う.
  13. What is UniTask Unity C# 用にチューニングされた非同期処理用のライブラリ 最近 Unity C# は

    async/await Task 使えるようになったが,パフォーマンスに問題あり. UniTask を利用することで解決できる. quoted by https://speakerdeck.com/torisoup/unitask2020
  14. UniTask Example ➢ 名前空間 (UniTask 2 からの変更) using UniRx.Async; →

    using Cysharp.Threading.Tasks; private async UniTaskVoid Hoge() { // 1秒待つ await UniTask.Delay(TimeSpan.FromSeconds(1)); // 1フレーム待つ await UniTask.DelayFrame(1); // 1フレーム待ってUpdate()のタイミングまで待機 await UniTask.Yield(); var flag = true; // 条件がtrueになるまで待つ await UniTask.WaitUntil(() => flag); flag = false; }
  15. 例題2|UnityWebRequestのダウンロード進捗率の表示をMV(R)Pで実装 やること ➢ Presenter View ⇆ Model の橋渡し処理を行う ➢ Model

    ① 取得したいコンテンツのURLを設定する (今回は設定済) ② UnityWebRequest.Get(url) で GET する ③ 受信を待つ (await する) ④ SendWebRequest は AsyncOperation なので, ToUniTask() に変換できる ⑤ UniTaskの Progress.Create<float>(x => _progress.Value = x * 100) を利用して 現在のダウンロード進捗の値を取得する
  16. What is Extenject Unity C#用の Dependency Injection (依存性注入) ライブラリ. 必要な物を注入してくれるイメージ.

    (Dependency Injection Pattern, Service Locator Pattern については今回説明しません.) FYI : Service LocatorとDependency InjectionパターンとDI Container https://www.nuits.jp/entry/servicelocator-vs-dependencyinjection もともとは Zenject [Zen (禅の心) + ject (Inject) ] だったが, 色々事情(裁判中)があり Extenject と言ったほうがよい. 今回は最低限の部分の簡単な説明しかしません. 詳しい機能の中身や概念を深堀したい場合は, 最後に載せる参考資料を読んでください. (というより多機能すぎて未だに全て把握できていない)
  17. とりあえず簡単な利用方法を説明 Presenter は Model, View の橋渡し役なので,両方知る必要がある(依存している). 現時点では, Model, View を知るために,

    [SerializeField] アトリビュートを加えて, Inspector 上で設定している. この, Presenter に Model, View に参照を与える際に, [SerializeField]を用いずに, Extenject を用いる方法でやってみましょう. Extenject の専門用語があるので,まずは用語整理をします.
  18. Installer 次に MonoInstaller を作ります. MonoInstaller は MonoBehaviour を 継承した Installer

    です. よって, Hierarchy 上に実体があります. Projects ウィンドウで右クリック > Create > Zenject > MonoInstaller すると, MonoInstaller.cs を作成できます. InstallBindings() メソッド内で 注入(Inject) したいクラスを Bind します. MonoInstaller を Scene Context に 設定するのを忘れず!(忘れると動きません)
  19. Bind (MonoBehaviour 継承クラス) Fooクラスの依存性注入メソッド Container.Bind<Foo>().AsCashed(); IFoo インタフェースには Fooクラスを注入する Container.Bind<IFoo>().To<Foo>().AsCashed(); Fooインスタンスを注入する

    Container.BindInstance(_Foo).AsCashed(); Bind の仕方については沢山設定方法があるので,ドキュメントを読んでください() 以下,参考. 【Unity】【Zenject】Containerでバインドする際の設定まとめ https://light11.hatenadiary.com/entry/2019/02/22/005845 【Unity】【Zenject】いろんなものを依存性注入する https://light11.hatenadiary.com/entry/2019/02/20/234834
  20. Inject ➢ フィールドインジェクション (循環参照問題により避ける) [Inject] private Bar _bar; [SerializeField] と同じ感じだと思えばよい.

    ➢ プロパティインジェクション (循環参照問題により避ける) [Inject] public Hoge hoge {get; private set;} ➢ メソッドインジェクション (循環参照問題が起きてしまうが… MonoBehaviour クラスはこちらを利用) [Inject] private void Construct(Model model){} ➢ コンストラクタインジェクション (Plain C# クラスはこちらを利用する) [Inject] (一応コンストラクタインジェクションの場合は [Inject]を付けなくてよい) private Boo(Model model){}
  21. 事前準備 ハンズオンプロジェクトを開いてください. ➢ ハンズオン用のシーン ExtenjectExample/Scenes/00_Question ➢ ハンズオン用のスクリプト ExtenjectExample/Scripts/0X_QuestionX Answer フォルダもありますが,最初は見ないようにしましょう.

    自身で問題を解いて,合っているかどうかの確認のために利用しましょう. どうしても問題がわからない場合は,参照してもかまいません. SceneContext, Installer 設定は事前に済ませています.
  22. 例題2| Model の MonoBehaviour をなくそう Model は正直 MonoBehaviour を継承する必要がない. MonoBehaviour

    の継承をなくすことで, Hierarchy 上に実体が必要なくなる. Plain C# クラスの Bind をしてみよう.
  23. 閑話休題 Model に MonoBehaviour を継承すべきかどうかはプロジェクト次第ですが, ドメインロジックを担当するなら,個人的には継承しないのが責務範囲的に正しいと思います. ただ諸々の事情により,MonoBehaviour を継承してしまっても良い時もあります. ここは個人個人の経験と肌感覚によります. また,Presenter

    はModel と View の橋渡し役なので MonoBehaviour を最終的には継承する必要はありません. ただ今回では MonoBehaviour のライフサイクルに利用される Awake(),Start() メソッドを利用しているため,継承している形です. Extenject には IInitializable のインタフェースがあるので, Presenter に MonoBehaviour を継承させずに, Initializable.Initialize() で実装するのが美しいです. (今回は時間がないので,PresenterもMonoBehaviourを継承させた形で進めます.)
  24. 脳死でMonoBehaviourを継承していませんか? MonoBehaviour を継承しているクラスは,以下の特徴があります. 1. Hierarchy上に実体が必要. 2. MonoBehaviour ライフサイクルを利用することができる. Awake(),Start(),Update() など.

    3. コンストラクタメソッドを作ることができません.なので,new Bar() ができない. MonoBehaviour はコンストラクタメソッドを利用できないので, メソッドインジェクションで疑似的に Inject させる運用になります. Extenject (VContainerも) を利用すれば, MonoBehaviour 継承不要なクラスも 疑似的に Awake(),Start(),Update() のような処理が出来るので,便利です.
  25. Extenject (VContainer) を利用することの個人的な利点 Extenject (VContainerも) を利用すれば, MonoBehaviour 継承不要なクラスも 疑似的に Awake(),Start(),Update()

    のような処理が出来るので,便利です. MonoBehaviour の特徴に合わせて照らし合わせます. ◆ Hierarchy上に実体が必要. → Hierarchy上に実体が不要.よって,いちいち空オブジェクトを生成して, スクリプトをアタッチする手間が省けるし,抜け漏れを防ぐことができる. ◆ MonoBehaviour ライフサイクルを利用することができる. Awake(),Start(),Update() など. → Zenject.IInitializable.Initialize(), Zenject.ITickable.Tick() を利用すればいい. ◆ コンストラクタメソッドを作ることができません.なので,new Bar() ができない. → コンストラクタが利用できるので,コンストラクタインジェクションが出来る. コンストラクタインジェクションの場合,明示的に [Inject] を書く必要がない.
  26. Pros (Personal View) ➢ チーム開発で大いに役に立つ ➢ 設計が疎結合になりやすい ➢ コードの可読性が高い ➢

    採用例が多い (MESON Inc. は採用条件に Extenject の知識が必要とも書かれている) quoted by https://www.wantedly.com/projects/568250
  27. Cons (Personal View) ➢ 学習コストが高い(ただ身に着ければ,その分のリターンは大きい) ➢ フィーリングで UniRx を使ってしまうとリアクティブスパゲッティになりがち ➢

    短期開発のような拡張性を考える必要のない場面では冗長すぎる ➢ 機能が多すぎてどの道具を利用した方がよいかわからなくなる ➢ 三種の神器なしのコードが書けなくなるし,読むスピードも遅くなる チーム開発で UniRx, UniTask, Extenject を利用する場合は チームメンバーのレベル感, 開発納期, 今後の拡張性等を考慮して使うこと バリバリ使う必要はないし,最初は最低限の箇所から利用していくのがよい
  28. Extenject と VContainer の比較 (1/2) ➢ Code のサイズ quoted by

    https://vcontainer.hadashikick.jp/comparing/comparing-to-zenject
  29. 事前準備 ハンズオンプロジェクトを開いてください. ➢ ハンズオン用のシーン VContainerExample/Scenes/00_Question ➢ ハンズオン用のスクリプト VContainerExample/Scripts/0X_QuestionX Answer フォルダもありますが,最初は見ないようにしましょう.

    自身で問題を解いて,合っているかどうかの確認のために利用しましょう. どうしても問題がわからない場合は,参照してもかまいません. LifetimeScope 設定は事前に済ませています.
  30. 例題2| Extenject 例題 2 を VContainer にする Extenject の Question

    2 のシーンをコピーして, Extenject から VContainer のスクリプトの変更しましょう.
  31. 今回のクラス図(参考)|概説 Entity (Scriptable Object) マスターデータ Repository CRUD Model ドメインロジック View

    UIビジネスロジック Presenter 仲介者 Domain Layer Presentation Layer 例えばQuest版,HoloLens2版, Mobile版 のUIを変える場合は Concreate View さえ差し替えればよい Entity, Repository について今回説明せず 他にも Infrastructure Layer があるが,今回は説明せず. たとえば Multiplayer によるネットワーク接続や Database による サーバデータベース接続 など
  32. 参考資料 ➢ MV(R)P パターン ➢ Web出身のUnityエンジニアによる大規模ゲームの基盤設計 https://developers.cyberagent.co.jp/blog/archives/4262/ ➢ UniRxでMV(R)Pパターンをやってみた https://www.slideshare.net/torisoup/unirxmvrp

    ➢ UniRx ➢ UniRx入門シリーズ (qiita) https://qiita.com/toRisouP/items/00b8a5bb8e7b68e0686c ➢ UniRx/UniTask完全理解 より高度なUnity C#プログラミング (書籍) https://www.amazon.co.jp/dp/B08MNQT86F ➢ UniTask ➢ UniTask2の使い方 https://speakerdeck.com/torisoup/unitask2020 ➢ UniRx/UniTask完全理解 より高度なUnity C#プログラミング (書籍) https://www.amazon.co.jp/dp/B08MNQT86F UniRx, UniTask は neuecc さん, torisoup さんのブログ,qiita,書籍を読めば網羅できる
  33. 参考資料 ➢ Extenject (Zenject) と DI Pattern ➢ Zenject入門その1 疎結合とDI

    Container https://qiita.com/toRisouP/items/b3d3c43db40857ca4ad4 ➢ ZenjectチョットワカルBook https://booth.pm/ja/items/1520608 ➢ 【Unity】依存性注入とは?と、なっているのでZenject(Extenject)を入門してみた https://qiita.com/4_mio_11/items/4306732bc47780802b74 ➢ VContainer ➢ Unity専用最速DIコンテナVContainer と、UnityにおけるDIの勘所 https://learning.unity3d.jp/6297/ ➢ 【Unity】DIコンテナVContainerの使い方まとめ https://light11.hatenadiary.com/entry/2021/02/01/203252 ➢ VContainer入門 – MVPパターンを組んでみる https://gaprot.jp/2021/02/18/vcontainer/ ➢ その他 ➢ Unityにおける設計パターン (必読) https://speakerdeck.com/torisoup/unityniokerushe-ji-patan