ステートマシン
CoddyのUnityジャーニー「Unityにおけるゲームアーキテクチャ」セクションの一部。レッスン 29/50。
ビーコンのSetStateメソッドは、砲塔、敵、ドアなど、状態を持つすべてのオブジェクトで同じになります。このコードは、現在の状態を保持して切り替えるステートマシンという独立したクラスに置けます。各オブジェクトはコードを繰り返す代わりに、ステートマシンを1つ作って呼び出します。
public class StateMachine
{
public IState Current { get; private set; }
public void ChangeState(IState next)
{
Current?.Exit();
Current = next;
Current.Enter();
}
public void Tick()
{
Current?.Tick();
}
}
public class Beacon : MonoBehaviour
{
public Renderer lamp;
private StateMachine machine = new StateMachine();
private IState blink;
private IState steady;
void Start()
{
blink = new BlinkState(lamp);
steady = new SteadyState();
machine.ChangeState(steady);
}
void Update()
{
if (Keyboard.current.spaceKey.wasPressedThisFrame) machine.ChangeState(machine.Current == steady ? blink : steady);
machine.Tick();
}
}最初のChangeStateでは、終了する状態はありません。Currentはnullで、?.はExitの呼び出しをスキップします。セクション1では、?.はUnityの擬似nullを見落とすと説明しました。これは破棄されたUnityオブジェクトについての話で、状態は通常のC#オブジェクトなので、ここで?.を使っても安全です。Currentにはprivate setがあるため、どのスクリプトもChangeStateとそのExit、Enterを通さずに状態を切り替えることはできません。
2つの状態が同じものに作用する場合、ChangeState内の順序が重要です。Exitで古い状態による変更を元に戻してから、Enterで新しい状態を設定するので、新しい状態が最後に反映されます。DarkStateのEnterがランプを消灯し、BlinkStateのExitより先に実行された場合、そのExitによってランプが再び点灯し、暗い状態ではなくなってしまいます。StateMachineも通常のクラスです。ビーコン、砲塔、敵はそれぞれnewで独自のインスタンスを作り、それぞれのUpdateからそのTickを呼び出します。
チャレンジ
中級Spaceキーを押したままにすると、しゃがみます。WalkState と SneakState は PlayerStates.cs に用意されています。それぞれの Enter と Exit は何が起きたかをログに出力し(Walk: enter、Sneak: exit...)、Tick はプレイヤーの Move() を呼び出します。それらを実行するマシンを書きましょう。
StateMachine.csに、通常のクラスStateMachineを書きます。このクラスには、マシンだけが設定できる public なCurrentプロパティ、現在の状態の Exit を実行してから新しい状態の Enter を実行するChangeState(IState next)メソッド、そして現在の状態の Tick を実行するTick()メソッドを定義します。- プレイヤーは毎フレーム自身の状態を確認するため、すでに現在の状態になっている状態を指定して
ChangeStateを呼び出した場合は何もしません。Exit も Enter も実行しません。 Player.csに、machineという名前のStateMachineフィールドを追加します。Start でマシンと両方の状態(それぞれプレイヤーを受け取ります)を作成し、歩行を開始します。Update では、Spaceキーを押している間はしゃがむ状態に切り替え、そうでなければ歩行状態に切り替えてから、マシンの Tick を呼び出します。
Play を押し、歩き、Spaceキーを押したまま歩き続けてから、Check を押してください。
自分で試してみよう
// StateMachine: Current、ChangeState、Tick
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。