반응형

Command Pattern 이란?

커맨드 패턴(Command Pattern)이란 여러 커맨드(실행될 기능들, 요청들)들을 커맨드 객체로 감싸 재사용성이 높은 클래스를 설계하는 패턴입니다. 예를 들어 커맨드 객체에는 게임에서 음악을 실행 한다던가 캐릭터가 움직이거나 물건을 던지는 등의 커맨드를 모아두기만 하고 커맨드들이 언제 어떻게 실행될지는 관여하지 않게 만듭니다.

  • 게임에서 사용자의 취향에 맞게 키 세팅을 다시 할 수 기능을 제공하고 싶을 때 유용합니다.
  • 사용자가 입력한 모든 커맨드들을 저장해서 리플레이 시스템을 구현할 때 유용합니다.
  • 이전 커맨드 되돌리기, 되돌렸던 커맨드 다시 실행하기와 같은 기능을 구현할 때 유용합니다.

유니티에서 Command Pattern 예시와 활용법

커맨드 패턴을 따라 움직이는 고양이를 커맨드 패턴으로 구현해봅시다.

public abstract class Command
{
    public abstract void Execute();
    public abstract void Undo();
}

// 커맨드 패턴은 이런 게임의 특정 행동마다 이런 구현체들을 만듭니다.
// 따라서 게임에서 특정 행동이 많을수록 이런 구현체도 많아질 것입니다.
public class CatMoveForwardCommand : Command
{
    private Cat cat;

    public MoveForwardCommand(Cat cat) { this.cat = cat; }

	// 이 커맨드로부터 실행될 객체의 매서드를 여기에 작성해줍니다.
    // MoveForward매서드가 실제로 하는 일은 아래 Cat 클래스에서 정의해두었습니다.
    public override void Execute() { cat.MoveForward(); }

    public override void Undo() { moveObject.MoveBack(); }
}

위 코드에 Command 추상 클래스가 있습니다. 만약 구상하는 게임에 되돌리기 기능이 필요없다면 Undo는 없어도 될 것입니다. 다만 커맨드가 해야하는 행동을 실행하는 함수 Execute는 있어야 합니다. 그것을 구현하는 구현체의 예시로 고양이가 앞으로 움직이는 행동에 대한 클래스를 만들었습니다. 마찬가지로 고양이가 할 수 있는 모든 행동들에 대해서 클래스를 만들어주면 됩니다. 예를 들어 CatMoveForwardCommand는 같은 형태로 움직이는 방향에 따라 총 4개의 클래스를 만들면 되겠습니다. 이런 커맨드 클래스는 언제 어떻게 구체적으로 실행될지에 대한 정보를 포함하지 않습니다.

 

아래는 고양이 클래스입니다. 고양이 클래스이므로 실제 고양이가 할 행동들(실제로 화면에서 무언가를 하게 만드는 코드들)을 매소드로 구현하는 곳입니다.

public class Cat : MonoBehaviour
{
    private const float SPEED = 1f;

    public void MoveForward() { Move(Vector3.forward); }
    public void MoveBack() { Move(Vector3.back); }
    public void TurnLeft() { Move(Vector3.left); }
    public void TurnRight() { Move(Vector3.right); }
    // 식빵굽기, 꾹꾹이 등 고양이가 할 수 있는 다른 행동들을 여기에 구현할 수 있습니다.

    private void Move(Vector3 dir) { transform.Translate(dir * SPEED); }
}

이 고양이를 상하좌우 방향으로 각각 움직이기 위해서 4개의 커맨드가 필요할 것입니다. 아래 GameController에서 키 입력에 따라 고양이를 움직이게 만듭니다. GameController의 역할은 그 이름에 걸맞게 아래와 같이 "키 입력과 CatMoveForwardCommand의 Execute를 바인딩하는 역할"만 하면 됩니다.

public class GameController : MonoBehaviour
{
    public Cat cat;

    private Command catMoveForwardButton;
	
    void Start()
    {
    	cat = new Cat();
    	catMoveForwardButton = new CatMoveForwardCommand(cat);
    }
    
    void Update()
    {
    	if(Input.GetKeyDown(KeyCode.W)) catMoveForwardButton.Execute();
    }
}

물론 단순히 움직이고 끝내는 경우에는 굳이 커맨드 패턴을 사용할 이유가 없습니다. 그러나 만들고자 하는 게임 기능에 유저의 커스텀 키 세팅, 커맨드 기록에 따른 리플레이 시스템, 이전 커맨드 되돌리기(undo), 되돌리기를 취소(redo)가 있다면 Command 패턴이 유용하게 됩니다.

 

아래는 Command 패턴 사용시 구현할 수 있는 기능들의 대략적인 코드 구조 예시입니다. 스택과 같이 적절한 자료구조를 골라 커맨드 기록을 남기면 리플레이 시스템과 undo, redo를 구현하기 용이할 것입니다.

public class GameController : MonoBehaviour
{
    public Cat cat;

    private Command catMoveForwardButton;
    // 더 많은 커맨드들...
	
    // undo, redo, replay에 활용하기 위한 자료구조
    Stack<Command> undoCommands = new Stack<Commad>();
    Stack<Command> redoCommands = new Stack<Commad>();
    
    void Start()
    {
    	cat = new Cat();
    	catMoveForwardButton = new CatMoveForwardCommand(cat);
    }
    
    void Update()
    {
    	// 새 커맨드 입력할 경우 undo, redu 스택에 변경이 있어야 한다.
    	if(Input.GetKeyDown(KeyCode.W)) ExecuteNewCommand(catMoveForwardButton);
        
        if(undo 입력) 
        {
		// undoCommands 0개 아니면 undoCommands.Pop 과 같은 방식으로 구현
		// some code...
        }
        
        if(redo 입력)
        {
        	// some code...
        }
    }
    
    // 새 커맨드 입력시 undo, redo 업데이트
    void ExecuteNewCommand(Command commandButton) 
    {
    	commandButton.Execute();
        undoCommands.Push(commandButton)
        redoCommands.Clear();
    }
    
    // 커스텀 키를 쉽게 구현할 수 있다.
    private void SwapKey(ref Command key1, ref Command key2)
    {
        Command temp = key1;
        key1 = key2;
        key2 = temp;
    }
}
반응형
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기