무민은귀여워

[게임프로그래밍패턴] 명령패턴 본문

IT/디자인패턴

[게임프로그래밍패턴] 명령패턴

moomini 2019. 7. 25. 15:44
반응형

요청 자체를 캡슐화하는 것입니다. 이를 통해 요청이 서로 다른 사용자를 매개변수로 만들고, 요청을 대기시키거나 로깅하며, 되돌릴 수 있는 연산을 지원합니다. (GoF의디자인패턴, 311쪽)

 

명령 패턴은 매서드 호출을 실체화reify 한 것이다.

        → 함수 호출을 객체로 감쌌다는 의미.

         '콜백', '일급함수', '함수 포인터', '클로저', '부분 적용 함수'와 비슷하다.

 


 

예를 들어 x 버튼을 누르면 점프를 하는 기능이 있다고 하자.

 

대부분의 게임은 키 변경을 지원하므로 x가 눌렸을 때 바로 jump()를 실행하는 것이 아니라, 키 바인딩을 통해 함수를 직접 호출하지 말고 교체 가능한 무언가로 바꾸어야 한다. 

 

이러한 게임에서 할 수 있는 행동을 실행 할 수 있는 공통 상위 클래스부터 정의한다. (Command 클래스)

 

입력 핸들러 코드에는 각 버튼별로 Command 클래스 포인터를 저장한다. (InputHandler 클래스)

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Command {
public:
    virtual ~Command() {}
    virtual void execute() = 0;
    virtual void undo() = 0;
};
 
class MoveUnitCommand : public Command {
public:
    MoveUnitCommand(Unit* unit, int x, int y)
        : unit_(unit), x_(x), y_(y),
        xBefore_(0), yBefore_(0),
        x_(x), y_(y) {
 
    }
 
    virtual void execute() {
        // 나중에 이동을 취소할 수 있도록 원래 유닛 위치를 저장한다.
        xBefore_ = unit_->x();
        yBefore_ = unit_->y();
        unit_->moveTo(x_, y_);
    }
 
    virtual void undo() {
        unit_->moveTo(xBefore_, yBefore_);
    }
 
private:
    Unit* unit_;
    int x_, int y_;
    int xBefore_, int yBefore_;
};
cs

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Command* InputHandler::handleInput() {
    if (isPressed(BUTTON_X)) return buttonX_;
    if (isPressed(BUTTON_Y)) return buttonY_;
    if (isPressed(BUTTON_A)) return buttonA_;
    if (isPressed(BUTTON_B)) return buttonB_;
 
    // 아무것도 누르지 않았다면, 아무것도 하지 않는다.
    return NULL;
}
 
Command* command = inputHandler.handleInput();
if (command) {
    command->excute(actor);
}
cs

 

입력 핸들러 코드는 플레이어가 이동을 선택할 때마다 명령 인스턴스를 생성해야한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Command* handleInput() {
    Unit* unit = getSelectedUnit();
    if (isPressed(BUTTON_UP)) {
        // 유닛을 한 칸 위로 이동한다.
        int destY = unit->y() - 1;
        return new MoveUnitCommand(unit, unit->x(), destY);
    }
    if (isPressed(BUTTON_DOWN)) {
        // 유닛을 한 칸 위로 이동한다.
        int destY = unit->y() + 1;
        return new MoveUnitCommand(unit, unit->x(), destY);
    }
    //다른 이동들...
    return NULL;
}
cs

 

 

 

반응형
Comments