【Unity】Model-View-(Reactive)Presenterパターンとは何なのか

# はじめに

今回はUnityにおける「`Model-View-(Reactive)Presenter`パターン」とは何なのかについて解説します。

## 対象読者

* Unity開発者
* UniRxを使うことができる
* UnityにおけるGUI周りの実装に困っている

# GUI周りの設計パターン

`Model-View-(Reactive)Presenter`パターン（略してMV(R)Pパターン）とは、Unityにおける**GUI周りの設計パターン**の一種です。
「GUI」とはいわゆる「ユーザインターフェース」のことで、ゲーム中における「画面上に表示される情報」や「メニュー」や「ボタン」といったものを指します。
（ざっくりいえば、`uGUI`のことだと思って下さい）

GUI周りの実装手法というものはUnityに限らず、複雑になりがちな難しい部分です。
そのためいろいろな設計パターンが考案されてきました。
代表的なもので言えば`MVC`や`MVVM`などがあげられます。

その中でも`MVP`パターンというものがあり、`Model-View-(Reactive)Presenter`パターンはこれをもとにした設計パターンです。

## なぜ設計パターンが必要なのか

GUI周りの実装時にこのような複数の設計パターンがなぜ登場するかというと、**GUI周りをキレイに実装することがムズカシイから**です。
GUI周りはその用途から、非常に複雑になりがちです。

* 画面上のオブジェクトに表示するデータは、その裏側で相互に連動している
* 人間に対して**リアルタイムに**情報を表示する必要がある
* 人間からGUI経由で**データ処理に干渉される**ことがある

![GUI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47146/9391c078-813c-978a-dc44-1f88437d7450.jpeg)


GUI周りは、ただでさえデータ構造が複雑になりがちな上に、リアルタイム性、インタラクション性が必要とされる場所なのです。
そのため適当に実装してしまうと「相互参照」「循環参照」「ビジーウェイト」「イベントの無限ループ」などが発生する恐れが非常に高い場所なのです。

このように複雑になりやすいGUI周りを、「責務を分離したオブジェクト同士の相互作用で構築すれば多少はマシにできるのではないか」という発想のもとで提唱されてきたものが`MVC`や`MVVM`や`MVP`といった設計パターンなのです。

#### 「やばい」実装例

設計パターンを用いずに、愚直に`uGUI`を使うとこうなりますよという例です。

雑に作ると、次のような「ゲーム中で用いる重要な数値」と「`uGUI`にアクセスする部分」が同じクラスの中にそのまま書かれてしまったり。

```cs
using UnityEngine;
using UnityEngine.UI;

namespace Yabai
{
    /// <summary>
    /// UIとデータとごっちゃになったクラス
    /// </summary>
    public class Data : MonoBehaviour
    {
        [SerializeField] private Slider _slider;

        // 外から読み書きされたりする
        public float CurrentValue { get; set; }

        private void Update()
        {
            // Updateで無駄に値のチェックを毎フレームやってて最悪
            if (_slider.value != CurrentValue)
            {
                _slider.value = CurrentValue;
            }
        }

        // コード上では参照無いが、Unity上から呼び出されるpublicメソッド
        public void OnSliderValueChanged()
        {
            CurrentValue = _slider.value;
        }
    }
}
```

![Yabai.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47146/d537f4f2-0b5d-7318-713e-2564abb158ba.jpeg)


Unity上でオブジェクトの相互参照が発生して、どちらかが欠けるとエラーが発生してゲームが動かなくなったりしています。

こういう状況になるのを避ける目的として、Unityでよく使われるのが`Model-View-(Reactive)Presenter`パターンです。

では`Model-View-(Reactive)Presenter`パターンを話す前に、これのベースとなっている「`Model-View-Presenter`パターン」を先に解説します。

## `Model-View-Presenter`パターン

`Model-View-Presenter`（MVP）パターンとは、GUIの設計パターンのうち「`Presenter`」という概念を用いたものです。

GUI周りの構成要素を次の3つに分けて考えます。

* Model - データの実体。GUIとは直接関係ないアプリケーション本体の要素部分。
* View - GUIを制御する部分。データを画面に表示したり、逆にユーザからの操作を受け付ける部分。
* Presenter - ModelとViewをつなげる存在。仲介役。

```plantuml
@startuml

object View

object Presenter

object Model


Presenter --r> View
Presenter --l> Model

note top of Model: データの実体
note top of View: GUI周りの制御
note top of Presenter: 両者の仲介
@enduml
```

MVPパターンで重要な点は1つです。
「**Presenterが存在しなければ、ViewとModelは完全に独立した状態になる**」という点です。

`View`と`Model`をつなげる存在は`Presenter`のみであるため、`Presenter`を排除するとこの2つは完全に独立することになります。
つまり「**ViewはModelを完全に知らない**」「**ModelはViewを完全に知らない**」ということになります。

```plantuml
@startuml

object View

object Model

note top of Model: Viewのことは一切知らない
note top of View: Modelのことは一切知らない

@enduml
```

さて、このMVPパターンですが、実際にコードとして実装しようとすると問題がでてきます。

それは「ModelとViewをリアルタイムに連動させるにはどうしたらいいか」です。
「Modelの変化をViewにすぐに反映する」「ユーザからのView入力をModelへ即座に伝える」という、**リアルタイムな動作**を何らかの方法を用いて実現する必要があるわけです。

このように「リアルタイムな動作をするモノ」を別途用意する必要があるのがMVPパターンの欠点でした。

## MVPからMV(R)Pへ

「リアルタイムな動作をするモノ」の用意が必須だった`Model-View-Presenter`パターンに、UniRxを加えることでその欠点を補ったものが「`Model-View-(Reactive)Presenter`パターン」です。
UniRxのもつリアクティブなオブジェクト、とくに`ReactiveProperty`の利便性に着目しMVPパターンへと応用したものが「`MV(R)P`パターン」です。


# Model-View-(Reactive)Presenterパターン解説

改めて「`Model-View-(Reactive)Presenter`パターン（`MV(R)P`パターン）」の解説をします。

## 概要

`Model-View-(Reactive)Presenter`パターンとは、**MVPパターンとUniRxを併用したUnityにおけるGUI周りの実装手法**です。
GUIの構成要素を`Model`、`View`、`Presenter`の3つに分解し、それらをUniRx（の`ReactiveProperty`）を用いて連携させる手法となっています。

![MVPPattern.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47146/c7b5f272-e690-cf2f-97e5-6928acb81f9f.jpeg)

(`Model`の変化を`Presenter`経由で`View`に反映。`View`の変化を`Presenter`経由で`Model`に反映。その橋渡しにUniRxを使う。)

## `MV(R)P`パターンが言っていること/言ってないこと

こういう設計パターンを用いる際に気をつけるべきこととして、「この設計パターンは何を語っているのか」です。
たまに深読して、**本来の意味とは全く違うことを豪語する人**が居たりするので注意が必要です。

`MV(R)P`パターンが言っていること、言ってないことを次にまとめたのでこれらを念頭に入れた上で読んで下さい。

---

### `MV(R)P`パターンが言っていること

* GUI(とくに`uGUI`)周りの実装パターンである
* `View`と`Model`を「`Presenter`」という薄いレイヤでつなごう
* 各オブジェクトの連結には`Observable`(`ReactiveProperty`)を活用しよう

### `MV(R)P`パターンが**言ってないこと**

* Unityの`GameObject`などはすべてViewである（←言ってない。`Model`が`GameObject`を使ってても別にいい）
* `MV(R)P`パターンはUnity依存レイヤと非Unity依存レイヤを分離するものである（←言ってない）
* `MV(R)P`パターンを使えばゲームを何でもキレイに実装できる（←言ってない。）
* インゲームに`MV(R)P`パターンを使えば設計が上手にできる（←言ってない。クリーンアーキテクチャとかと話が混ざってない？）

---

`MV(R)P`パターンは「GUI周りをこう作るといいよ」というシンプルなことしか語っていません。
それ以上のことは「深読みのしすぎ」であり、**MV(R)Pパターンに過度の期待を持ちすぎ**です。


## 各種オブジェクトの役割

`MV(R)P`パターンには次の３つのオブジェクトが登場します。

* `Model`
* `View`
* `Presenter`

抑えておくとよいポイントとして、これらのオブジェクトは**実装者の視点によって相対的に決定するもの**だということです。
つまり「これから`Model`を作るぞ！」という役割を決めてからオブジェクトを作るのではなく、「このオブジェクトはこの`GUI`にとっては`Model`として振る舞うな」と決める形が正しいです。

要するに**何がModelで何がViewなのかなどは厳密な定義があるのではなく、システム全体の流れをみて自分で決めてよい**ということです。


### Modelとは

 * GUIに対して表示するデータの実体を持つ部分

`MV(R)P`パターンにおける`Model`とは、「データの実体をもつ部分」を指します。
言い換えると「**GUIに表示するデータの実体をもつ**」部分です。
アクションゲームを例に挙げると、「プレイヤの座標」「プレイヤの体力」「敵の体力」「現在のステージ情報」「残り制限時間」などが挙げられます。
もう少し踏み込んだ表現をするならば、**`Model`は「ゲームの構成に必要不可欠な情報（ドメイン）」を扱っているオブジェクト**です。

そして`MV(R)P`パターンにおける`Model`で重要なのは、**`ReactiveProperty`を用いて`Presenter`からのアクセスを可能にしている**という点です。
`ReactiveProperty`を公開することで、`Model`の内部状態が変化したときにそれが`Observable`として外部に通知できるようになっています。

#### Modelの実装方法


あるオブジェクトを、`MV(R)P`パターンの`Model`として扱うために必要な手順は次です。

* 状態を`IObservable<T>`または`ReactiveProperty<T>`として公開する
* （必要ならば）`Model`の内部状態を書き換えるプロパティやメソッドを用意する

要するに**UniRxを使って状態を公開する**だけです。

たとえば、次の`Player`は「体力」を`ReactiveProperty`として公開しています。
（また同時に、`Enemy`に衝突したら体力が減る）

```cs
using UniRx;
using UnityEngine;

namespace MVRP.Models
{
    /// <summary>
    /// アクションゲームにおけるプレイヤー
    /// 「画面に体力を出す」という視点からみるとModel
    /// </summary>
    public sealed class Player : MonoBehaviour
    {
        /// <summary>
        /// 体力
        /// ReactivePropertyとして外部に状態をReadOnlyで公開
        /// </summary>
        public IReadOnlyReactiveProperty<int> Health => _health;

        private readonly IntReactiveProperty _health = new IntReactiveProperty(100);


        /// <summary>
        /// 衝突イベント
        /// </summary>
        private void OnCollisionEnter(Collision collision)
        {
            // Enemyに触れたら体力を減らす
            if (collision.gameObject.TryGetComponent<Enemy>(out var _))
            {
                _health.Value -= 10;
            }
        }

        private void OnDestroy()
        {
            _health.Dispose();
        }
    }
}
```

なお、生の`ReactiveProperty`を`public`として公開し値の`Read/Write`を直接可能にするか、`Read`専用に`IReadonlyReactiveProperty<T>`のみを公開し`Write`は別メソッドを経由させるかはどっちでも構いません。
このあたりはチームの設計ルールや個人の好みなどで決めて大丈夫です。


### Viewとは

`MV(R)P`パターンにおける`View`とは、「ユーザ（人間）に情報を提示したり、ユーザ入力を受け付ける部分」を指します。
もっと簡単にいえば、**uGUIを使ってる部分**だと思って下さい（もちろん`uGUI`以外を使ってもOKです）

シンプルに、`uGUI`の「Text」「Button」「Slider」コンポーネントをそのまま`View`として扱うこともあります。
また`Tween`を追加するなどUI要素がアニメーションする場合は、そのアニメーションを管理するコンポーネントも`View`とみなして扱ってもOKです。

#### Viewの実装方法


あるオブジェクトを`View`として扱えるようにするために必要なことは、「`Presenter`から参照可能にする」だけでOKです。

`uGUI`のコンポーネントを直接`View`とみなすのであれば、`Presenter`から`uGUI`コンポーネントを直接参照させるだけです。
何か自前のコンポーネントを`Presenter`に参照させるのであれば、`Presenter`から見える位置にそのコンポーネントを配置する必要があります。

たとえば次のコンポーネントは、`uGUI`の`Slider`を制御するコンポーネントです。
このコンポーネントは紛れもなく`View`の要素であり、`Presenter`から参照しても問題ありません。

```cs
using DG.Tweening;
using UnityEngine;
using UnityEngine.UI;

namespace MVRP.Views
{
    /// <summary>
    /// uGUIのSliderをアニメーションさせるコンポーネント（View）
    /// </summary>
    public sealed class AnimationSlider : MonoBehaviour
    {
        [SerializeField] private Slider _slider;

        public void SetValue(float value)
        {
            // アニメーションしながらSliderを動かす
            DOTween.To(() => _slider.value, 
                n => _slider.value = n,
                value, 
                duration: 1.0f);
        }
    }
}
```

（スライダーをアニメーションしながら変化させる）

### Presenterとは

`MV(R)P`パターンにおける`Presenter`とは、`Model`と`View`の橋渡しをするオブジェクトです。

* `Model` -> `View`： `Model`の内部状態の変化を`ReactiveProperty`や`Observable`を通じて検知し、それを`View`に反映する
* `View` -> `Model`： `View`の状態変化を検知して、それを`Model`に反映する
* `View`と`Model`の間で必要なデータの変換（`string`->`int`などの型変換や、値の表現範囲の調整をしたり）

といった責務を持ちます。
（場合によっては`Model`->`View`の一方通行で終わる場合も多々あります）

**`Presenter`はデータ変換程度のロジックしか持たない、非常に薄いレイヤにするべき**です。
`Presenter`にはゲームロジックやViewの管理ロジックをもたせるべきではなく、基本的にデータの受け渡しと簡単なデータ変換のみを行うべきです。


#### Presenterの実装方法


`Presenter`は紐付けたい`Model`と`View`の両方を参照する必要があります。
そのためこれら2つが参照できるレイヤやモジュールに定義し、それぞれにアクセスさせます。

実装としては`Model`の`Observable`や`ReactiveProperty`を`Subscribe()`し、その状態変化を`View`に伝える。
`View`の状態変化を同様に`Model`に伝える。
このような処理を実装するだけです。


次の実装は、さきほどの`Player`と`AnimationSlider`の両者を結ぶ`Presenter`です。

```cs
using MVRP.Models;
using MVRP.Views;
using UniRx;
using UnityEngine;

namespace MVRP.Presenters
{
    /// <summary>
    /// Playerの体力をViewに反映するPresenter
    /// </summary>
    public sealed class PlayerHealthPresenter : MonoBehaviour
    {
        // Model
        [SerializeField] private Player _player;

        // View
        [SerializeField] private AnimationSlider _animationSlider;

        private void Start()
        {
            // PlayerのHealthを監視
            _player.Health
                .Subscribe(x =>
                {
                    // Viewに反映
                    _animationSlider.SetValue((float)x / _player.MaxHealth);
                }).AddTo(this);
        }
    }
}
```

`Presenter`を上手く作るコツとしては、「`Presenter`に複雑なロジックを書かない」「`Presenter`に状態を持たせない」です。

`Presenter`はあくまで`Model`と`View`をつなぐだけの存在であり、**Presenterの有無がゲームの動作に影響してはいけません。**
（`Presenter`を配置しないと`Model`の挙動が変わったり、エラーを出して動作しないといった作りはよくないです）

「`Presenter`と`View`は最悪未実装でも、`Model`さえあればゲーム自体はエラーを出さずに動作する（それで正しく遊べるとは言ってない）」と思って下さい。


### サンプルの動作例

先程あげた`Player`と`AnimationSlider`の組み合わせですが、実際に動作させるとこのようになります。

![MVRP_Sample.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47146/2f195aad-f8b9-6c70-7d19-df5b8f00fa78.gif)
（`Player`（カプセル）に`Enemy`（箱）が衝突するたびに体力が減り、それがSliderに反映される）

#### サンプルのシーン構成

![PresenterUnity.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47146/ad0dd12e-abde-872a-6399-269dcc7f3825.png)

`Presenter`という`GameObject`に、`PlayerHealthPresenter`がアタッチされています。
これがUnityのヒエラルキーウィンドウ上で、`Player`と`AnimationSlider`を直接参照しています。



### 別例：Model->View / View->Model の相互のやり取りがある例

先程の例では`Model`->`View`の一方通行でした。
こちらは`View`->`Model`の経路もあるパターンです。

![MVRP_Slider.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47146/6d9b983d-a759-dbe2-502c-d49d167f72ba.gif)

（スライダの操作がModelに反映され、Modelの操作がスライダとTextに反映される）


```cs
using UniRx;
using UnityEngine;

namespace MVRP.Models
{
    /// <summary>
    /// データの実体を持つ
    /// </summary>
    public sealed class SampleModel : MonoBehaviour
    {
        // Count値
        public IReadOnlyReactiveProperty<int> Count => _count;

        // 最大値
        public readonly int MaxCount = 100;

        private readonly IntReactiveProperty _count = new IntReactiveProperty(0);

        public void SetCount(int value)
        {
            // 数値の範囲を補正
            value = Mathf.Clamp(value, 0, MaxCount);
            _count.Value = value;
        }

        private void OnDestroy()
        {
            _count.Dispose();
        }
    }
}
```

```cs
using MVRP.Models;
using UniRx;
using UnityEngine;
using UnityEngine.UI;

namespace MVRP.Presenters
{
    /// <summary>
    /// Countを表示するPresenter
    /// </summary>
    public sealed class CountPresenter : MonoBehaviour
    {
        // Views
        // uGUIコンポーネントをダイレクトに参照
        [SerializeField] private Slider _slider;
        [SerializeField] private Text _text;

        // Model
        [SerializeField] private SampleModel _model;

        private void Start()
        {
            // Model -> View
            _model.Count.Subscribe(v =>
                {
                    // Slider
                    _slider.value = ((float)v / _model.MaxCount);

                    // Text
                    _text.text = $"{v}%";
                })
                .AddTo(this);

            // View -> Model
            _slider.OnValueChangedAsObservable()
                .Subscribe(x =>
                {
                    // Sliderは 0.0~1.0 なので補正
                    // こういうModel-View間での値の範囲補正も
                    // Presenterの責務
                    var value = (int)(100 * x);

                    // Modelに反映
                    _model.SetCount(value);
                })
                .AddTo(this);
        }
    }
}
```


## MV(R)Pパターンのよくある質問とその答え

`MV(R)P`パターンがわからない、という人から寄せられた質問とその解答をまとめてみました。

### 「`MV(R)P`パターンって、MonoBehaviourは使っていいんですか」

`MV(R)P`パターンは「**`MonoBehaviour`の有無について語っていません**」。

`MonoBehaviour`はすべてのオブジェクトで使ってもいいですし使わなくてもいいです。つまり、**どっちでもいいです。**
たまに「`Model`はUnity非依存にするべき」とか語っている人がいますが、そんなことはないです。

そもそも`MV(R)P`パターンは「GUIをキレイに実装すること」を目的にした設計パターンです。そのため「GUIさえキレイに実装できるなら細かい部分は割とどうでもいい」です。

たとえばアクションゲームにおいて、「ゲーム中に存在するキャラクタのステータスを画面に出す」といったパターンは頻出します。この場合はゲーム中のキャラクタも`GameObject`の上に載せて動いている場合が多いでしょう。
このとき、`MV(R)P`パターンが果たしたいことは「キャラクタのステータスをGUIに表示したい」です。

![Reimu.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47146/8a0919ae-d143-02a8-2892-921530ee8aa3.png)

（プレイヤの体力バーを出す、とかよくやるけどこのプレイヤって`GameObject`でしょ？）

もしここで「`Model`に`MonoBehaviour`を使ってはいけない」という縛りを追加してしまうと、「GUIに情報を出したいだけなのに、キャラクタを`MonoBehaviour`非依存に根本から作りから直さないといけない」という状況になってしまいます。
これでは話が大きくなりすぎてしまい「GUI周りの扱いをキレイにしたい」という目的から外れていってしまいます。

何度も言います。`MV(R)P`パターンの目的は「GUI周りをキレイに実装すること」が目的です。
`MonoBehaviour`の有無や、何がUnityに依存してる/してないという議論は**ナンセンスです。**


### 「各オブジェクトの初期化の方法がわからない」

`Model`、`View`、`Presenter`、これらの初期化は頭を悩める問題です。

いろいろやり方はあるのですが、最終的に「 `Presenter` が `View` と `Model`を参照している」という形が作れれば何でもよいです。

ぱっと思いつく限りで3パターンほどありますので、参考にしてください。

#### 初期化方法A:インスペクターウィンドウ上で紐付けておく

`Model`、`View`、`Presenter`のすべてが最初からシーンに配置されている場合に使えるパターンです。
動的に生成されたり削除されることが無いのであれば、Unityのインスペクターウィンドウ上で最初から紐付けておけばOKです。

![PresenterInitialize.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47146/d68ad1a7-af62-a48b-83c7-bbf39d00565d.png)
（インスペクター上で`Presenter`にあらかじめ紐付けておくのが一番かんたん）

#### 初期化方法B:DIContainerを使う

方法Aとあまり変わらないですが、動的にオブジェクトが生成/削除されないのであればDIContainer（`Zenject`や`VContainer`）経由でオブジェクトを`Presenter`に渡してしまうというやり方もあります。

#### 初期化方法C:動的にバインドする

これが一番ややこしいパターンです。`Model`があとから動的に追加されたり、削除されたりすることをどう扱うかです。

自分がよくやるのは別途`Dispatcher`というオブジェクトを配置し、それが`Model`の生成/削除をイベントを購読するというやり方です。

例として、次のようなものを考えてみます。

* `Player`に体力バーを表示する
* `Player`は動的にあとから増える

このようなパターンを、`Dispatcher`を使って実装してみます。

##### Model

(さっきのPlayerとおなじ)


```cs
using System;
using UniRx;
using UnityEngine;

namespace MVRP.Models
{
    /// <summary>
    /// アクションゲームにおけるプレイヤー
    /// 「画面に体力を出す」という視点からみるとModel
    /// </summary>
    public sealed class Player : MonoBehaviour
    {
        /// <summary>
        /// 体力
        /// ReactivePropertyとして外部に状態をReadOnlyで公開
        /// </summary>
        public IReadOnlyReactiveProperty<int> Health => _health;

        // 体力の最大値
        public readonly int MaxHealth = 100;
        
        private readonly IntReactiveProperty _health = new IntReactiveProperty(100);
        

        private void Start()
        {
            _health.Value = MaxHealth;
        }

        /// <summary>
        /// 衝突イベント
        /// </summary>
        private void OnCollisionEnter(Collision collision)
        {
            // Enemyに触れたら体力を減らす
            if (collision.gameObject.TryGetComponent<Enemy>(out var _))
            {
                _health.Value -= 10;
            }
        }

        private void OnDestroy()
        {
            _health.Dispose();
        }
    }
}
```

#### Modelを動的に生成するManager

`Player`を動的に生成する`Manager`がいて、こいつが`Player`生成イベントを発行しているとします。

```cs
using System;
using Cysharp.Threading.Tasks;
using UniRx;
using UnityEngine;
using Random = UnityEngine.Random;

namespace MVRP.Models
{
    public sealed class PlayerManager : MonoBehaviour
    {
        /// <summary>
        /// プレイヤ一覧
        /// ReactiveCollectionのため、Player数が増えると通知される
        /// </summary>
        public IReactiveCollection<Player> Players => _players;

        private readonly ReactiveCollection<Player> _players = new ReactiveCollection<Player>();

        // PlayerのPrefab
        [SerializeField] private Player _playerPrefab;

        private async UniTaskVoid Start()
        {
            // 初期化処理
            // Playerを時間差で4体作成
            
            await UniTask.Delay(TimeSpan.FromSeconds(1));
            _players.Add(CreatePlayer(1));
            
            await UniTask.Delay(TimeSpan.FromSeconds(1));
            _players.Add(CreatePlayer(2));
            
            await UniTask.Delay(TimeSpan.FromSeconds(1));
            _players.Add(CreatePlayer(3));
            
            await UniTask.Delay(TimeSpan.FromSeconds(1));
            _players.Add(CreatePlayer(4));
        }

        private Player CreatePlayer(int id)
        {
            // Playerの生成
            var player = Instantiate(_playerPrefab, Vector3.right * Random.Range(1f, 2f), Quaternion.identity);
            player.name = $"Player{id}";
            return player;
        }
    }
}
```

#### Presenter

`Presenter`は引数で外から`Player`と`View`の組み合わせをうけて、バインドできるようしておきます。
今回は単一の`Presenter`を使いまわして複数の`Player`と`View`を扱わせます。

```cs
using MVRP.Models;
using MVRP.Views;
using UniRx;
using UnityEngine;

namespace MVRP.Presenters
{
    // PlayerのPresenter
    public sealed class PlayerPresenter : MonoBehaviour
    {
        // Playerが生成されたらBindする
        public void OnCreatePlayer(Player player, PlayerView view)
        {
            view.SetName(player.name);
            
            // PlayerのHealthを監視
            player.Health
                .Subscribe(x =>
                {
                    // Viewに反映
                    view.SetHealth((float)x / player.MaxHealth);
                }).AddTo(this);
        }
    }
}
```

#### View

今回は`View`として「ネームプレート」と「体力バー」があったとします。
このUIを扱う`Canvas`と、各要素を制御するコンポーネントを用意して`Prefab`化しておきます。

```cs
using DG.Tweening;
using UnityEngine;
using UnityEngine.UI;

namespace MVRP.Views
{
    /// <summary>
    ///  Playerの状態を表示するView
    /// </summary>
    public sealed class PlayerView : MonoBehaviour
    {
        [SerializeField] private Text _nameplate;
        [SerializeField] private Slider _healthSlider;

        public void SetName(string name)
        {
            _nameplate.text = name;
        }

        public void SetHealth(float value)
        {
            // アニメーションしながらSliderを動かす
            DOTween.To(() => _healthSlider.value, 
                n => _healthSlider.value = n,
                value, 
                duration: 1.0f);
        }
    }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47146/06aa43aa-349f-dc26-fc59-934394d464c9.png)
（`View`の`Prefab`）

#### Dispatcher

`PlayerManager`のイベントを検知して、`View`を生成して`Presenter`にバインドする`Dispatcher`を用意します。
今回はこの`Dispatcher`をシーンにあらかじめ配置していますが、DIContainerなどで扱ってもOKです。

```cs
using MVRP.Models;
using MVRP.Presenters;
using MVRP.Views;
using UniRx;
using UnityEngine;

namespace MVRP.Dispatchers
{
    /// <summary>
    /// Playerの生成を検知して、Viewを割り当てる
    /// </summary>
    public sealed class PlayerDispatcher : MonoBehaviour
    {
        // Modelを提供するManager
        [SerializeField] private PlayerManager _playerManager;

        // PlayerのPresenter
        [SerializeField] private PlayerPresenter _presenter;

        // ViewのPrefab
        [SerializeField] private PlayerView _viewPrefab;

        private void Start()
        {
            // 今リストにあるやつをDispatch
            foreach (var p in _playerManager.Players)
            {
                Dispatch(p);
            }

            // 以降新規作成されたものをDispatch
            _playerManager.Players.ObserveAdd().Subscribe(x => Dispatch(x.Value)).AddTo(this);
        }

        private void Dispatch(Player player)
        {
            // Playerの子要素としてViewを作成
            var view = Instantiate(_viewPrefab, player.transform, true);

            // 位置を調整
            view.transform.localPosition = Vector3.up * 1.5f;
            
            // Presenterに組み合わせて通知
            _presenter.OnCreatePlayer(player, view);
        }
    }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47146/e4dc78a6-488f-e733-c93e-8bb1bad3798b.png)
（シーンに`Dispatcher`を配置して、`PlayerManager`、`Presenter`、`ViewのPrefab`を参照させる）

今回は`Presenter`をあらかじめシーンに配置していますが、`Presenter`もまた`prefab`にしてしまい都度生成する形にしても問題ありません（やりやすいようにやればOK)

#### 動作の様子


![MultiPresenter.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47146/d1e5b32c-5d83-4616-2513-8944afc4ab81.gif)

（`Player`が動的に生成されても体力バーがそれぞれに表示されており、それぞれの体力が反映されている）

### 「Presenterってどこにおけばいいんですか」

`Presenter`をシーン上のどこに配置するのか。もしくは`MonoBehaviour`を使わずにピュアクラスとして`DIContainer`に管理するべきなのか。という質問です。

答えは「自由にやってよい」です。
管理できてるならどこに`Presenter`を配置してもよいです。`uGUI`の`Canvas`の`GameObject`に`Presenter`を貼り付けてもいいですし、空の`GameObject`を作ってそこに貼り付けてもよいです。

自分が管理さえできているのであれば、どこにどう置いてもよいです。



### 「PresenterとViewの規模感がわからない」

これは「1つの`Presenter`が複数の`View`を管理していいのか」「`View`の内部で状態を持っていてよいのか」といった質問と同じです。

#### Presenterの規模感

1つの`Presenter`がどう`View`を管理するかですが、これも答えは「自分が管理できるなら自由にやってよい」です。

* 1つの`GameObject`に1つの`Presenter`だけ割り当てるやり方
* 1つの`GameObject`に複数の`Presenter`を割り当てるやり方
* 1つの`Presenter`で複数の`Model`や`View`を割り当てるやり方

![PresenterPatterns1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47146/7f301257-a738-2421-cf56-3a26f50e444d.jpeg)

![PresenterPatterns2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47146/614cf7bf-5717-5a53-16d9-c2741b54118e.jpeg)

![PresenterPatterns3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47146/78fec5a7-636b-52ac-2f31-719fa9dc7d9a.jpeg)
（どうやってもOK）


いくつかパターンは挙げられますが、どれも好きにやってOKです。

#### Viewの規模感

「`View`の内部で状態を持ってよいか」「`View`を管理するコンポーネントも`View`として扱ってよいか」ですが、答えは「はい」です。
`View`自身が状態をもち、`View`が自分自身を管理するのは`MV(R)P`パターン的には問題がありません。

たとえば、次のような「RGBの三色のスライダー」があったとします。

![ColorSlider.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47146/fdee8e29-54c0-1e08-dbd4-529085001f80.gif)


これには「3つのスライダーを管理するコンポーネント」がアタッチされているわけですが、コンポーネントは`View`とみなして問題ないです。


```cs
using UniRx;
using UnityEngine;
using UnityEngine.UI;

namespace MVRP.Views
{
    /// <summary>
    /// RGBのスライダーを管理するViewコンポーネント
    /// </summary>
    public sealed class ColorSlider : MonoBehaviour
    {
        /// <summary>
        /// 今のスライダーが指し示す色
        /// </summary>
        public readonly ColorReactiveProperty Color 
            = new ColorReactiveProperty(UnityEngine.Color.white);

        [SerializeField] private Slider _red;
        [SerializeField] private Slider _green;
        [SerializeField] private Slider _blue;

        private void Start()
        {
            Color.Subscribe(c =>
                {
                    _red.value = c.r;
                    _green.value = c.g;
                    _blue.value = c.b;
                })
                .AddTo(this);

            // スライダーがどれか1つでも変化したら反映
            Observable.Merge(
                    _red.OnValueChangedAsObservable(), 
                    _green.OnValueChangedAsObservable(),
                    _blue.OnValueChangedAsObservable())
                .Subscribe(_ =>
                {
                    Color.Value = new Color(_red.value, _green.value, _blue.value);
                })
                .AddTo(this);
        }
    }
}
```

```cs
using MVRP.Models;
using MVRP.Views;
using UniRx;
using UnityEngine;
using UnityEngine.UI;

namespace MVRP.Presenters
{
    // Presenter
    public class ColorPresenter : MonoBehaviour
    {
        [SerializeField] private ColorModel _model;
        [SerializeField] private ColorSlider _colorSlider;
        [SerializeField] private Text _text;

        private void Start()
        {
            // Model -> View
            _model.Color.Subscribe(x =>
                {
                    _text.text = x.ToString();
                    _text.color = x;
                    
                    _colorSlider.Color.Value = x;
                })
                .AddTo(this);

            // View -> Model
            _colorSlider.Color.Subscribe(x =>
                {
                    _model.Color.Value = x;
                })
                .AddTo(this);
        }
    }
}

```

```cs
using UniRx;
using UnityEngine;

namespace MVRP.Models
{
    // Model
    public class ColorModel : MonoBehaviour
    {
        public readonly ColorReactiveProperty Color 
            = new ColorReactiveProperty();
    }
}
```


### この値ってViewかModelのどちらがもつべき状態ですか

この質問は、たとえば「クリックされたら色の変わるボタンがあったときに、この"色"の情報は`View`か`Model`のどちらがもつのですか」みたいなやつです。

![ChanageColorButton.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47146/a262a3e1-4947-8950-6b47-cdb800c88cf8.gif)
（クリックされたら色の変わるボタン。この「色」情報は`Model`か`View`のどっちがもつ？）


答えは「**その色情報がゲームにおけるドメイン要素にあたるかどうか**」で決まります。
（「ドメイン」とは「ゲームのコア要素」みたいな意味だと思って下さい）

その「色情報」がゲームの成立に必要不可欠な要素であるならば、それは`Model`で管理するべき情報となります。
一方でゲームの成立に直接関係なく、あくまで演出の目的としてボタンの色を変えている程度であるならばそれは`View`で管理するべき情報となります。

たとえば「3色リバーシ」などは色情報がゲームの成立に絶対必須な要素です。
そのため3色リバーシを`MV(R)P`パターンで構築するのであれば、「色の情報は`Model`が管理するべき」となります。

一方で「ちょっと見た目がリッチなトグルボタン」という扱いであるならば、これはただの演出でしかなく、最悪「ボタンの色の変化」無くてもゲームとして成立します。
こういった場合は`View`で管理するべきとなります。


##### Model要素になるパターン

 * その情報（データ）がゲームの成立に必要不可欠である
    * ゲームの進行や状態管理に必須である

##### View要素になるパターン

 * その情報（データ）が無くてもゲームとして成立する
   * あると見た目がリッチになるが最悪無くてもなんとかなる、みたいなの
 
### View->Presenter->Model->Presenter->View って値がループしたりしないんですか


1. `View`の変化を検知して`Presenter`が`Model`に書き込む
2. `Model`の状態が変化する
3. `Model`の状態が変化したことを検知して`Presenter`が`View`に反映する
4. `View`の状態が変化する
5. `View`の変化を検知して`Presenter`が`Model`に書き込む
6. （以下無限ループ）

という現象が起きないかどうかという質問です。
答えは「`ReactiveProperty`を使っている場合においてはループしません」。

`ReactiveProperty`は「直前と同じ値が書き込まれた場合、イベントを発行しない」という仕組みになっています。そのためイベントが一巡はしますが、2周目のループには入らないようになっています。

ただし、`ReactiveProperty.SetValueAndForceNotify()`を使っていた場合は強制的にメッセージ発行が実行されるため無限ループを引き起こす可能性があります。

### Model/View/PresenterごとにAssembly Definition Filesを分けたほうがいいですか

答えは「ご自由にどうぞ」です。
厳密にやるために分けてもいいですし、面倒くさいから分けないというのも手です。
この辺は本当にご自由にどうぞ。

### 依存関係逆転則を使って、依存関係を`View`->`Presenter`->`Model`にしたほうがいいですか

参照関係は「`View` ← `Presenter` → `Model`」だが、モジュールとしての依存関係は「 `View` → `Presenter` → `Model`」にしたほうがいいんですかという質問です。

意味がわからない人はこのスライドでちょっと説明してます。

[Unityにおける設計パターン(56ページ)](https://speakerdeck.com/torisoup/unityniokerushe-ji-patan?slide=56)


結論は「冗長なのでやる必要はなし」だと自分は考えます。
厳密にやりたいならやってもいいですが、手間の割にリターンが見合わないと思います。

### 「MVPパターンを使うとUnity非依存でキレイに作れるって聞きました」

答えとしては「**できなくはないが、それ以前に考えることがある**」です。

`MVP`パターン（`MV(R)P`パターン）はもともと「GUI周り向け」の設計パターンです。
`View`（GUI）をどう管理すればキレイに実装できるか、という話でした。

しかし、この`View`という概念を拡大解釈し、「`View`（Unity）と`Model`（Unity非依存）を分けてキレイに作るための設計パターンだ」と勘違いしている人がたまにいます。

* 「`MVP`パターンを使えば、`GameObject`を使わずにゲームロジックが組める」
* 「`MVP`パターンを使えば、アクションゲームをキレイに作れる」
* 「`MVP`パターンを使えば、設計がすべてキレイになる」

これらはすべて**MVPパターンに過度の信頼をおきすぎ**です。

何回も言いますが、「**`MVP`パターンがいう`View`とはGUIのこと**」であり、`GameObject`や`MonoBehaviour`のことではありません。`MVP`パターンは「GUIを実装するためのパターン」です。

「フレームワーク（Unity）」と「ドメイン（ゲームのコアロジック）」を分離した設計はたしかに理想ではあります。
ですがこれを実現するためには「`MVP`パターン」だけでは手駒が足りず、他の設計パターンやアーキテクチャの知識も必要となってきます。（たとえば、巷で話題の「クリーンアーキテクチャ」は、まさに「複雑化するドメインからフレームワーク依存をどう分離するか」を語っているアーキテクチャとなっています。）

（参考: [Unityにおける「設計レベル」を定義してみた](https://qiita.com/toRisouP/items/79b97c472e588bb91c52)）

では`MVP`パターンを拡大解釈して、「フレームワークとドメインの分離」に用いるのがダメなのかというと、別にそうではありません。シチュエーションによっては`MVP`パターンが使える場面もあったりします。

たとえば、「サーバサイドにロジックとデータがある」「UnityはそのViewerとして使う」みたいにハッキリ区分ができている場合は`MVP`パターンを使っても上手くいく可能性があります。

ですが一方で、「アクションゲーム」みたいな「Unityにべったり依存した仕組みの上で動くゲーム」などはそもそも「フレームワークとドメインの分離」自体が難しかったりします。こういったそもそも分離が難しい類のものを無理やり分離して作るのは非常に筋が悪く、上手くいかない可能性が高いです。

なので「フレームワークとドメインの分離」をやる場合は、まずそもそもそれをやる意義があるのかを考える必要があります。「フレームワークとドメインの分離」は確かに設計上はキレイになりそうですが、実際は実施コストが高く、また開発速度も遅くなるというデメリットがあります。

* 作ろうとしているゲームの規模感
* 開発者の人数
* スケジュール
* ゲームのジャンル
* システムのアーキテクチャ（クライアント完結なのか、サーバ連携するのかなど）

このあたりを判断材料として、「そもそもフレームワークとドメインを完全に分離して作ることに本当にメリットがあるのか」「やる場合はどういうアーキテクチャを採用するのか」を考える必要があります。


というわけで、答えは「**（MVPパターンを使ってUnity非依存な実装は）できなくはないが、それ以前に考えることがある**」です。

口酸っぱくいいますが、「`MVP`パターンはもともとGUI設計のためのパターン」です。

これを応用して、「フレームワークとドメインの分離」に使ってみること自体は悪いことではありません。ですが、**それをやるには設計についての他の知識や、それ相応の覚悟が必要となります**。

そしてそれを初心者に求めるのは酷なので、**MVPパターンは素直にGUI周りにだけ使っておいた方が安全という話になります。**



# まとめ

* `MV(R)P`パターンはUnityにおける「GUI周り」の設計パターン
* `Model`と`View`を、`Presenter`という薄いレイヤでつなごう
* `Model`と`View`の連動には、UniRxを活用しよう（`ReactiveProperty`が便利）
* 上記以上のことについては、**割と自由にやってよい**
  * 絶対の正解は無いので、やりやすいように解釈して使ってOK
  * ただし「あくまでGUI周り用の設計パターンである」ということは念頭におくこと

# （追記）

~~あとで「スマホ画面から移動操作などのInputを受け付けるときの考え方」みたいなのを追記します~~
機会があれば書くかも…。