객체의 내부 상태에 따라서 스스로 행동을 변경 할 수 있는 패턴으로, 이렇게 하면 객체가 내부에서 마치 자신의 클래스를 바꾸는 것처럼 사용할수 있다.

상태패턴에서는 finite state machine (FSM) 을 언급할 수밖에 없다. 그러다 보니 hierarchical state machine(HSM) 과 pushdown automata까지 이어졌다.

많은걸 다루다보니 분량 조절을 위해서 세세한 부분은 설명하지 않겠다. 우리한테 중요한건 큰 그림을 그리고 파악하는 것이다.

간단한 횡스크롤 플랫포머를 만든다고 보자. 게임월드의 주인공이 사용자 입력에 따라서 이동하도록 구현해야한다. B버튼을 누른다면 점프를 한다고 생각해보자.

void Heroine::handleInput(Input input)
{
  if (input == PRESS_B)
  {
    yVelocity_ = JUMP_VELOCITY;
    setGraphics(IMAGE_JUMP);
  }
}

자세히 들여다보면 버그가 보일 것이다. 어디서든 점프가 가능하다. 즉 공중에서도 점프가 가능할 것이다. 그러면 해당 공중점프를 막아야 한다. bool 변수를 추가해서 막아보자.

void Heroine::handleInput(Input input)
{
  if (input == PRESS_B)
  {
    if (!isJumping_)
    {
      isJumping_ = true;
      // Jump...
    }
  }
}

주인공이 땅에 있을 때 아래버튼 누르면 엎드리고, 버튼을 때면 다시 일어나는 기능을 추가해보자.

void Heroine::handleInput(Input input)
{
  if (input == PRESS_B)
  {
    // Jump if not jumping...
  }
  else if (input == PRESS_DOWN)
  {
    if (!isJumping_)
    {
      setGraphics(IMAGE_DUCK);
    }
  }
  else if (input == RELEASE_DOWN)
  {
    setGraphics(IMAGE_STAND);
  }
}

이번에도 버그를 찾아보자. 어떤것이 보이는가?

  1. 아래버튼을 누른다.
  2. B버튼을 엎드린 상태에서 점프를 하고
  3. 공중에서 아래버튼을 떄면 스탠딩 상태( 점프중인데도 땅에서 서있는 애니메이션 재생)

이런걸 방지하기 위해서 플래그 변수가 더 필요할 것이다.

void Heroine::handleInput(Input input)
{
  if (input == PRESS_B)
  {
    if (!isJumping_ && !isDucking_)
    {
      // Jump...
    }
  }
  else if (input == PRESS_DOWN)
  {
    if (!isJumping_)
    {
      isDucking_ = true;
      setGraphics(IMAGE_DUCK);
    }
  }
  else if (input == RELEASE_DOWN)
  {
    if (isDucking_)
    {
      isDucking_ = false;
      setGraphics(IMAGE_STAND);
    }
  }
}

점프중에 아래 버튼 눌러 내려찍기 공격을 할 수 있게 해보자.

void Heroine::handleInput(Input input)
{
  if (input == PRESS_B)
  {
    if (!isJumping_ && !isDucking_)
    {
      // Jump...
    }
  }
  else if (input == PRESS_DOWN)
  {
    if (!isJumping_)
    {
      isDucking_ = true;
      setGraphics(IMAGE_DUCK);
    }
    else
    {
      isJumping_ = false;
      setGraphics(IMAGE_DIVE);
    }
  }
  else if (input == RELEASE_DOWN)
  {
    if (isDucking_)
    {
      // Stand...
    }
  }
}