요청을 객체로 캡슐화한다. 그렇게함으로서 유저들은 다른 요청, 큐(Queue), 혹은 로그 요청으로 클라이언트를 매개변수화하고, 실행할 수 없는 명령을 서포터한다." -Game Programming Pattern
커맨드 패턴의 핵심은 추상화이다. 이 패턴은 객체의 함수 호출을 리얼 타임에서 구체화해서 실행시킨다. GOF에서는 "커맨드는 콜백의 객체기반 대체품이다"라고 소개한다.
게임에는 여러가지의 입력에 반응하는 코드가 어딘가에는 존재한다. 이 코드들은 유저의 입력을 게임 월드에서 유의미한 액션으로 바꾼다.
예를들어, 유저는 A, B, X와 Y버튼을 가지고, 각각 무기변경(SwapWeapon), 달리기(Dash), 점프(Jump), 공격(FireGun)의 액션(Action)을 의미한다.
이것들은 간단하게 구현할 수 있다.
if(isPressed(Button_X)) Jump();
else if(isPressed(Button_Y)) Fire()_Gun();
else if(isPressed(Button_A)) SwapWeapon();
else if(isPressed(Button_B)) Dash();
그러나, 우리가 만드는 대부분의 게임은 유저가 쉽게 조작키를 변경할 수 있기에 Jump()와 Fire_Gun()을 쉽게 스왑(Swap)할 수 있는 방법이 필요하다. 이것의 해결법(Solution)이 바로 커맨드(Command) 패턴이다.
우리는 액션을 대표할 객체가 필요하기에 트리거가능한 기본 클래스를 정의할 것이다.
class Command
{
public:
virtaul ~Command();
virtual void exucte();
}
그리고 우리는 각각의 다른 액션을 대표할 서브 클래스를 정의한다.
class JumpCommand
:public Command
{
virtual void excute() {jump();}
//another code
}
이로서 우리의 InputHandler는 다음과 구성된다.
class InputHandler
{
public:
void InputHandle();
private:
Command* button_X;
Command* button_Y;
Command* button_A;
Command* button_B;
}
void InputHandler::InputHandle()
{
if(isPressed(Button_X)) button_X->excute();
else if(isPressed(Button_Y)) button_Y->excute();
else if(isPressed(Button_A)) button_A->excute();
else if(isPressed(Button_B)) button_B->excute();
}
다음과 같이 구조를 그릴 수 있다.
위에서, 우리는 각각의 액션을 Command 클래스로 정의했다. 그러나, 이것은 두 문제가 있다. 이것들은 암묵적으로 Actor(플레이어 캐릭터)의 액션을 알려준다. 이것은 커맨드와 플레이어 캐릭터 객체가 크게 결합되어있기 때문이다. JumpCommand안에 있는 Action(Jump 메서드)를 분리시켜야한다.
우리는 원하는 액션이 있는 객체(GameAcotr, PlayerCharactor등등)를 매개변수로 패스할 수 있다.
class Command
{
public:
virtual ~Command();
virtual void excute(GameActor& actor);
};
여기서, GameActor는 우리의 Player 캐릭터를 의미한다. 또한, 우리는 상속받은 Command 클래스들이 Actor안에 있는 메서드를 선택하여 호출하게 만든다.
class JumpCommand
:public Command
{
virtual void excute(GameActor& actor) { actor.Jump(); }
};
이로서 우리는 성공적으로 Command와 Actor를 분리시켰고, Command들은 Actor의 액션을 알지 못하게 되었다.
이렇게 함으로서, InputHandler는 acotr안으로 던져버렸기때문에, command가 어떤 함수를 실행할지 알 수 없게되었다. 그래도 우리는 커맨드가 구체화된 호출이라는 사실을 알고있다. 약간의 코드를 추가해서 이 문제를 해결할 수 있다.
class InputHandler
{
public:
Command* InputHandle();
private:
Command* button_X;
Command* button_Y;
Command* button_A;
Command* button_B;
};
Command* InputHandler::InputHandle()
{
if (isPressed(Button_X)) return button_X;
else if (isPressed(Button_Y)) return button_Y;
else if (isPressed(Button_A)) return button_A;
else if (isPressed(Button_B)) return button_B;
return NULL;
}
void Update(InputHandler* handler, GameActor* actor)
{
Command* command = handler->InputHandle();
if (command)
command->excute(*actor);
}
----------------------------------------------------
내가 지금 뭐하는건지...그냥 영어공부한다고 원서책에 다이빙하듯이 돌진했던걸, 왜 다시 한글로 정리한건지 모르겠네.
어짜피 디자인패턴일뿐인데...;;
다음부터 따로 정리안할 예정이고, 내가 더 실력을 키우고 해당 디자인패턴을 실제로 여러번 쓰기전까지는 더 쓰지 않을 예정
VS Code로 Markdown 편집하기 (0) | 2018.01.23 |
---|---|
VS를 깃허브에 연동 (0) | 2018.01.22 |
Singleton 패턴 (0) | 2016.10.22 |
ProtoType 패턴 (0) | 2016.10.15 |
Abstract Factory 패턴 (0) | 2016.10.15 |
댓글,
Lowpoly
게임 서버 프로그래머 지망생