デニッキ

XRエンジニア🔰の備忘録・日記です.

UniRxのReactivePropertyを用いたMV(R)Pパターンを学習しました

UniRxのReactivePropertyを用いたMV(R)Pパターンを勉強しました.
Unity1Weekの後半でコードのぐちゃぐちゃ具合で痛い目に遭ったので,
これを機にしっかり設計,責務配置を考慮したコーディングの勉強をやっていきます.

UniRx・ReactiveProperty

こちらのqiita記事シリーズを読んでいきました.
qiita.com

今日はその1からその3の途中まで読みました.
UniRx入門 その1 - Qiita
UniRx入門 その2 - メッセージの種類/ストリームの寿命 - Qiita
UniRx入門 その3 -ストリームソースの作り方 - Qiita

Observerパターンやストリームの概念はは以前学習したことがあるのでスムーズに頭が入った.
Subject,IObservable,IObserver,Subscribe…
Observerパターン[Java] - デニッキ
ラムダ式とStreamAPI[Java] - デニッキ

その3の途中でReactivePropertyの項でMV(R)Pパターンの話が出てきたので,やってみた.

MV(R)Pパターン

Javaを触れる際にMVCパターンを学習したことがありました.
MVCとMVPの違いとかは以下の記事がわかりやすく説明されてる.

Webアプリケーション開発者から見た、MVCとMVP、そしてMVVMの違い - Qiita

サイバーエージェントブログのこちらの記事もわかりやすい.
Web出身のUnityエンジニアによる大規模ゲームの基盤設計 | CyberAgent Developers Blog

MV(R)PパターンはModel View (Reactive) Presenterパターンのこと.Reactiveが加わっている.
とりすーぷさんのこちらのスライドも参考にしました.

www.slideshare.net

Unity1weekでuGUI実装する際のコードもなんじゃこりゃ~になってたので
これはしっかり勉強しておきたい.

ちなみにUnity1weekで用いたライフ表示のアセットはMVPパターンだったのを思い出した.
でもHealthBarController.csって書いてあったからMVCパターンなのかなあ.難しい.
assetstore.unity.com
具体的なコード部分は各自がアセットダウンロードして確認して頂ければ.

ReactivePropertyを用いたMV(R)PパターンでuGUI実装してみた

こちらの記事が参考になったので利用いたしました.

Unityで学ぶMVPパターン ~ UniRxを使って体力Barを作成する ~ - Qiita
Unityで学ぶMVPパターン(2) ~ Viewコンポーネントを複数にする ~ - Qiita

このサンプルコードをできる限りカプセル化とDisposeをさせ,
さらにプレイヤーがダメージを受ける際にHPを減らすのを加えて実装しました.

こんな感じ.
Model部分

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

using UniRx;

public class StatusModel : MonoBehaviour
{
    private int healthMax = 100;
    private ReactiveProperty<int> healthRP = new ReactiveProperty<int>(100);

    public IReadOnlyReactiveProperty<int> Health
    {
        get { return healthRP; }
    }
    public int HealthMax
    {
        get { return healthMax; }
    }

    public void GetDamaged(int value)
    {
        if (healthRP.Value > 0)
        {
            healthRP.Value -= value;
        }
        else
        {
            healthRP.Value = 0;
        }
    }
}

Presenter部分
今回プレゼンターは1View1Presenterではなく,一纏めにしています.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

using UniRx;

public class ParameterPresenter : MonoBehaviour
{

    [SerializeField] private StatusModel model = null;
    [SerializeField] private ParameterViewBase[] healthViews = null;

    void Awake()
    {
        foreach (var view in healthViews)
        {
            model.Health
            .Subscribe(value => { view.SetParameter(model.HealthMax, value); })
            .AddTo(this);
        }
    }
}

View部分
qiita記事と殆ど変わりません.
publicフィールドになっていたのをprivate serializefieldに変えただけです.

敵の攻撃部分
ボタンクリック時の挙動をObservable化した.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UniRx;

[RequireComponent(typeof(Button))]
public class Enemy : MonoBehaviour
{
    [SerializeField] private StatusModel model = null;
    [SerializeField] private int damage = 5;

    void Start()
    {
        Button button = GetComponent<Button>();
        button.OnClickAsObservable()
            .Subscribe((_) => { model.GetDamaged(damage); })
            .AddTo(this);
    }
}

やってみた

Enemy Attack ボタンを押すごとにPlayer Healthが減っていきます.
減るごとに回りが赤く,ヘルスバーも,テキストも減っていきます.

f:id:xrdnk:20200308000401g:plain

終わりに

PS. 何か気になる部分とかツッコミ部分があればコメントくださいです.