クラスとしての状態
CoddyのUnityジャーニー「Unityのゲームアーキテクチャ」セクションの一部。レッスン 35/64。
セクション3のenumを使ったswitchでは、すべての状態の処理を1つのUpdateにまとめています。しかし、状態がそれぞれ独自のデータを持つようになると、この方法では拡張が難しくなります。1つの状態だけが使うタイマーが、ほかのすべての状態のフィールドと並んでMonoBehaviourのフィールドになり、各caseで使うデータを忘れずにリセットしなければなりません。C#のステートパターンでは、それぞれの状態に専用のクラスを用意し、すべてのクラスに1つのインターフェースを実装させます。この道路標識灯は点滅するか点灯し続け、Spaceキーで2つの状態を切り替えます。
public interface IState
{
void Enter();
void Tick();
void Exit();
}
public class BlinkState : IState
{
private Renderer lamp;
private float timer;
public BlinkState(Renderer lamp)
{
this.lamp = lamp;
}
public void Enter() { timer = 0f; }
public void Tick()
{
timer += Time.deltaTime;
if (timer >= 0.5f)
{
timer = 0f;
lamp.enabled = !lamp.enabled;
}
}
public void Exit() { lamp.enabled = true; }
}
public class SteadyState : IState
{
public void Enter() { }
public void Tick() { }
public void Exit() { }
}
public class Beacon : MonoBehaviour
{
public Renderer lamp;
private IState blink;
private IState steady;
private IState current;
void Start()
{
blink = new BlinkState(lamp);
steady = new SteadyState();
SetState(steady);
}
void Update()
{
if (Keyboard.current.spaceKey.wasPressedThisFrame) SetState(current == steady ? blink : steady);
current.Tick();
}
void SetState(IState next)
{
if (current != null) current.Exit();
current = next;
current.Enter();
}
}状態はMonoBehaviourではなく、通常のC#クラスです。newで作成し、コンストラクターを通して必要なものを渡します。ここではランプのRendererを渡しています。これらはコンポーネントではないため、GetComponentを呼び出したり、Inspectorにフィールドを表示したりできません。また、Unityがこれらのメソッドを呼び出すこともありません。MonoBehaviourなのはBeaconなので、そのUpdateが毎フレームcurrent.Tick()を呼び出します。
timerはBlinkStateだけが持ち、点滅を始めるたびにEnterがその値を0に設定します。Exitは状態が終わるときに後片付けをします。ランプが消えている間にSpaceキーが押されることもありますが、Exitがランプを再び点灯させるため、SteadyStateは暗いランプの状態で始まりません。SetStateは古い状態のExitを実行してから、新しい状態のEnterを実行します。ゆっくり点滅する状態など、3つ目の状態を追加する場合はクラスをもう1つ作るだけで、BeaconのUpdateは変更する必要がありません。
チャレンジ
中級タレットのスキャン状態を記述しましょう。
IState.csで、void Enter();、void Tick();、void Exit();を持つpublic interface IStateを宣言します。TurretStates.csで、頭部を受け取るScanState : IStateを記述します。Enter:角度を0にし、頭部をColor.greenにします。Tick:頭部のローカルy角度を-45から45の間で、1秒あたり90度動かします。Turret:Startで状態を1つ開始し、Updateでそれを更新します。
「プレイ」を押すと、緑色の頭部が往復します。その後、「確認」を押します。
自分で試してみよう
using UnityEngine;
// ScanState
Unityのゲームアーキテクチャのすべてのレッスン
自分で練習してみよう: Unityプレイグラウンド