2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

MVPパターンにUniRxを適用

Posted at

はじめに

こんにちは。今回はUnityにおける設計パターンの「MVPパターン」を実装してアウトプットという形で記事にしてみたいと思います。
この記事ではMVPの説明はメモ程度にとどめておきます。詳しいことを知りたい人は、参考にした記事を以下においておくので、そちらを参照ください。

ざっくりMVPとは

このパターンを用いることで、Unity内で複雑になりがちなGUI周りの処理が綺麗に書けるようになります。「Model」「View」「Presenter」の頭文字をとってMVPモデルです。今回はオブザーバーにUniRxの「ReactiveProperty」を使用するので、MV(R)Pとも表現できますね。$\tiny{最近だとUniRxの代わりにR3というライブラリが使われることもあるそうで。}$
 
さておき、それぞれの役割は以下の通りです。

  • Model
    GUIに表示させるデータを保持する部分。例えばint型の残り時間であるとか。
    PresenterのこともViewのことも知らない。独立している。
     
  • View
    GUI。UnityだとButtonとかSliderとかTextMeshProとか。
    PresenterのこともModelのことも知らない。独立している。
     
  • Presenter
    ModelとViewの橋渡し役。
    Modelの情報をViewに反映させるし、Viewで変更が行われたらModelに反映させる。

MVPパターンの実装

BGM音量設定のUI部分をMVPで実装していこうと思います。

Unity内のオブジェクト

Unity内で適当にUI部分とその他を配置しました。ヒエラルキーはこんな感じです。

image.png

CanvasにViewスクリプトをアタッチし、空っぽのゲームオブジェクトScriptにPresenterスクリプトをアタッチしています。ModelはMonobihaviourを継承しないピュアクラスですので、Unityエディタ上ではインスタンス化しません。

画面内のUIはこんな感じです。

image.png

画像内の+ボタンを押したら音量が上がり、数字が増える。-ボタンを押したら音量が下がり、数字が減る。この簡単な処理をMVPパターンで実装していきます。

スクリプト

Model

Modelサンプルコード
Model
using UniRx;

namespace MVP_pettern
{
    public class Model
    {
        const int MAX_SPEED = 15;
        const int MIN_SPEED = 0;
    
        private ReactiveProperty<int> _speed;
        public IReadOnlyReactiveProperty<int> Speed
        { 
            get { return _speed; }
        }

        //コンストラクタ
        public Model()
        {
            _speed = new ReactiveProperty<int>(0);
        }

        //スピードのセット
        public void SetSpeed(int value)
        {
            value = Mathf.Clamp(value, MIN_SPEED, MAX_SPEED);
            _speed.Value = value;
        }
    }
}

スピードはプロパティでgetだけできるようにしています。setはメソッドを通しています。ここはお好みでプロパティからそのままsetできるようにしてもいいと思います。

そして、getで公開しているIReadOnlyReactivePropertyは、interfaceの継承を追っていけば分かると思いますが、Subscribeが出来るようになっています。publicとして他クラスに公開することで、Presenterから購読と読み取りだけが出来るようになるってわけですね。

View

Viewサンプルコード
View
using UnityEngine;
using System;
using TMPro;

namespace MVP_pettern
{
    public class View : MonoBehaviour
    {
        //テキスト
        [SerializeField] TextMeshProUGUI _speedText;

        //「+」ボタンのイベントリスナー
        public Action OnPlusButtonClickedListener;

        //「-」ボタンのイベントリスナー
        public Action OnMinusButtonClickedListener;

        //スピードが変わったときのメソッド
        public void OnSpeedChanged(int speed)
        {
            _speedText.text = speed.ToString();
        }

        //プラスボタンがクリックされたとき
        public void OnPlusButtonClicked()
        {
            if (OnPlusButtonClickedListener != null) { OnPlusButtonClickedListener(); }
        }

        //マイナスボタンがクリックされたとき
        public void OnMinusButtonClicked()
        {
            if (OnMinusButtonClickedListener != null) { OnMinusButtonClickedListener(); }
        }
    }
}

それぞれのボタンが押されたときのActionをpublicで宣言しています。
このActionに、Presenterがボタンを押されたときの挙動(メソッド)を購読します。

Presenter

Presenterサンプルコード
Presenter
using UnityEngine;
using UniRx;

namespace MVP_pettern
{
    public class Presenter : MonoBehaviour
    {
        [SerializeField] View _view;
        private Model _model;

        private void Start()
        {
            _model = new Model();

            SetEvents();
            Bind();
        }

        //Viewにボタンが押されたときのメソッドを購読
        private void SetEvents()
        {
            _view.OnPlusButtonClickedListener += OnPlusButtonClicked;
            _view.OnMinusButtonClickedListener += OnMinusButtonClicked;
        }

        //Modelにスピードが変わったときのメソッドを購読
        private void Bind()
        {
            _model.Speed
                .Subscribe(_view.OnSpeedChanged)
                .AddTo(this.gameObject);
        }

        private void OnPlusButtonClicked()
        {
            _model.SetSpeed(_model.Speed.Value + 1);
        }

        private void OnMinusButtonClicked()
        {
            _model.SetSpeed(_model.Speed.Value - 1); 
        }
    }
}

Modelが公開しているIReadOnlyReactivePropertyにスピードが変わったときのViewのメソッドを購読します。
また、Viewが公開しているActionに、それぞれModelのスピードを変えるメソッドを購読しています。

MVPパターンを用いることで、ModelとViewが独立した状態を実装することが出来ました。ちょっと複雑な構造ですが、ゲーム内のGUI周りの処理がごちゃごちゃするのをある程度防いでくれます。便利ですね。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?