レオ 2023/08/26 20:46

Enumステート管理

はじめに

スマホアプリのときは、表示しているUIボタンとかがRaycastOnでイベント設定がされていれば押したときの挙動を紐づけるだけでいろいろできるので簡単ですが
コンシューマー,,,というかコントローラー操作を想定するとタップ以外でイベントを発火しないといけないのでちょっと手間です.

そこで,ベストプラクティスかはわかりませんが自分の場合最近こういう処理を書くことが多いです.

    enum TitleMenuState
    {
        Campaign,
        Config
    }

    Dictionary<TitleMenuState, IMenu> _stateDictionary;
    MenuState _state = MenuState.Top;
    IMenu current => _stateDictionary[state];

	// 1つ上のメニューボタンにフォーカス
    public void Decrement()
    {
        var beforeState = _state;
        int verticalIndex = (int)(_state - 1);
        if (verticalIndex < 0)
        {
            // ループさせるかどうかで処理かえる...
            _state = TitleMenuState.Campaign;
        }
        else
        {
            _state = (TitleMenuState)verticalIndex;
        }
    }

共通化を試みる

こういうif文をあっちこっちの画面でかくのはちょっと嫌だなぁと思ったので
ちょっとステートを管理するクラスを作ってみました.

Enumを使ったステート管理クラス

	public sealed class EnumState<TEnum> where TEnum : Enum
    {
        private Index _index;
        private Dictionary<int, TEnum> _dic;

        public EnumState(bool loop, TEnum currentEnum = default)
        {
            var values = Enum.GetValues(typeof(TEnum));
            _index = new(
                maxIndex: values.Length-1,
                loop: loop,
                currentIndex: Convert.ToInt32(currentEnum));

            _dic = new Dictionary<int, TEnum>(values.Length);
            int index = 0;
            foreach (TEnum value in values)
            {
                _dic[index] = value;
                index++;
            }
        }

        public TEnum Current => _dic[_index.CurrentIndex];
        
        public void SetIndex(TEnum target)
        {
            _index.SetIndex(Convert.ToInt32(target));
        }

        public bool CanDecrement()
        {
            return _index.CanDecrement();
        }

		// 略---
    }
    
  **  intをつかったIndex管理**
    public class Index
    {
        public int CurrentIndex { get; private set; }
        public int MaxIndex { get; private set; }
        private readonly bool _loop;

        public Index(int maxIndex, bool loop = true, int currentIndex = 0)
        {
            CurrentIndex = currentIndex;
            MaxIndex = maxIndex;
            _loop = loop;
        }
        
        // Indexを1減らす
        public bool TryDecrement()
        {
            if (CurrentIndex - 1 >= 0)
            {
                CurrentIndex--;
            }
            else if (_loop)
            {
                CurrentIndex = MaxIndex;
            }
            else
            {
                // デクリメント失敗
                return false;
            }

            return true;
        }
        
        // 略---
  	}

これを使った処理の例

これで少しは共通化して書きやすくなりました.

    IMenu current => _stateDictionary[state];
    _titleMenuVerticalState = new EnumState<TitleMenuState>(loop: false);

	public async UniTask TryCangeState()
    {
        if (_titleMenuVerticalState.CanDecrement())
        {
            // 減らす前にステートを抜ける処理とか
            await _current.OnEndAsync(ct);

            _titleMenuVerticalState.TryDecrement();

            // 次のステートを開始
            await _current.OnStartAsync(ct);
        }
    }

おわりに

自分で用意したステート用のEnumを使って処理分けするのを共通化するのって意外と面倒くさいことをしないといけないんだなーと思いました.

あとはIncrementとかをasyncにして,変更前と後でActionを登録しておいて勝手に呼ばれるようなObserver的な処理を追加してもいいかもしれませんね。

この記事が良かったらチップを贈って支援しましょう!

チップを贈るにはユーザー登録が必要です。チップについてはこちら

最新の記事

記事のタグから探す

月別アーカイブ

限定特典から探す

記事を検索