-
유니티 쉽게 배우는 디자인 패턴 - 명령 패턴 / Unity Design Pattern - Command Pattern유니티 2024. 2. 7. 17:32
명령패턴이란?
실행될 동작을 클래스로 만들어 실행과 실행취소를 쉽게 만들어주는 패턴입니다.
Input에서 명령을 순차적으로 수행하거나
Command Buffer에서 그래픽을 버퍼에 순차적으로 그리거나
체스같은 그리드기반의 턴제 게임에서 실행취소가 용이하므로 사용되고 있습니다.
리플레이에서도 각 시간마다 입력 명령이 다르기에 사용됩니다.
명령패턴 반영 전
Robot.cs
using UnityEngine; [System.Serializable] public class Robot { public Vector2Int position; public Vector2Int direction; public Robot(Vector2Int position, Vector2Int direction) { this.position = position; this.direction = direction; } public void MoveForward() { position += direction; } public void TurnRight() { direction = new Vector2Int(direction.y, -direction.x); } public void TurnLeft() { direction = new Vector2Int(-direction.y, direction.x); } public void Print() { Debug.Log($"position: {position}, direction: {direction}"); } }
Test.cs
using UnityEngine; public class Test : MonoBehaviour { void Start() { Robot robot = new Robot(new Vector2Int(0, 0), Vector2Int.up); robot.MoveForward(); robot.TurnRight(); robot.MoveForward(); robot.TurnLeft(); robot.MoveForward(); robot.MoveForward(); robot.Print(); } }
로봇의 행동은 한 칸 전진, 오른쪽 회전, 왼쪽 회전으로 3가지입니다.
Test에서 로봇의 초기 위치와 초기 방향을 설정한 뒤 이동 혹은 회전 명령을 쭉 진행합니다.
하지만 이러한 명령이 되는 것에 대해 기록이 남지 않고 실행취소도 불가합니다.
명령패턴 반영 후
Robot.cs
using System.Collections.Generic; using UnityEngine; [System.Serializable] public class Robot { public Vector2Int position; public Vector2Int direction; public Robot(Vector2Int position, Vector2Int direction) { this.position = position; this.direction = direction; } public void MoveForward() { position += direction; } public void TurnRight() { direction = new Vector2Int(direction.y, -direction.x); } public void TurnLeft() { direction = new Vector2Int(-direction.y, direction.x); } public void Print() { Debug.Log($"position: {position}, direction: {direction}"); } } public abstract class RobotCommand { public Robot robot; public RobotCommand(Robot robot) { this.robot = robot; } public abstract void Execute(); public abstract void Undo(); } public class RobotCommandMoveForward : RobotCommand { public RobotCommandMoveForward(Robot robot) : base(robot) { } public override void Execute() { robot.MoveForward(); } public override void Undo() { robot.TurnRight(); robot.TurnRight(); robot.MoveForward(); robot.TurnRight(); robot.TurnRight(); } } public class RobotCommandTurnRight : RobotCommand { public RobotCommandTurnRight(Robot robot) : base(robot) { } public override void Execute() { robot.TurnRight(); } public override void Undo() { robot.TurnLeft(); } } public class RobotCommandTurnLeft : RobotCommand { public RobotCommandTurnLeft(Robot robot) : base(robot) { } public override void Execute() { robot.TurnLeft(); } public override void Undo() { robot.TurnRight(); } } public class RobotController { Stack<RobotCommand> history; public RobotController() { history = new(); } public void ExecuteCommand(RobotCommand robotCommand) { robotCommand.Execute(); history.Push(robotCommand); } public void Undo() { if (history.Count > 0) { RobotCommand command = history.Pop(); command.Undo(); } else { Debug.Log("Nothing to undo"); } } }
Test.cs
public class Test : MonoBehaviour { void Start() { Robot robot = new Robot(new Vector2Int(0, 0), Vector2Int.up); RobotController robotController = new RobotController(); robotController.ExecuteCommand(new RobotCommandMoveForward(robot)); robotController.ExecuteCommand(new RobotCommandTurnRight(robot)); robotController.ExecuteCommand(new RobotCommandMoveForward(robot)); robotController.ExecuteCommand(new RobotCommandTurnLeft(robot)); robotController.ExecuteCommand(new RobotCommandMoveForward(robot)); robotController.ExecuteCommand(new RobotCommandMoveForward(robot)); robotController.Undo(); robotController.Undo(); robot.Print(); } }
자 굉장히 방대해진거 같지만 사실 별거 아닙니다.
로봇의 행동은 한 칸 전진, 오른쪽 회전, 왼쪽 회전으로 3가지입니다.
이것을 RobotCommand를 상속받아 3가지 클래스로 만듦니다.
RobotCommand는 실행과 실행취소가 있으므로 각각 구현해줍니다.
RobotController는 로봇 행동을 Stack<RobotCommand> 기록하고 실행 및 실행취소 명령을 수행합니다.
실행될 때 스택에 들어가고 실행 취소될 때 스택에서 마지막에 넣은게 빠집니다.
마무리
행동을 기록할 수 있다는 점, 행동을 취소할 수 있다는 점이 매력적인 명령 패턴이었습니다.
'유니티' 카테고리의 다른 글