🔰

【Unity,C#】MVPパターン入門【GIFアリ】

に公開

関連記事

背景と補足

  • MVPというのを時々聞く。特にUI周りで有効だとか(依存関係の整理)
  • 学習する!!!

MVPとは何か

  • 実装対象のプログラムをModel, View, Presenter に分けて設計するパターン
    • Model:データ,更新ロジックを保持(例:体力、スコアなど)
    • View:UIの見た目を担当、入力をPresenterに通知
    • Presenter:入力を受け取りModelを制御、Modelの状態に応じてViewを更新

どんなときに有効?

  • UIの状態,表示が複雑なロジックに依存(例:HP,スコア,インベントリ表示)

    • 状況に応じてUIを更新する処理が複雑になりがち
    • View単体で処理するとデータの参照,描画が混在
  • 同じModelを異なるViewで再利用したい(例:簡易,詳細ウィンドウで同じデータを表示)

    • 1Modelのロジックを複数のViewに反映できる
  • 画面遷移や状態遷移をしたい(例:チュートリアルのUI)

    • Model:状態の履歴,条件,ロジック保持
    • View:UI表示,非表示
    • Presenter:状態遷移の管理者になり、画面構成を整理
  • 外部のゲームロジックと連携したい場合(例:攻撃を受けてHPバー減少)

    • Presenterが外部との窓口になり、Model,Viewの内部を知られずに制御できる
    • このように、外部からの操作や通知もPresenter経由にすると、疎結合が保てる

どんなときに向かない?

  • 小規模 or 単発のロジック
    • それ、3クラスもいる? と思ったら注意
    • この後の実装例こそそうだったりする

実装例

MVPパターンを取り入れない場合

  • 入力検知側
public class ClickBroadcaster : MonoBehaviour, IPointerClickHandler
{
    public static event Action OnButtonClicked;

    public void OnPointerClick(PointerEventData eventData)
    {
        OnButtonClicked?.Invoke();
    }
}
  • UI側
public class ColorChanger : MonoBehaviour
{
    [SerializeField] private Image img;

    private void OnEnable()
    {
        ButtonClickBroadcaster.OnButtonClicked += ChangeColor;
    }

    private void OnDisable()
    {
        ButtonClickBroadcaster.OnButtonClicked -= ChangeColor;
    }

    private void ChangeColor()
    {
        img.material.color = Random.ColorHSV();
    }
}
  • InputManager がイベント発行、ButtonSystem が購読を行う

MVPパターンを取り入れる場合

  • Model (アタッチ先:ナシ)
public class ColorModel
{
    public Color CurrentColor { get; private set; }

    public void GenerateNewColor()
    {
        CurrentColor = Random.ColorHSV();
    }
}
  • View (アタッチ先:Imageコンポーネントを持つオブジェクト)
public class ImageView : MonoBehaviour, IPointerClickHandler
{
    public event Action OnClicked;

    [SerializeField] private Image image;

    public void OnPointerClick(PointerEventData eventData)
    {
        OnClicked?.Invoke();
    }

    public void SetColor(Color color)
    {
        image.color = color;
    }
}
  • Presenter (アタッチ先:ナシ)
public class ImagePresenter
{
    private readonly ImageView view;
    private readonly ColorModel model;

    public ImagePresenter(ImageView view, ColorModel model)
    {
        this.view = view;
        this.model = model;

        view.OnClicked += OnViewClicked;
    }

    private void OnViewClicked()
    {
        model.GenerateNewColor();
        view.SetColor(model.CurrentColor);
    }
}
  • MVPの初期化,Presenter保持 (アタッチ先:適当なオブジェクト)
public class GameRoot : MonoBehaviour
{
    [SerializeField] private ImageView view;
    private ImagePresenter presenter;

    private void Start()
    {
        var model = new ColorModel();
        presenter = new ImagePresenter(view, model);
    }
}

比較

  • MVPなし:UIが直接イベント処理を持つ
  • MVP:Presenterがロジックを中継する

MVPパターンのメリットまとめ

  • 責務をより分離できる

    • 表示処理はView、データはModel、制御はPresenter
    • Presenterにより、View,Modelがお互いに仕様変更の波及を受けにくい
  • 再利用性が高い

    • 同じModelに対し、View,Presenterを複数使い分けられる
    • Viewの再利用性が高い → Prefabの再利用性も高い
  • 例:ImageView → ImagePresenter → ColorModel の連携

    • ImageView:クリック通知
    • ImagePresenter:ロジックを中継し、色を生成
    • ColorModel:色状態を管理
      各要素が互いを直接参照しないことで、柔軟に入れ替え可能

参考

Discussion