728x90
반응형
게임을 만들다 보면 이런 고민을 자주 하게 됩니다 👇
- 플레이어가 입력한 행동(이동, 공격, 점프 등)을 기록하거나 되돌리고 싶을 때
- AI, UI, 플레이어 모두 같은 방식으로 명령을 실행하게 만들고 싶을 때
- 입력 로직과 실제 행동 로직을 깔끔하게 분리하고 싶을 때
이럴 때 사용하기 좋은 디자인 패턴이 바로 커맨드(Command) 패턴입니다.
🧩 커맨드 패턴이란?
요청(행동)을 객체로 캡슐화해서, 실행·취소·재실행 등을 자유롭게 다룰 수 있게 하는 패턴
즉, “명령을 데이터처럼 다루는 방식”이에요.
- 버튼 클릭 → “공격 명령”
- 키보드 입력 → “이동 명령”
- AI의 의사결정 → “점프 명령”
이런 행동들을 모두 같은 인터페이스로 실행할 수 있게 만들 수 있습니다.
⚙️ 커맨드 패턴의 기본 구조
클래스 | 역할 |
ICommand | 명령의 인터페이스 (Execute, Undo 정의) |
ConcreteCommand | 실제 동작을 수행하는 명령 클래스 |
Invoker | 명령을 실행하는 주체 (예: InputManager) |
Receiver | 실제 행동을 하는 객체 (예: Player, Character 등) |
🧠 예제 시나리오 — “캐릭터 이동 및 공격”
방향키로 캐릭터를 이동하고, Space키로 공격하는 간단한 2D 게임 예제입니다.
🔹 1. ICommand 인터페이스
public interface ICommand
{
void Execute();
void Undo();
}
모든 명령은 Execute()로 실행되고, Undo()로 되돌릴 수 있습니다.
🔹 2. 실제 명령 클래스들 (Concrete Commands)
using UnityEngine;
public class MoveCommand : ICommand
{
private Character _character;
private Vector3 _direction;
public MoveCommand(Character character, Vector3 direction)
{
_character = character;
_direction = direction;
}
public void Execute()
{
_character.Move(_direction);
}
public void Undo()
{
_character.Move(-_direction);
}
}
public class AttackCommand : ICommand
{
private Character _character;
public AttackCommand(Character character)
{
_character = character;
}
public void Execute()
{
_character.Attack();
}
public void Undo()
{
Debug.Log("공격은 되돌릴 수 없습니다.");
}
}
🔹 3. 캐릭터 클래스 (Receiver)
using UnityEngine;
public class Character : MonoBehaviour
{
public float moveSpeed = 3f;
public void Move(Vector3 direction)
{
transform.position += direction * moveSpeed * Time.deltaTime;
Debug.Log($"이동: {direction}");
}
public void Attack()
{
Debug.Log("공격!");
}
}
🔹 4. 입력 처리 클래스 (Invoker)
using System.Collections.Generic;
using UnityEngine;
public class InputHandler : MonoBehaviour
{
[SerializeField] private Character _character;
private Stack<ICommand> _commandHistory = new Stack<ICommand>();
private void Update()
{
ICommand command = null;
if (Input.GetKey(KeyCode.W)) command = new MoveCommand(_character, Vector3.up);
if (Input.GetKey(KeyCode.S)) command = new MoveCommand(_character, Vector3.down);
if (Input.GetKey(KeyCode.A)) command = new MoveCommand(_character, Vector3.left);
if (Input.GetKey(KeyCode.D)) command = new MoveCommand(_character, Vector3.right);
if (Input.GetKeyDown(KeyCode.Space)) command = new AttackCommand(_character);
if (command != null)
{
command.Execute();
_commandHistory.Push(command);
}
if (Input.GetKeyDown(KeyCode.Z)) UndoLastCommand();
}
private void UndoLastCommand()
{
if (_commandHistory.Count > 0)
{
var lastCommand = _commandHistory.Pop();
lastCommand.Undo();
}
}
}
🔍 실행 흐름 정리
단계 | 설명 |
1 | InputHandler가 키 입력을 감지 |
2 | 해당 입력에 맞는 Command 객체 생성 |
3 | Execute()로 명령 실행 (Character 행동 수행) |
4 | 실행된 명령을 _commandHistory 스택에 저장 |
5 | Z 키 입력 시 마지막 명령을 Undo()로 되돌림 |
🧱 커맨드 패턴의 장점
장점 | 설명 |
🧩 입력과 행동 분리 | 입력 로직을 바꿔도 행동 코드 수정이 필요 없음 |
🕹️ 되돌리기/재실행 구현 용이 | 명령을 저장해두면 쉽게 Undo/Redo 가능 |
🔌 유연한 확장성 | 새로운 명령(예: 점프, 회피)을 쉽게 추가 가능 |
🧠 AI, 매크로 등에도 재사용 가능 | 동일한 명령 객체를 AI나 스크립트에서도 재활용 가능 |
⚠️ 주의할 점
- 너무 작은 동작마다 커맨드로 만들면 불필요한 객체 생성이 많아짐
→ 주로 “의미 있는 행동 단위”에만 사용하기 - 명령 저장(Undo)을 많이 사용한다면
스택 메모리 관리를 신경써야 함
🧩 확장 아이디어
- AI 행동 시스템
- AI가 행동을 Command로 큐에 넣고 순서대로 실행
- 플레이어 리플레이 기능
- 명령 기록을 파일에 저장 후 재생
- UI 버튼과의 연동
- 버튼 클릭 시 AttackCommand 실행 등
✅ 정리
커맨드 패턴은 **“입력과 행동을 분리”**해서
게임의 유연성과 유지보수성을 높이는 데 매우 유용한 패턴입니다.
Unity에서 이 패턴을 사용하면
입력, 행동, 되돌리기, AI, UI 모두 하나의 구조로 통일할 수 있습니다.
728x90
반응형
'개발 > Unity' 카테고리의 다른 글
Unity 내장 오브젝트 풀(Object Pool) 사용하기 (0) | 2025.10.13 |
---|---|
Unity에서 tag 비교할 때 CompareTag()를 사용해야 하는 이유 (0) | 2025.10.12 |
Unity에서 이벤트버스(Event Bus) 패턴 활용하기 (0) | 2025.10.08 |
Unity에서 상태패턴으로 캐릭터 움직임과 공격 구현하기 (0) | 2025.10.07 |
Unity에서 추상 클래스 기반 Singleton 패턴 구현하기 (0) | 2025.10.06 |