본문 바로가기
개발/Unity

Unity에서 커맨드(Command) 패턴 활용하기

by DinoDev 2025. 10. 11.
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)을 많이 사용한다면
    스택 메모리 관리를 신경써야 함

🧩 확장 아이디어

  1. AI 행동 시스템
    • AI가 행동을 Command로 큐에 넣고 순서대로 실행
  2. 플레이어 리플레이 기능
    • 명령 기록을 파일에 저장 후 재생
  3. UI 버튼과의 연동
    • 버튼 클릭 시 AttackCommand 실행 등

✅ 정리

커맨드 패턴은 **“입력과 행동을 분리”**해서
게임의 유연성과 유지보수성을 높이는 데 매우 유용한 패턴입니다.

Unity에서 이 패턴을 사용하면
입력, 행동, 되돌리기, AI, UI 모두 하나의 구조로 통일할 수 있습니다.

728x90
반응형