Unityで学ぶMVPパターン(2) ~ Viewコンポーネントを複数にする ~
でCompositeパターンを使ったほうが良いと思ったので、記事の追記・修正です。
目的
MVPパターンでViewコンポーネントを複数対応化のため、
Presenterのview変数を配列化する必要があるという欠点があった。
Compositeパターンでこの問題を解決したい。
シーンの用意
シーンは前の使いまわしです。
Githubにプロジェクトを置いておきます。
04-PropertyChanged-HealthBar
コンポーネントの作成
Presenterを修正してCompositeコンポーネントを新たに作成します。
ParameterPresenter
配列型だったview変数を単体の変数に戻します。
この変数に後述で作成するCompositeコンポーネントを登録します。
using UnityEngine;
using System.ComponentModel;
public class ParameterPresenter : MonoBehaviour {
public StatusModel model;
public ParameterViewBase healthView;
void Awake()
{
model.PropertyChanged += Model_PropertyChanged;
Model_PropertyChanged(model, new PropertyChangedEventArgs("")); // 開始時に値を発生させる用
}
private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
healthView.SetParameter(model.HealthMax, model.Health);
}
}
ParameterViewComposite
こちらがCompositeパターンを実装するコンポーネントです。
ParameterViewBaseを継承して、且つParameterViewBaseのコレクションを所持していることがミソです。
このパターンは再帰的な処理をする役割を持っており、
SetParameterメソッドは、リストにある全てのSetParameterも呼び出す仕組みになっています。
つまり、これでPresenter側はCompositeコンポーネントからSetParameterを呼び出せば、
複数のViewを更新することができます。
当然ですが、PresenterはView単体でも登録可能なので、適応性がグンッと上がります。
ついでに、AddComponentInChildrenメソッドを作成して、このコンポーネントがあるゲームオブジェクトの子からParameterViewBaseコンポーネントを継承したコンポーネントを検索して、childrenリストに追加するプログラムを作成します。
GUIのルートオブジェクト(Canvasオブジェクト)に、このコンポーネントを追加しておいて、
、コンポーネント上のコンテキストメニューから実行すれば、リスト生成の手間が省けます。
(ZenjectとかのDIコンテナフレームワークにある自動で依存性を注入する機能に似ている?)
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
public class ParameterViewComposite : ParameterViewBase
{
public List<ParameterViewBase> children;
[ContextMenu("AddComponentInChildren")]
public void AddComponentInChildren()
{
gameObject.GetComponentsInChildren<ParameterViewBase>(children);
children.Remove(this);
}
public override void SetParameter(int max, int value)
{
children.ToList().ForEach(child => child.SetParameter(max, value));
}
}
完成
結果は前回と変わらず、inspectorからHealthの値を変えると
- 体力バーの長さが変わる
- テキスト表示が更新される
- 画面上下が赤くなる
で3種類のViewが反映されます。
Githubに完成プロジェクトを上げておきます。
まとめ
MVPパターンでViewコンポーネントを複数対応化の際に
Presenterのview変数を配列化する修正が必要だったのが、
これで解決しました。
Compositeパターンのリストに
登録しているViewをアクティブにして、
登録していないViewは非アクティブにすれば、
動作しているGUIのみ表示するといったことができそう。
それを実現する良さげなパターンが見つかったら、また記事にしたいです。