【Winform】中級:プレイヤーの動きを完全にコントロール: コマンドとメメントパターンを使った状態管理

この資料では、C#でコマンドパターンとメメントパターンを組み合わせて、操作の取り消し機能(Undo)、再実行機能(Redo)、そして特定の状態に自由に戻る機能を実装する方法を解説します。これにより、プレイヤーの動きを完全にコントロールし、履歴管理と状態遷移を柔軟に操作できるシステムを構築します。

コマンドパターンとメメントパターンの基礎

コマンドパターン は、操作(コマンド)をオブジェクトとしてカプセル化し、その操作を実行するタイミングや方法を柔軟に変更できるようにするデザインパターンです。各操作は独立したクラスとして定義され、操作対象(例: ゲーム内のプレイヤー)に対して特定のアクションを実行します。

メメントパターン は、オブジェクトの状態をスナップショット(メメント)として保存し、その状態を後で復元できるようにするパターンです。このパターンを利用することで、過去の特定の状態に戻ることが可能になります。

取り消し・再実行・状態遷移の実装手順

1. メメントクラスの定義

プレイヤーの状態を保存するためのメメントクラスを作成します。このクラスは、プレイヤーの状態(位置情報など)をキャプチャし、保存する役割を担います。

public class PlayerMemento
{
    public int PositionX { get; }
    public int PositionY { get; }

    public PlayerMemento(int x, int y)
    {
        PositionX = x;
        PositionY = y;
    }
}

2. プレイヤークラスにメメント機能を追加

プレイヤークラスに、現在の状態を保存したり、過去の状態に戻したりするメソッドを追加します。

public class Player
{
    public int PositionX { get; private set; }
    public int PositionY { get; private set; }

    public Player(int startX, int startY)
    {
        PositionX = startX;
        PositionY = startY;
    }

    public void MoveForward() { PositionY += 1; }
    public void MoveBackward() { PositionY -= 1; }
    public void MoveLeft() { PositionX -= 1; }
    public void MoveRight() { PositionX += 1; }

    public PlayerMemento SaveState()
    {
        return new PlayerMemento(PositionX, PositionY);
    }

    public void RestoreState(PlayerMemento memento)
    {
        PositionX = memento.PositionX;
        PositionY = memento.PositionY;
    }
}

3. ゲームコントローラーで状態管理

コントローラークラスで、プレイヤーの状態を管理する機能を追加します。リストを使用してプレイヤーの状態を時間順に保存し、任意の時点に戻ることができるようにします。

public class GameController
{
    private readonly Player player;
    private readonly List<PlayerMemento> history = new List<PlayerMemento>();
    private int currentStateIndex = -1;

    public GameController(Player player)
    {
        this.player = player;
    }

    public void ExecuteCommand(ICommand command)
    {
        command.Execute();
        // 状態を保存
        SaveState();
    }

    private void SaveState()
    {
        var state = player.SaveState();
        if (currentStateIndex < history.Count - 1)
        {
            history.RemoveRange(currentStateIndex + 1, history.Count - currentStateIndex - 1);
        }
        history.Add(state);
        currentStateIndex++;
    }

    public void Undo()
    {
        if (currentStateIndex > 0)
        {
            currentStateIndex--;
            player.RestoreState(history[currentStateIndex]);
        }
    }

    public void Redo()
    {
        if (currentStateIndex < history.Count - 1)
        {
            currentStateIndex++;
            player.RestoreState(history[currentStateIndex]);
        }
    }

    public void RestoreToState(int index)
    {
        if (index >= 0 && index < history.Count)
        {
            currentStateIndex = index;
            player.RestoreState(history[currentStateIndex]);
        }
    }
}

4. WinFormでの実装

WinFormのボタンをクリックした際に、特定の状態に戻る、または進む機能を実装します。

public partial class MainForm : Form
{
    private GameController gameController;
    private Player player;

    public MainForm()
    {
        InitializeComponent();
        player = new Player(0, 0); // 初期位置を設定
        gameController = new GameController(player);
    }

    private void buttonMoveForward_Click(object sender, EventArgs e)
    {
        ICommand command = new MoveForwardCommand(player);
        gameController.ExecuteCommand(command);
    }

    private void buttonUndo_Click(object sender, EventArgs e)
    {
        gameController.Undo();
    }

    private void buttonRedo_Click(object sender, EventArgs e)
    {
        gameController.Redo();
    }

    private void buttonRestoreToSpecific_Click(object sender, EventArgs e)
    {
        int targetIndex = ... // 任意のインデックスを取得
        gameController.RestoreToState(targetIndex);
    }
}
</code>

まとめ

この実装では、コマンドパターンとメメントパターンを組み合わせることで、プレイヤーの状態を保存し、自由に過去の状態に戻ったり、進んだりすることができます。これにより、ユーザーは操作の取り消しや再実行を行うだけでなく、特定の時点の状態に戻ることができ、リプレイ機能や履歴管理機能を持つシステムにおいて非常に有用です。