操作(コマンド)をオブジェクトとしてカプセル化し、操作の実行と操作の内容を分離することを目的としています。
これにより、操作の履歴を保持したり、操作を取り消したり、再実行したりすることが容易になります。
コマンドインターフェース(ICommand):
コマンドオブジェクトが実装すべきインターフェースを定義します。一般的には、Executeメソッドを持ちます。
具体的なコマンドクラス(ConcreteCommand):
コマンドインターフェースを実装し、特定のアクションを実行します。
レシーバー(Receiver):
コマンドが実行される対象オブジェクトです。
インボーカー(Invoker):
コマンドを呼び出す役割を持ちます。コマンドオブジェクトを保持し、適切なタイミングでExecuteメソッドを呼び出します。
クライアント(Client):
コマンド、レシーバー、インボーカーを構成して操作を実行するクラスです。
コマンドインターフェース
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();
}
}
}
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();
}
}
コマンドの管理を分離してみた
コマンドインターフェース
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);
}
}