はじめに
先日note
の方にこのような記事を書きました。
正直Unityちゃんデスクトップフィギュアが欲しい*1という不純な動機で書き始めましたが、想像以上に多くの方から反応をいただけて嬉しかったです。
今回はこちらの記事で紹介仕切れなかったクラス設計に関することを書いていこうと思います。
UIの表示について
このゲームにおけるUI表示はほぼ全てMV(R)Pパターンを用いて作成されています。
まだMV(R)Pパターン
を知らないよという方は以下のスライドをみてみると良いと思います。
また今回作成したゲームで実際に活用した一例を紹介させてください。
設定画面の例
このスライダーはMV(R)P
パターンが用いて作りました。
using UniRx; using UnityEngine; using UnityEngine.UI; public class SliderPresenter : MonoBehaviour { // View // Viewクラスを作る場合もあるが,規模感によりけり。 [SerializeField] private Slider _slider; // Model // ここの実装方法は無数に考えられる。こちらも規模感と要相談。 // 1. MonoBehavoirを継承してコンポーネント化 ([SerializeField] 等) // 2. ScriptableObjectを使う ([SerializeField] 等) // 3. Singletonにする (Model.Instance) // 4. ServiceLocator, DIフレームワークを使う (依存性を外部から注入する) // etc... [SerializeField] private Config _config; private void Start() { _slider.OnValueChangedAsObservable() .DistinctUntilChanged() .Subscribe(value => _config.ChangeMouseSensitivity(value)) .AddTo(this); _config.MouseSensitivity .Subscribe(value => _slider.value = value) .AddTo(this); } } // Modelクラス // 前述の通り,様々な実装が考えられる。 public class Config : MonoBehaviour { private ReactiveProperty<float> _mouseSensitivity = new ReactiveProperty<float>(); public IReadOnlyReactiveProperty<float> MouseSensitivity => _mouseSensitivity; /// <summary> /// マウス感度を変更する /// </summary> public void ChangeMouseSensitivity(float value) => _mouseSensitivity.Value = value; }
Viewの扱い
今回はUnityEngine.UI.Slider
をView
として扱いました。
docs.unity3d.com
ただViewクラス
を自分で定義するパターンもありますが、ここら辺は規模感(大人数で作業したり)とかで変えると良いかもしれません。
developers.cyberagent.co.jp
Modelの扱い
Model
をどのように定義するかは無数に方法があると思います。
MonoBehavour
を継承するパターンScriptableObject
を継承するパターンSingleton
にするパターン(SingletonMonoBehavoir
を継承する等)- 依存性注入するパターン
開発の規模感によりどれを選ぶ可能性もあると思いますが,テストの事を考えると依存性注入するパターンが一番良さそうな気もします。(個人的な意見)
ただ依存性注入するにも色々パターンがあります。
- サービスロケーターを使うパターン
- DIフレームワークを使うパターン
もし依存性注入するならインターフェイスor抽象クラスを定義してあげるとモックを使ったテストが行えます。
ScriptableObjectを用いたMV(R)P
実は今回作成したゲームでは、ScsriptableObject
を継承するパターンを利用しています。
これは結構トリッキーな使い方で、こちらの記事を参考に自分で考えたやり方なのですが割と好みな手法です。
forpro.unity3d.jp
// Modelクラス [CreateAssetMenu()] public class Config : ScriptableObject, ISerializationCallbackReceiver { private float _mouseSensitivity; private ReactiveProperty<float> _reactiveMouseSensitivity; public IReadOnlyReactiveProperty<float> MouseSensitivity => _reactiveMouseSensitivity; public void OnAfterDeserialize() { _reactiveMouseSensitivity = new ReactiveProperty<float>(_mouseSensitivity); } public void OnBeforeSerialize() { } /// <summary> /// マウス感度を変更する /// </summary> public void ChangeMouseSensitivity(float value) => _reactiveMouseSensitivity.Value = value; }
この手法はシーンの遷移とかも一切考えずにでき,いろんなデータの初期値を簡単に切り替えられる(アセットを複数作れば良い)のでメリットは多いと思います。
さいごに
色々とMV(R)Pパターンについて書きましたが、正直どれが正解とかは特にないとは思います。
規模感であったり,開発メンバーの技術力であったりと考慮して自分なりの結論が出せればOKではないでしょうか。
ではまた。