🎨
【Unity】デザインパターン State ゲーム管理編
ゲーム開発では、ゲームの状態(ステート)や振る舞いを管理することが必要になります。その際に役立つのがStateパターンです。
今回はStateパターンでのゲーム管理について、まとめています。
1. enum + switchによるシンプルな実装
状態をenumで定義し、それをswitch文で管理する方法です。状態が少ない場合や簡単な動作を記述する際に適しています。
実装例
public enum GameState
{
MainMenu,
Playing,
Paused
}
public class GameManager : MonoBehaviour
{
private GameState currentState = GameState.MainMenu;
// 他のマネージャーを参照
[SerializeField] private UIManager uiManager;
[SerializeField] private AnimationManager animationManager;
private void Start()
{
// 初期状態の設定
UpdateGameState(GameState.MainMenu);
}
private void Update()
{
// 状態ごとの処理
switch (currentState)
{
case GameState.MainMenu:
Debug.Log("Main Menu");
break;
case GameState.Playing:
Debug.Log("Playing");
break;
case GameState.Paused:
Debug.Log("Paused");
break;
}
// 状態変更の例
if (Input.GetKeyDown(KeyCode.M))
{
UpdateGameState(GameState.MainMenu);
}
}
// 状態を更新するメソッド
private void UpdateGameState(GameState newState)
{
if (currentState == newState) return; // 同じ状態には遷移しない
currentState = newState;
switch (currentState)
{
case GameState.MainMenu:
uiManager.ShowMainMenu();
animationManager.PlayMainMenuAnimation();
break;
case GameState.Playing:
uiManager.ShowGameplayUI();
animationManager.PlayGameplayAnimation();
break;
case GameState.Paused:
uiManager.ShowPauseMenu();
animationManager.PlayPauseAnimation();
break;
}
Debug.Log($"Game state updated to: {currentState}");
}
}
特徴
- シンプルで軽量。
- 状態が増えるとswitch文が肥大化しやすい。
2. クラスによるStateパターン
各状態を独立したクラスで管理する方法です。柔軟性が高く、状態ごとの処理を明確に分けられます。
State
public interface IState
{
void Enter(); // 状態に入る処理
void Update(); // 毎フレームの処理
void Exit(); // 状態を出る処理
}
public class MainMenuState : IState
{
private UIManager uiManager;
private AnimationManager animationManager;
public MainMenuState(UIManager uiManager, AnimationManager animationManager)
{
this.uiManager = uiManager;
this.animationManager = animationManager;
}
public void Enter()
{
uiManager.ShowMainMenu();
animationManager.PlayAnimation("MainMenu");
}
public void Update() => Debug.Log("Main Menu: Update");
public void Exit() => Debug.Log("Main Menu: Exit");
}
StateMachine
public class StateMachine
{
private IState currentState;
public void ChangeState(IState newState)
{
currentState?.Exit(); // 現在の状態を終了
currentState = newState;
currentState.Enter(); // 新しい状態を開始
}
public void Update()
{
currentState?.Update(); // 現在の状態の更新処理を呼び出す
}
}
GameManagerとAnimationManager
public class GameManager : MonoBehaviour
{
private StateMachine stateMachine;
private UIManager uiManager;
private AnimationManager animationManager;
private void Start()
{
uiManager = new UIManager();
animationManager = new AnimationManager();
// AnimationManagerに完了イベントを登録
animationManager.OnAnimationComplete += HandleAnimationComplete;
stateMachine = new StateMachine();
stateMachine.ChangeState(new MainMenuState(uiManager, animationManager));
}
private void Update()
{
stateMachine.Update();
}
// AnimationManagerのイベントハンドラ
private void HandleAnimationComplete()
{
Debug.Log("Animation completed. Transitioning to GameState.");
stateMachine.ChangeState(new GameState());
}
private void OnDestroy()
{
// イベントの解除
animationManager.OnAnimationComplete -= HandleAnimationComplete;
}
}
public class AnimationManager
{
// GameManagerにアニメーション完了を通知するイベント
public event Action OnAnimationComplete;
public void PlayAnimation()
{
Debug.Log("Playing animation...");
SimulateAnimationComplete();
}
private async void SimulateAnimationComplete()
{
await Task.Delay(2000);
Debug.Log("Animation finished.");
OnAnimationComplete?.Invoke(); // GameManagerにイベントを通知
}
}
特徴
- 各状態の処理を個別のクラスに分けられる。
- 状態の増加や変更に柔軟に対応可能。
- Stateクラスが他のManagerClassを利用する場合、それらを依存性注入の形でStateに渡す。
※依存性注入:クラスが必要とする依存関係(他のクラスやオブジェクト)を外部から注入する設計手法
まとめ
Stateパターンを使うと、オブジェクトの状態やゲーム全体の状態を整理し、柔軟で保守性の高いコードを構築できます。状態ごとに独立した処理を持たせることで、コードの見通しを良くし、状態が増えた場合でも既存のコードに影響を与えにくくなります。
Discussion