Tsuの雑記¯\_(ツ)_/¯

主に製作メモ・備忘録として使用。製作したアプリのリンクもあります。

【Unity】2つの状態を同じ命令で切り替える【C#】

列挙型enumを用いて分岐をする際に,同じ命令で2つの列挙子を切り替える方法です。

(Unity 2018.3.0f2)

続編記事にて,拡張メソッドとして改良した物を御紹介しております。

tsu-games.hatenablog.com

列挙型enumの使いかたについては,以下の記事を御覧ください。

tsu-games.hatenablog.com

「画面をクリック(タップ)するたびに,数字のカウントアップと回転を切り替える」というプログラムを作ります。

UI.Text がカウントアップと回転を交互に行っている画像
カウントアップと回転の切り替え


元のコード

using UnityEngine;

public class NewBehaviourScript : MonoBehaviour
{
    // 状態
    enum State { CountUp, Rotate };

    [SerializeField, Range(0, 1080), Tooltip("回転速度")]
    int speed = 360;
    [SerializeField, Tooltip("状態")]
    State state = State.CountUp;
    [SerializeField, Tooltip("数値")]
    float time = 0;
    [SerializeField, Tooltip("テキスト")]
    UnityEngine.UI.Text timeText = null;

    // 回転角
    Vector3 eulers;
    // 回転方向
    Vector3 vector3 = Vector3.back;

    void Update()
    {
        // State.Rotate の場合
        if (state == State.Rotate)
        {
            // 回転角の更新
            eulers = Time.deltaTime * vector3 * speed;
            // ui.Text を回転させる
            timeText.transform.Rotate(eulers);
            // クリック(タップ)開始
            if (Input.GetMouseButtonDown(0))
            {
                // State.CountUp に切り替え
                state = State.CountUp;
            }
        }
        // State.CountUp の場合
        else if (state == State.CountUp)
        {
            // time に deltaTime を加算
            time += Time.deltaTime;
            // timeText を更新
            timeText.text = ((int)time).ToString();
            // クリック(タップ)開始
            if (Input.GetMouseButtonDown(0))
            {
                // State.Rotate に切り替え
                state = State.Rotate;
            }
        }
    }
}

このままでも正常に作動しますが,2つの状態それぞれでInput.GetMouseButtonDown(0)を検知しているのが冗長に思えます。

これを1か所で検知する事によって,コードを整理します。


列挙定数を工夫する

using UnityEngine;

public class NewBehaviourScript : MonoBehaviour
{
    // 状態
    enum State
    {
        CountUp = -1,
        Rotate = 1
    };

    [SerializeField, Range(0, 1080), Tooltip("回転速度")]
    int speed = 360;
    [SerializeField, Tooltip("状態")]
    State state = State.CountUp;
    [SerializeField, Tooltip("数値")]
    float time = 0;
    [SerializeField, Tooltip("テキスト")]
    UnityEngine.UI.Text timeText = null;

    // 回転角
    Vector3 eulers;
    // 回転方向
    Vector3 vector3 = Vector3.back;

    void Update()
    {
        // State.Rotate の場合
        if (state == State.Rotate)
        {
            // 回転角の更新
            eulers = Time.deltaTime * vector3 * speed;
            // ui.Text を回転させる
            timeText.transform.Rotate(eulers);
        }
        // State.CountUp の場合
        else if (state == State.CountUp)
        {
            // time に deltaTime を加算
            time += Time.deltaTime;
            // timeText を更新
            timeText.text = ((int)time).ToString();
        }

        // クリック(タップ)開始
        if (Input.GetMouseButtonDown(0))
        {
            // State.CountUp に切り替え
            state = (State)((int)state * -1);
        }
    }
}

列挙型Stateの列挙子に,それぞれ-11という定数を割り当てました。

これによって,今の状態にかかわらずstate-1を掛ける事で相互の切り替えが可能になりました。

ただし,-1の意味が分かりにくいという欠点が有ります。

当ブログではほとんどの命令にコメントを添えていますが,コメントが無くとも理解できるコードを心掛けたいものです。

そこで,定数の出番です。

掛ける数である-1を定数にして,役割を明確化しましょう。


定数を利用する

docs.microsoft.com

using UnityEngine;

public class NewBehaviourScript : MonoBehaviour
{
    // 状態
    enum State
    {
        CountUp = -1,
        Rotate = 1
    };

    // 状態切り替え定数
    const int Switch = -1;

    [SerializeField, Range(0, 1080), Tooltip("回転速度")]
    int speed = 360;
    [SerializeField, Tooltip("状態")]
    State state = State.CountUp;
    [SerializeField, Tooltip("数値")]
    float time = 0;
    [SerializeField, Tooltip("テキスト")]
    UnityEngine.UI.Text timeText = null;

    // 回転角
    Vector3 eulers;
    // 回転方向
    Vector3 vector3 = Vector3.back;

    void Update()
    {
        // State.Rotate の場合
        if (state == State.Rotate)
        {
            // 回転角の更新
            eulers = Time.deltaTime * vector3 * speed;
            // ui.Text を回転させる
            timeText.transform.Rotate(eulers);
        }
        // State.CountUp の場合
        else if (state == State.CountUp)
        {
            // time に deltaTime を加算
            time += Time.deltaTime;
            // timeText を更新
            timeText.text = ((int)time).ToString();
        }

        // クリック(タップ)開始
        if (Input.GetMouseButtonDown(0))
        {
            // state 切り替え
            state = (State)((int)state * Switch);
        }
    }
}

Switchという定数名によって,stateに対する操作が直感的に分かりやすくなりました。


問題点

乗算記号と定数名との相性に,違和感は残ってしまうかもしれません。

つまり,「スイッチを掛けるとはどういう事だ?」という疑問です。

それから,キャストが複雑であることも考えものです。

これが,今回の提案の限界かもしれませんね……。

あまり実用的ではないかもしれませんが,せっかく思い付いたので公開してみました。

なにか良いアイディアがございましたら,御連絡お待ちしております。

以上,2つの状態を同じ命令で切り替える方法の提案でした。