Unity_Lesson

コマンドパターン

定義

操作(コマンド)をオブジェクトとしてカプセル化し、操作の実行と操作の内容を分離することを目的としています。
これにより、操作の履歴を保持したり、操作を取り消したり、再実行したりすることが容易になります。


コマンドパターンの構成要素


使用例


利点


欠点

コマンドパターンの実装例

コマンドインターフェース

public interface ICommand {
    void Execute();
    void Undo();
}

具体的なコマンドクラス

using UnityEngine;

public class MoveUpCommand : ICommand {
    private Transform player;
    private Vector3 previousPosition;

    public MoveUpCommand(Transform player) {
        this.player = player;
    }

    public void Execute() {
        previousPosition = player.position;
        player.position += Vector3.up;
    }

    public void Undo() {
        player.position = previousPosition;
    }
}

public class MoveDownCommand : ICommand {
    private Transform player;
    private Vector3 previousPosition;

    public MoveDownCommand(Transform player) {
        this.player = player;
    }

    public void Execute() {
        previousPosition = player.position;
        player.position += Vector3.down;
    }

    public void Undo() {
        player.position = previousPosition;
    }
}

コマンドの使用例

using UnityEngine;
using System.Collections.Generic;

public class GameManager : MonoBehaviour {
    private Stack<ICommand> commandHistory = new Stack<ICommand>();

    public Transform player;

    void Update() {
        if (Input.GetKeyDown(KeyCode.W)) {
            ICommand moveUp = new MoveUpCommand(player);
            moveUp.Execute();
            commandHistory.Push(moveUp);
        }

        if (Input.GetKeyDown(KeyCode.S)) {
            ICommand moveDown = new MoveDownCommand(player);
            moveDown.Execute();
            commandHistory.Push(moveDown);
        }

        if (Input.GetKeyDown(KeyCode.Z) && commandHistory.Count > 0) {
            ICommand lastCommand = commandHistory.Pop();
            lastCommand.Undo();
        }
    }
}





ちょっとだけ変えたプログラム1




MoveUpCommand と MoveDownCommand の内容が重複してるので1つにまとめてみた


public interface ICommand {
    void Execute();
    void Undo();
}
using UnityEngine;

public class MoveCommand : ICommand {
    private Transform player;
    private Vector3 direction;
    private Vector3 previousPosition;

    public MoveCommand(Transform player, Vector3 direction) {
        this.player = player;
        this.direction = direction;
    }

    public void Execute() {
        previousPosition = player.position;
        player.position += direction;
    }

    public void Undo() {
        player.position = previousPosition;
    }
}
using UnityEngine;
using System.Collections.Generic;

public class GameManager : MonoBehaviour {
    private Stack<ICommand> commandHistory = new Stack<ICommand>();

    public Transform player;

    void Update() {
        if (Input.GetKeyDown(KeyCode.W)) {
            ExecuteCommand(new MoveCommand(player, Vector3.up));
        }

        if (Input.GetKeyDown(KeyCode.S)) {
            ExecuteCommand(new MoveCommand(player, Vector3.down));
        }

        if (Input.GetKeyDown(KeyCode.A)) {
            ExecuteCommand(new MoveCommand(player, Vector3.left));
        }

        if (Input.GetKeyDown(KeyCode.D)) {
            ExecuteCommand(new MoveCommand(player, Vector3.right));
        }

        if (Input.GetKeyDown(KeyCode.Z) && commandHistory.Count > 0) {
            UndoLastCommand();
        }
    }

    void ExecuteCommand(ICommand command) {
        command.Execute();
        commandHistory.Push(command);
    }

    void UndoLastCommand() {
        ICommand lastCommand = commandHistory.Pop();
        lastCommand.Undo();
    }
}





ちょっとだけ変えたプログラム2



コマンドの管理を分離してみた




コマンドインターフェース

public interface ICommand {
    void Execute();
    void Undo();
}


具体的なコマンドクラス:

using UnityEngine;

public class MoveCommand : ICommand {
    private Transform _transform;
    private Vector3 _position;
    private Vector3 _previousPosition;

    public MoveCommand(Transform transform, Vector3 position) {
        _transform = transform;
        _position = position;
    }

    public void Execute() {
        _previousPosition = _transform.position;
        _transform.position = _position;
    }

    public void Undo() {
        _transform.position = _previousPosition;
    }
}


インボーカークラス:

using System.Collections.Generic;

public class CommandInvoker {
    private Stack<ICommand> _commandHistory = new Stack<ICommand>();

    public void ExecuteCommand(ICommand command) {
        command.Execute();
        _commandHistory.Push(command);
    }

    public void Undo() {
        if (_commandHistory.Count > 0) {
            var command = _commandHistory.Pop();
            command.Undo();
        }
    }
}


使用例:

using UnityEngine;

public class GameManager : MonoBehaviour {
    public Transform player;
    private CommandInvoker _invoker;

    void Start() {
        _invoker = new CommandInvoker();
    }

    void Update() {
        if (Input.GetKeyDown(KeyCode.W)) {
            MovePlayer(new Vector3(0, 0, 1));
        } else if (Input.GetKeyDown(KeyCode.S)) {
            MovePlayer(new Vector3(0, 0, -1));
        } else if (Input.GetKeyDown(KeyCode.Z)) {
            _invoker.Undo();
        }
    }

    void MovePlayer(Vector3 direction) {
        var moveCommand = new MoveCommand(player, player.position + direction);
        _invoker.ExecuteCommand(moveCommand);
    }
}