UniRxを勉強しよう8(完結)

めっちゃくちゃ間が空いたが、ようやく電卓アプリがひと段落したのでコードを公開してみる。
Githubにも上げているが、もしGithubからいろんな情報が流出したら怖いのでいったんここで公開する。

こんな感じになりました

入力検知クラス

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

public class PassPushedButton : MonoBehaviour
{
    [SerializeField] List<Button> NumberKeys;
    [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> NumberKeyClicked
    {
        get { return sendCalcSubject; }
    }

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

    void Awake()
    {
        for (int i = 0; i < NumberKeys.Count; i++)
        {
            SetMonitoringNumberKeyClicked(i);
        }
        for(int i =0; i < operaterKeys.Count; i++)
        {
            SetMonitoringOperatorKeyClicked(i);
        }
    }

    void SetMonitoringNumberKeyClicked(int i)
    {
        NumberKeys[i].onClick.AsObservable()
            .Subscribe(count => sendCalcSubject.OnNext(texts[i].text))
            .AddTo(gameObject);
    }

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

ディスプレイへの表示クラス

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

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

    void Awake()
    {
        //監視対象スクリプトからOnNextが飛んで来たら下記Subscribe処理を実行
        passPushedButton.NumberKeyClicked
            .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)
        {
            //小数点が入力されたら
            if (value == ".")
            {
                //小数点が既に存在していたら何もせずに終了
                if (displayPanelText.text.Contains(".") && value == ".") return;
                //表示中の数字に小数点をつける
                displayPanelText.text += value;
            }
            //整数が入力されたら
            else
            {
                //ディスプレイを一度クリアする
                displayPanelText.text = "";
            }
            operatorFlg = 0;
        }

        //小数点が二つ以上なら入力を無視する
        if (displayPanelText.text.Contains(".") && value == ".") return;

        //入力値が小数点でなければ先頭の0は消す
        else if (displayPanelText.text == "0" && value != ".")
        {
            displayPanelText.text = value;
        }
        else
        {
            displayPanelText.text += value;
        }
    }
}

計算クラス

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; }
    }

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

    private void Calculate(int newOperationSymbol)
    {
        displayText = displayPanelText.text;

        //正負の入れ替えが選択されたとき
        if (newOperationSymbol == 7)
        {
            displayText = (double.Parse(displayText) * -1).ToString();
            sendCalculateSubject.OnNext(displayText);
        }
        //Clearが入力された場合
        else 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) 
                    if(double.Parse(displayText) == 0 )calculatedValue = 0;
                    else 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");
    }
}

以上!
修正点はいくつかあるが、大きなところとしては

  • 「イコール」で結果表示後に小数点を入力するとディスプレイ上の数値がクリアされる点を改善
  • 0で除算を行った場合に「Err」と表示される点を改善

という修正を実施した。
自分で動かした範囲ではうまいこと動いている。

さて、今回電卓を作るなかでUniRxで四苦八苦したわけだが、次第に慣れてくるとInspectorをポチポチしなくてもイベント関連の処理を定義していけるのは結構便利だった。
使いこなせているかというとまだ微妙だが、いろいろなサイトを参考にしたおかげで何とか形になった。

今からUniRxを勉強する人はここまでで紹介した記事を参照してみたらやりやすいと思います。

とりあえず、簡単だが今日はここまで!