Command 패턴은 요청을 객체로 캡슐화하여, 명령의 실행, 취소, 재실행 등을 유연하게 처리 할 수 있게 해주는 디자인 패턴.
🎮 게임에서의 활용 예시
| 활용 상황 | 설명 |
|---|---|
| 플레이어 이동 | 방향키 입력을 명령으로 만들고, 이동을 명령 객체로 실행 |
| 실행 취소 / 다시 실행 | 이전 위치로 되돌아가는 Undo 기능 구현 |
| 키 바인딩 시스템 | Space → Alt 키처럼 키를 자유롭게 바꿔도, 행동 로직은 재사용 |
| 튜토리얼 리플레이 | 명령 기록을 저장해 재생하면 행동을 그대로 보여줄 수 있음 |
Unity에서 Command 패턴을 적용해
격자 기반 타일 맵에서 플레이어를 이동시키고,
되돌리기(Z), 재실행(Y) 기능을 구현해보았다.
Z 키로 되돌리기, Y 키로 다시 실행할 수 있음public interface ICommand
{
void Execute();
void Undo();
}
// 플레이어를 격자 형태로 이동시키는 명령
public class MoveCommand : ICommand
{
private Transform player; // 움직일 오브젝트
private Vector2Int from, to; // 이동 전 / 후 위치
private GridManager gridManager; // 격자 관리
public MoveCommand(Transform player, Vector2Int from, Vector2Int to, GridManager gridManager)
{
this.player = player;
this.from = from;
this.to = to;
this.gridManager = gridManager;
}
public void Execute()
{
// 이동 실행 목표위치로 이동
player.position = gridManager.GetWorldPosition(to);
}
public void Undo()
{
// 이전 위치로 되돌리기
player.position = gridManager.GetWorldPosition(from);
}
}
public class CommandInvoker
{
private Stack<ICommand> undoStack = new Stack<ICommand>();
private Stack<ICommand> redoStack = new Stack<ICommand>();
// 명령 실행
public void ExecuteCommand(ICommand command)
{
command.Execute();
undoStack.Push(command);
redoStack.Clear(); // 새로운 명령 실행 시 Redo 초기화
}
// 되돌리기
public void Undo()
{
if (undoStack.Count > 0)
{
var command = undoStack.Pop(); // 이전 명령 위치 가져오기
command.Undo(); // 되돌리기
redoStack.Push(command); // 이번 명령 다시 실행 스택에 추가
}
}
// 다시 실행
public void Redo()
{
if (redoStack.Count > 0)
{
var command = redoStack.Pop(); // 취소 시켰던 명령 가져오기
command.Execute(); // 다시 실행
undoStack.Push(command); // 되돌리기 스택에 추가
}
}
}
public class PlayerController : MonoBehaviour
{
public Text inputKeyText; // 입력키 확인 용 UI
public GridManager gridManager;
public Vector2Int currentGridPos = new Vector2Int(0, 0);
private CommandInvoker invoker = new CommandInvoker();
void Update()
{
// 키 입력 감지(WASD)
Vector2Int move = Vector2Int.zero;
if (Input.GetKeyDown(KeyCode.W)){ move = Vector2Int.up; inputKeyText.text = "현재 입력 키: W";}
if (Input.GetKeyDown(KeyCode.S)){ move = Vector2Int.down; inputKeyText.text = "현재 입력 키: S";}
if (Input.GetKeyDown(KeyCode.A)){ move = Vector2Int.left; inputKeyText.text = "현재 입력 키: A";}
if (Input.GetKeyDown(KeyCode.D)){ move = Vector2Int.right; inputKeyText.text = "현재 입력 키: D";}
// 키 입력이 감지되면
if (move != Vector2Int.zero)
{
Vector2Int target = currentGridPos + move;
// 이동 명령 생성 후 실행
ICommand moveCommand = new MoveCommand(transform, currentGridPos, target, gridManager);
invoker.ExecuteCommand(moveCommand);
currentGridPos = target;
}
// Z -> 되돌리기(Undo) , Y -> 다시실행(Redo)
if (Input.GetKeyDown(KeyCode.Z)) {invoker.Undo(); inputKeyText.text = "현재 입력 키: Z";}
if (Input.GetKeyDown(KeyCode.Y)) {invoker.Redo(); inputKeyText.text = "현재 입력 키: Y";}
}
}
