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

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

【Unity】2つの列挙子を交互に切り替えるメソッド【拡張メソッド】

SwitchEnum() というメソッドで,列挙型の値を交互に切り替える方法を御紹介いたします。

(Unity 2019.4.17f1)


始めに

本稿は,以下の記事の続編です。

tsu-games.hatenablog.com

「絶対値を同じくする正負の数を定数とした列挙子のペアを作り,定数 -1 を乗算する事で交互に切り替える」という方法を解説しました。


前回のコード(簡略版)

NewBehaviourScript.cs

using UnityEngine;

public class NewBehaviourScript : MonoBehaviour
{
    // 状態
    enum State
    {
        A = -1,
        B = 1
        C = -2,
        D = 2
    };

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

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

中身が-1である定数Switchを掛ける事で,列挙型stateを交互に切り替えられます。

この方法には,以下のような問題点がありました。

  • 「* Switch」という記法の可読性

  • 型のキャストを記述する手間

今回はこれらの問題を,処理のメソッド化によって解決したいと思います。


新しいコード

まずは,メソッド側のスクリプトを準備します。

余計に複雑になっているように見えますが,呼び出し時の使い勝手を重視したためです。

細かい解説の前に,呼び出し元のスクリプトも見てみましょう。

NewBehaviourScript.cs

using UnityEngine;
// SwitchEnum() のため
using TsuGames;

public class NewBehaviourScript : MonoBehaviour
{
    /// <summary>
    /// 状態
    /// </summary>
    enum State
    {
        A = -1,
        B = 1,
        C = -2,
        D = 2
    };

    [SerializeField, Tooltip("状態")]
    State state = State.A;

    void Update()
    {
        // クリック(タップ)開始
        if (Input.GetMouseButtonDown(0))
        {
            // state 切り替え
            state.SwitchEnum();
            /* ↓本来このようにも書ける↓ */
            //EnumExtentions.SwitchEnum(ref state);
        }
    }
}

呼び出し時の記述はstate.SwitchEnum()です,簡単でしょう?

EnumExtentions.SwitchEnum(ref state)と書いても処理は同じなのですが,短く書けるようにメソッド側のスクリプトで工夫したというわけです。

それでは,その工夫について解説いたします。


解説

列挙型を扱うメソッドは,通常のクラスや構造体を扱うメソッドよりも複雑です。

なぜなら特定の列挙型をメソッド側で指定することは無意味であり,呼び出し側で用意されたあらゆる列挙型に対応せねばならないからです。

今回の例では呼び出し側のスクリプトStateという列挙型を用意していますが,このStateという列挙型しか扱えないメソッドを作っても仕方ないですよね。

そこで,ジェネリックメソッドを利用します。

docs.microsoft.com

メソッド名に<T>が付いている物が,ジェネリックメソッドです。

この宣言によってSwitchEnum()は,あらゆる型を操作できるようになりました!

……いや,それは困りますよね。

intVector3では使えないようにしながら,列挙型にだけ対応させたいわけです。

そこで必要になるのが,where句による型制約です。

docs.microsoft.com

パラメータの後ろに付いているwhere T : struct, Enumの部分ですね。

ここでEnumに制約しているため,列挙型のみを扱えるメソッドに設定できました(struct制約については後ほど)。

ここまで解説した部分で実現できるのは,EnumExtentions.SwitchEnum(ref state)という呼び出しかたです。

これをstate.SwitchEnum()という記述でも呼び出せるようにするため,拡張メソッド化します。

docs.microsoft.com

引数に取る列挙型を書き換える必要があるため,拡張メソッドの第1引数にはrefキーワードを用います(C# 7.2 以降の機能)。

docs.microsoft.com

この機能は構造体structにのみ適用されるため,struct型制約も必要となるわけです。

拡張メソッドの呼び出し時にはrefキーワードを省略できるので,簡潔な記述が出来るようになりました。


終わりに

前回の案で挙がった問題点は,解消されていると思います。

C# のバージョンアップによって,列挙型のメソッドも以前より作りやすくなりましたね。

またなにか,面白い提案が出来ればと思います。

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