ステートマシンの使い方

# この記事について
この記事は、現在チーム開発を行っているチームメンバー向けに書いた内容となっていますので予めご了承ください。

# 作成するスクリプト
３つのスクリプトを作成して下さい。
スクリプト名は担当箇所＋末尾が名詞単体もしくはerである事。

・管理クラス　　（Manager,Controllerなど）
・登録クラス　　（Register）
・ステートクラス（Move,Rotationなど　※今回は２つ作ります
```C#
// 管理クラス
public class HamsterController : MonoBehaviour
{
    private void Start()
    {
    
    }

    private void Update()
    {
    
    }
}

// 登録クラス
public class HamsterComponentRegister : MonoBehavior
{
    private void Start()
    {
    
    }

    private void Update()
    {
    
    }
}

// ステートクラスはStateを継承します
public class HamsterIdler : State
{
    // overrideを使うとStateクラスのメソッドを利用できます
    public override void OnEnter()
    {

    }

    public override void OnExit()
    {

    }

    public override void OnUpdate()
    {

    }
}

public class HamsterMover : State
{
    // 略
}
```

# 管理クラス

・管理クラスにStateMachineBaseの変数を追加します。
```C#
public class HamsterController : MonoBehaviour
{
    // Stateを継承したベースクラス
    private StateMachineBase<State> stateMachineBase;
    
    // 略
}
```

・ロケーターからベースクラスを取得して初期化を行います
```C#
public class HamsterController : MonoBehaviour
{
    // 略

    private void Start()
    {
        // ロケーターからベースクラスを取得する
        stateMachineBase = Locator<StateMachineBase<State>>.GetInstance();
        InitStateMachineBase();
    }

    // 略
    
    // StateMahcineBaseを初期化する
    private void InitStateMachineBase()
    {
        // LocatorからIdleスクリプトを取得する
        var idle = Locator<Idle>.GetInstance();

        // ハムスターをidleステートに初期化する
        stateMachineBase.InitState(idle);
    }
}
```
これでステートマシンの初期化をする事が出来ました。
IdlerクラスのOnEnterメソッドにDebug.Logを書いて実行すると開始時にDebug.Logが呼ばれている事が分かると思います。

・StateMachineBaseのUpdateメソッドを追加します
```C#
public class HamsterController : MonoBehaviour
{
    // 略
    
    private void Update()
    {
        // StateMachineBaseのOnUpdateを実行する
        stateMachineBase.OnUpdate();
    }

    // 略
}
```
これでステートのUpdateを行えるようになりました。
最初に、stateMachineBase.InitState(HamsterIdler);でIdlerステートに初期化をしているので、
現在はIdlerクラスのOnUpdate処理が実行され続けています。

・ここまでの内容を実装した管理クラス
```C#
public class HamsterController : MonoBehaviour
{
    // Stateを継承したベースクラス
    private StateMachineBase<State> stateMachineBase;

    private void Start()
    {
        // ロケーターからベースクラスを取得する
        stateMachineBase = Locator<StateMachineBase<State>>.GetInstance();
        InitStateMachineBase();
    }

    private void Update()
    {
        // ステートのUpdateを実行する
        // 最初にidleステートに初期化したので、idleのUpdateが実行される
        stateMachineBase.OnUpdate();
    }

    // ステートマシンを初期化する
    private void InitStateMachineBase()
    {
        // LocatorからIdleスクリプトを取得する
        var idle = Locator<Idle>.GetInstance();

        // ハムスターをidleステートに初期化する
        stateMachineBase.InitState(idle);
    }
}
```

# 登録クラス
登録クラスではUnityコンポーネントを取得したり、ステートクラスに必要なスクリプトの取得を行います。

```C#
using StateMachine;
using UnityEngine;
using Utility;

public class HamsterComponentRegister : MonoBehavior
{
    private StateMachineBase<State> stateMachineBase = new StateMachineBase<State>();
    private Idle idle = new Idle();
    private Move move = new Move();
    
    private void Awake()
    {
        // ロケーターにスクリプトを登録する
        // ロケーターの引き数には(登録したい物, 登録番号)を設定する事が出来ます
        // １番はプレイヤー、２番はエネミーと登録する場所を分けて管理出来ます
        // どの番号を使うかは各自話し合ってください
        
        // Locatorに登録する
        Locator<StateMachineBase<State>>.Register(stateMachineBase);
        Locator<Idle>.Register(idle);
        Locator<Move>.Register(move);
    }
}

```

# ステートクラス
コードの内容を追加して下さい。
```C#
using StateMachine;
using UnityEngine;

// ステートクラスはStateを継承します
public class Idle : State
{
    private StateMachineBase<State> stateMachineBase;
    private Move move;
    private bool isInit = false;
    
    public override void Init()
    {
        if (!isInit)
        {
            stateMachineBase = Locator<StateMachineBase<State>>.GetInstance();
            move = Locator<Move>.GetInstance();
            AddTransition();
            isInit = true;
        }
    }


    public override void OnEnter()
    {
        Init();
        Debug.Log("立ち止まった");
    }

    public override void OnExit()
    {
        Debug.Log("立ち止まるのをやめた");
    }

    public override void OnUpdate()
    {
    
    }

    private void AddTransition()
    {
        stateMachineBase.AddTransition(this, move);
    }
}

```

・ベース変数の遷移メソッドを実行する
```C#
// ステートクラスはStateを継承します
public class HamsterIdle : State
{
    // 略
    
    public override void OnUpdate()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            stateMachineBase.TransitionTo(this, move);
        }
    }

    // 略
}
```
Moverクラスにはこれの反対条件を追加してステートの動きを確認してください。
問題なくステートが切り替わればこれで全ての実装は終了となります。

# 複数のステートを実装したい時
複数のステートを同時に行い時もあると思います。アクションゲームで例えると移動しながらジャンプするなどの**ステート＋ステート**を同時に行いたい時です。

そのような時はステートクラスと行いたい処理のクラスに分けてステートクラスで実装すると解決出来ます。

```C#
public class PlayerStateMove : State
{
    // Stateを継承したベースクラス
    private StateMachineBase<State> stateMachineBase;
    
    // 移動だけを行うクラス
    private State playerMove;

    // 回転だけを行うクラス
    private State playerRotation;
    
    private bool isInit = false;
  
    public override void OnEnter()
    {
        Init();
    }

    public override void OnExit()
    {

    }

    public override void OnUpdate()
    {
        playerMove.OnUpdate();
        playerRotation.OnUpdate();
    }

    public override void Init()
    {
        if(!isInit)
        {
            // ロケーターからスクリプトを取得する
            stateMachineBase = Locator<StateMachineBase<State>>.GetInstance();
            playerMove = Locator<PlayerMover>.GetInstance();
            playerRotation = Locator<PlayerRotation>.GetInstance();
            
            // コンポーネントの初期化処理を行う
            // やる事はこのクラスと同じです
            playerMove.Init();
            playerRotation.Init();
            isInit = true;
        }
    }
}
```
このように実装する事で、PlayerStateMoveでは移動と回転処理を実行するという内容になりました。複数行いたいステートがある時はこのようにまとめるステートクラスを作ると解決出来ます。