画面などが一通り出来上がったと思います。ここではさっそくステートパターンのスクリプトを作成してみましょう。
ステートパターンの出来ることを知る
プログラムを書く前にステートパターンの簡単な仕組みややれることを知っておきましょう。まずは座学
各ステート(状態)の移り変わりで起こる事
ここでは3つの状態が存在し、それぞれの移り変わりが起こる際にどのようなことが起こるかを簡単に説明します。
次に、これらの状態が何もない状態→状態A→状態B→状態Cと遷移した場合、どのようなことが起こるかを下図に表します。
上図の中では、青い囲みと赤い囲みが状態Aと状態Bに滞在しているときの動きですがそれら以外の部分の切り替わりに注目してください。AからBに状態が遷移する際、状態Aの終了処理が行われ、状態Bの開始処理が行われています。状態が切り替わる際には直前状態の終了処理を行って、次状態の開始処理を行うということが起こります。開始・終了処理があることで、実行中に欲しい情報などを事前に準備片付けを行うことが出来るようになっています。
フロー | 処理タイミング | 補足 |
---|---|---|
OnEnterState | 開始時に1回 | 主にOnUpdateで使う変数などの初期化を行う IEnumlatorなどを組み合わせることでOnUpdateをカットすることも出来る |
OnUpdate | 滞在中毎フレーム | 毎フレーム呼ばれます 通常のアップデートと同じタイミングで呼ばれる |
OnExitState | 状態の切り替え時1回 | 別の状態に遷移するタイミングで呼ばれる 今回は入力処理などの切断などを行います |
各状態をまとめる管理者がいる
各状態(ステート)ごとに出入りの際、対応した処理が実行されることはわかりました。
ステートパターンはこの形式を実現するために各状態を管理するためのクラスが必要になります。そのため、ざっくりな考え方としては下図の様になります。
基本的に各状態同士の繋がりはほとんどなく、各状態の必要な処理は管理者であるStateMachine側から呼び出されます。
基本スクリプトを編集、簡単なテスト
では、実際にスクリプトを作ってから簡単なステートパターンを体験してみましょう。
ステートパターン用のスクリプト編集
ステートパターンに必要なスクリプトは2つ。作ったクラスを継承して利用します。
using UnityEngine;
public class StateBase<T> where T : StateMachineBase<T>
{
protected T machine;
public StateBase(T _machine) { machine = _machine; }
public virtual void OnEnterState(){ }
public virtual void OnUpdate(){ }
public virtual void OnExitState() { }
}
using UnityEngine;
public class StateMachineBase<T> : MonoBehaviour where T :StateMachineBase<T>
{
private StateBase<T> m_currentState;
private StateBase<T> m_nextState;
public bool ChangeState(StateBase<T> _nextState)
{
bool bRet = m_nextState == null;
m_nextState = _nextState;
return bRet;
}
private void Update()
{
if(m_nextState != null)
{
if (m_currentState != null)
{
m_currentState.OnExitState();
}
m_currentState = m_nextState;
m_currentState.OnEnterState();
m_nextState = null;
}
if (m_currentState != null)
{
m_currentState.OnUpdate();
}
}
}
試し打ち。実際にステートパターンを使ってみる
上記スクリプトを用意したら、ステートパターンをすぐに使うことが出来ます。今回はデバッグログを利用して、ステートパターンが使えるようになったのを確認してみたいと思います。テスト用の処理は以下要件を満たすものとします。
- 状態は「Neutral/Up/Left」の3つ
- 各状態のOnEnterStateでステート名の入ったログを出力
- はじめはニュートラルから開始
- ニュートラル状態から上を押すとUp状態へ遷移
- ニュートラル状態から左を押すとLeft状態へ遷移
- Up状態でEscapeを押すとニュートラル状態に戻る
- Left状態でEscapeを押すとニュートラル状態に戻る
さぁ、自分でこういったプログラムを作ろうと思ったらどんな風になるでしょうか?スクリプトファイル名は「TestStateMachine」として下記スクリプトを作ってください。
using UnityEngine;
public class TestStateMachine : StateMachineBase<TestStateMachine>
{
private void Start()
{
ChangeState(new TestStateMachine.Neutral(this));
}
private class Neutral : StateBase<TestStateMachine>
{
public Neutral(TestStateMachine _machine) : base(_machine)
{
}
public override void OnEnterState()
{
Debug.Log("<color=red>OnEnter:Neutral!</color>");
}
public override void OnUpdate()
{
if (Input.GetKeyDown(KeyCode.UpArrow))
{
machine.ChangeState(new TestStateMachine.Up(machine));
}
if (Input.GetKeyDown(KeyCode.LeftArrow))
{
machine.ChangeState(new TestStateMachine.Left(machine));
}
}
}
private class Up : StateBase<TestStateMachine>
{
public Up(TestStateMachine _machine) : base(_machine)
{
}
public override void OnEnterState()
{
Debug.Log("<color=blue>OnEnter:Up!</color>");
}
public override void OnUpdate()
{
if (Input.GetKeyDown(KeyCode.Escape))
{
machine.ChangeState(new TestStateMachine.Neutral(machine));
}
}
}
private class Left : StateBase<TestStateMachine>
{
public Left(TestStateMachine _machine) : base(_machine)
{
}
public override void OnEnterState()
{
Debug.Log("<color=purple>OnEnter:Left!</color>");
}
public override void OnUpdate()
{
if (Input.GetKeyDown(KeyCode.Escape))
{
machine.ChangeState(new TestStateMachine.Neutral(machine));
}
}
}
}
空のゲームオブジェクトを用意して上記スクリプトを貼り付けた状態でゲームを動かしてください。あとは上左エスケープを押して、狙った通りの状態に遷移することを確認してみてください。もちろんUp状態から何をやってもLeftに移動しないことも確認すると良いですね
ステートパターンの基本はだいたい理解出来ましたでしょうか。次は各ボタン類のスクリプトとセットアップを行いましょう。
コメント