UniRxを勉強しよう7

は~~~~~~い
今日もやっていくぞ!

完成までの流れ

1. まずは簡単に見た目だけ作る
2. ボタンを押下した際にこれを検知する
3. 押下されたボタンから数値や記号を取得する
4. 引数に渡された数値がディスプレイに表示されるようにする
5. 検知した数値や記号から四則演算を実施できるようにする
6. 見てくれを整える
7. 書き出す

昨日は5を実現するためのフローチャートを作り、機能を考えるところで終わっていた。今日はその実装と微調整だ。

完成したものがこちらになります

まず概要だが、今回作成した電卓は大きく3つのスクリプト(クラス)で動作が決まっている。

1. PassPushedButton
 電卓のボタンが押下されたことを検知する
2. ShowDisplayButton
 電卓のディスプレイに値を表示する
3. Calcurater
 計算の実処理を行う

まず一つ目!PassPushedButtonから

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UniRx;
using System;
using UnityEngine.UI;
using System.Security.Cryptography.X509Certificates;
using System.Threading;

public class PassPushedButton : MonoBehaviour
{
    [SerializeField] List<Button> buttons;
    [SerializeField] List<Button> operaterKeys;
    [SerializeField] List<Text> texts;


    //このクラスからOnNextで別クラスに処理を渡せるようSubjectを用意
    private Subject<string> sendCalcSubject = new Subject<string>();
    private Subject<int> sendOperaterSubject = new Subject<int>();

    //なんだっけこれ…プロパティ?外部クラスからアクセス(getのみ)できるようにしておく
    public IObservable<string> KeyClicked
    {
        get { return sendCalcSubject; }
    }

    public IObservable<int> OperaterKeyClicked
    {
        get { return sendOperaterSubject; }
    }

    // Start is called before the first frame update
    void Start()
    {


    }

    // Update is called once per frame
    void Update()
    {

    }

    void Awake()
    {
        for (int i = 0; i < buttons.Count; i++)
        {
            ButtonCheck(i);
        }
        for(int i =0; i < operaterKeys.Count; i++)
        {
            OperatorKeyChecked(i);
        }
    }

    void ButtonCheck(int i)
    {
        buttons[i].onClick.AsObservable()
            .Subscribe(count =>
            {
                //定義したSubjectからOnNextでメッセージを出す
                sendCalcSubject.OnNext(texts[i].text);
            })
            .AddTo(gameObject);
    }

    void OperatorKeyChecked(int i)
    {
        operaterKeys[i].onClick.AsObservable()
            .Subscribe(count =>
            {
                sendOperaterSubject.OnNext(i);
            })
            .AddTo(gameObject);
    }
}

次にShowDisplay。

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

public class ShowDisplay : MonoBehaviour
{
    //メッセージのやりとり対象はInspector画面から設定する。
    public Text displayPanelText;
    private int operatorFlg = 0;
    [SerializeField] private PassPushedButton passPushedButton;
    [SerializeField] private Caculater calculater;

    // Start is called before the first frame update
    void Start()

    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    void Awake()
    {
        //監視対象スクリプトからOnNextが飛んで来たら下記Subscribe処理を実行
        passPushedButton.KeyClicked
            .Subscribe(KeyCode => ShowToDisPlay(KeyCode))
            .AddTo(gameObject);

        calculater.CalculateResult
            .Subscribe(result =>
            {
                displayPanelText.text = result;
                operatorFlg = 1;
            })
            .AddTo(gameObject);
    }

    private void ShowToDisPlay(string value)
    {
        //演算記号が入力されたら一度ディスプレイをクリアする
        if (operatorFlg == 1)
        {
            displayPanelText.text = "";
            operatorFlg = 0;
        }
        //入力値が少数点でなければ先頭の0は消す
        //else 
        if (displayPanelText.text=="0" && value !=".")
        {
            displayPanelText.text = value;
        }
        else
        {
            displayPanelText.text += value;
        }
    }
}

最後にCalcurater

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

public class Caculater : MonoBehaviour
{
    //メッセージのやりとり対象はInspector画面から設定する。
    public Text displayPanelText;
    [SerializeField] private PassPushedButton passPushedButton;
    private Nullable<double> calculatedValue;
    private Nullable<int> operationSymbol;
    private string displayText;

    private Subject<string> sendCalculateSubject = new Subject<string>();
    public IObservable<string> CalculateResult
    {
        get { return sendCalculateSubject; }
    }

    // Start is called before the first frame update
    void Start()

    {

    }

    // Update is called once per frame
    void Update()
    {

    }

    void Awake()
    {
        //演算記号の入力を検知
        passPushedButton.OperaterKeyClicked
            .Subscribe(operateKeyNum => Calculate(operateKeyNum))
            .AddTo(gameObject);
    }

    private void Calculate(int newOperationSymbol)
    {
        displayText = displayPanelText.text;
        if (newOperationSymbol != 5)
        {
            //数値・演算記号が共に入力済みだったとき
            if (calculatedValue.HasValue && operationSymbol.HasValue)
            {
                //+
                if (operationSymbol == 0) calculatedValue += double.Parse(displayText);
                //-
                if (operationSymbol == 1) calculatedValue -= double.Parse(displayText);
                //×
                if (operationSymbol == 2) calculatedValue *= double.Parse(displayText);
                //÷
                if (operationSymbol == 3) calculatedValue /= double.Parse(displayText);
                //=
                if (operationSymbol == 4) calculatedValue = double.Parse(displayText);

                //今回入力された演算記号を格納
                operationSymbol = newOperationSymbol;

                //AC(演算記号リセットのため、これだけ判定タイミングをずらす)
                if (operationSymbol == 6)
                {
                    calculatedValue = 0;
                    operationSymbol = null;
                }
                //showdisplayに計算結果を送信する
                sendCalculateSubject.OnNext(calculatedValue.ToString());
            }
            else
            {
                calculatedValue = double.Parse(displayText);
                operationSymbol = newOperationSymbol;
                sendCalculateSubject.OnNext(calculatedValue.ToString());
            }

        }
        //Cが入力されたとき、変数の値は書き換えずにディスプレイのみクリアする
        else sendCalculateSubject.OnNext("0");
    }
}

各コードについての解説をコメントで書いた。
今回はフローチャートを作成してからスクリプトを作成したのだが、これがとても楽しい…
先に色々考えておいて、コードを書いてみてカチッと動きがはまるとたまらん!(まあ適宜ロジック修正したけど)

期限は明日となっているのでコードをきれいに書きなおし、また見た目も整えていきたいと思う。