＜自作＞勉強でStateMachineを制作してみた

初めての投稿です！様々な意見を下さるととてもうれしいです！
今回は現在Unityを使用してゲーム制作をしているのですが、それで自分で制作したStateMachineを公開したいと思います！
プログラム歴約2年ほどですが、いろんな人に参考にしていただけると嬉しいです！

コードはGitHubにも公開しているので良ければ見てみてください！
[StateMachine](https://github.com/ryota0303F/StateMachine)

###IState

```
public interface IState
{
    //始めに呼ばれる関数
    StateTagList Enter(MonoBehaviour mono);
    //毎フレーム呼ばれる関数
    StateTagList Execute(MonoBehaviour mono);
    //消去時に呼ばれる関数
    StateTagList Exit(MonoBehaviour mono);
}
```
interfaceでStateの抽象クラスを制作しました。
これを継承していろいろなStateを制作していきます。
戻り値をenumのtagにしており戻り値によってStateを変更するようにしています。
###StateTagList
```
//タグと一緒にStateを追加する
public enum StateTagList
{
    none,
    move,
    move1,
}
```
enumでtagを制作しました。
Stateをリスト化してゲーム初めにすべてのStateをインスタンスしてtagと一緒に管理してやるために制作しました。
Stateの変更がないときはnoneを返すようにします。

###IStateを継承して作ったクラス
```
public class move : IState
{
    GameObject gameObject = null;
    public StateTagList Enter(MonoBehaviour mono)
    {
        gameObject = mono.gameObject;
        return StateTagList.none;
    }
    Vector3 v3 = new Vector3(0.05f, 0, 0);
    public StateTagList Execute(MonoBehaviour mono)
    {
        gameObject.transform.position += v3;
        if (Input.GetKeyDown(KeyCode.C))
        {
            return StateTagList.move1;
        }
        return StateTagList.none;
    }

    public StateTagList Exit(MonoBehaviour mono)
    {
        return StateTagList.none;
    }
}

public class move1 : IState
{
    GameObject gameObject = null;
    public StateTagList Enter(MonoBehaviour mono)
    {
        gameObject = mono.gameObject;
        return StateTagList.none;
    }
    Vector3 v3 = new Vector3(-0.05f, 0, 0);
    public StateTagList Execute(MonoBehaviour mono)
    {
        gameObject.transform.position += v3;
        if (Input.GetKeyDown(KeyCode.C))
        {
            return StateTagList.move;
        }
        return StateTagList.none;
    }

    public StateTagList Exit(MonoBehaviour mono)
    {
        return StateTagList.none;
    }
}


```
次はIStateを継承して作った子クラスです。
中身は今回はテスト用ということで”C”を押すと移動が左右切り替わるだけのシンプルなものにしています。


###StateList
```
public class StateList
{
    //Dictionaryクラスのオブジェクト生成
    private Dictionary<StateTagList, IState> statelist = new Dictionary<StateTagList, IState>();

    //この関数でStateリストの追加
    public void AddState(StateTagList _tag,IState _state)
    {
        statelist.Add(_tag, _state);
    }

    //ここにタグを引数として入れればそのStateがかえってくる
    public IState GetState(StateTagList _tag)
    {
        return statelist[_tag];
    }

}
```
次に早速enumで作ったtagを使ってリスト化していきます。
管理方法はDictionaryクラスを使用していきます。
私は今回初めてDictionaryクラスを使ったのですが、とても便利でした！！
今までC++でネイティブ開発をしていたのですが、正直std::mapより使いやすくて好きです！！

###StateMachine
```
public class StateMachine
{
    private IState state = null;

    private StateList stateList = new StateList();

    public StateList GetStateList() { return stateList; }

    public IState GetState() { return state; }

    //Stateを変更する際に通す関数
    //自動でEnterとExitに通るようになっている
    public void ChangeState(StateTagList _tag,MonoBehaviour _mono)
    {

        IState _state = stateList.Getstate(_tag);

        state?.Exit(_mono);
        state = _state;
        state?.Enter(_mono);
    }

    //StateMachineを持たせているscriptのUpdateでこの関数を回してやる
    //引数に自分のGameObjectを持たせてやる
    public void Update(MonoBehaviour _mono)
    {
        if(state != null)
        {
            StateTagList _tag = state.Execute(_mono);   
            if (_tag != StateTagList.none) 
            {
                ChangeState(_tag, _mono);
            }
        }
    }

}

```
最後に本題のStateMachineです。中身としてはStateの切り替えやStateの管理などをしています。
またStateを切り替えるとEnterとExitをに入るようにしています。
最近知ったのですが、State?.~~~~とすることでnull回避ができると聞いてとても便利だと感じました！！

###使用方法
```
public class EnemyController : MonoBehaviour 
{
    StateMachine state = new StateMachine();
    void Start()
    {
        state.GetStateList().AddState(StateTagList.move, new move());
        state.GetStateList().AddState(StateTagList.move1, new move1());
        state.ChangeState(StateTagList.move1, this);
    }

    void Update()
    {
        state.Update(this);

    }
}
```
使用方法はシンプルです。StateMachineを変数宣言して初めに使うStateをすべてインスタンスしてListに入れてやります。そして初めに使うStateをChangeStateでセットしてやります。
最後にUpdateで自分自身を引数に入れてUpdateを回してやるだけです！


###最後に
今回初めて記事を書いたのですがどうでしょうか？
今回制作したStateMachineはもっと改良すればもっといいものになると思うので今後も改良してみたいと思います。またいろいろな意見を下さるととてもうれしいです！！
最後まで見て下さりありがとうございます！！



###変更
IStateのすべての関数の引数をtemplateにして汎用性を高めました。
変更プログラムはこちらから　
[StateMachine Ver1.1](https://github.com/ryota0303F/StateMachine-ver1.1)