Defining the clown's state machine

A Finite State Machine (FSM) is nothing but a system to manage the various states that a particular machine (the clown in our case) can be in. You can create an FSM to describe the various stages of an entity's lifecycle. The term entity could be anything from the main character in a game where the states would be walking, running, jumping, shooting, dying, and so on. Similarly, an entity could be the game itself where the states would be: game world creation, game world update, collision detection, level completion, game over, and so on.

A common way to represent the states within an FSM is to enumerate them, which in the case of our clown's states is the following enum that you will find in the GameGlobals.h file:

enum EClownState
{
  E_CLOWN_NONE = 0,
  E_CLOWN_UP,
  E_CLOWN_DOWN,
  E_CLOWN_BOUNCE,
  E_CLOWN_ROCKET,
  E_CLOWN_BALLOON,
};

For this simple game, we have just five states the clown can be in, excluding the E_CLOWN_NONE state of course. The Clown class will have a state_ property of the type EClownState. We will now look at the SetState function of the Clown class:

void Clown::SetState(EClownState state)
{
  // only accept a change in state
  if(state_ == state)
    return;

  state_ = state;

  // call respective state based action
  switch(state_)
  {
  case E_CLOWN_UP:
    StartGoingUp();
    break;
  case E_CLOWN_DOWN:
    StartComingDown();
    break;
  case E_CLOWN_BOUNCE:
    StartBounce();
    break;
  case E_CLOWN_ROCKET:
    StartRocket();
    break;
  case E_CLOWN_BALLOON:
    StartBalloon();
    break;
  }
}

We keep the SetState function straightforward. We perform processing only if there is a change in state. Why is this of importance you wonder? Well, consider that you create a new CCAnimate object to play a walking animation, as the character enters into the walking state every time the player presses the right arrow key. This way, you will be repeatedly creating a new CCAnimate object every time the right arrow key's event is fired!

Next, we write a switch case structure that handles each state and calls the respective function. The StartGoingUp, StartComingDown, and StartBounce functions change the current display frame for the clown, so we'll skip straight to the StartRocket function:

void Clown::StartRocket()
{
  setDisplayFrame(CCSpriteFrameCache::sharedSpriteFrameCache()->
    spriteFrameByName("cjroket.png"));
  // unschedule any previously scheduled selectors...possibly 
    by another rocket/balloon
  unschedule(schedule_selector(Clown::FinishRocketBalloon));
  // stay in this state for some time
  scheduleOnce(schedule_selector(Clown::FinishRocketBalloon), 
    ROCKET_DURATION);

  // no gravity while aboard a bottle rocket
  body_->SetGravityScale(0.0f);
  // decently high velocity while aboard a bottle rocket
  body_->SetLinearVelocity(b2Vec2(0.0f, 30.0f));

  // create neat jet stream for the rocket
  rocket_trail_ = CCParticleSystemQuad::create("explosion.plist");
  rocket_trail_->setDuration(-1);
  rocket_trail_->setPositionType(kCCPositionTypeRelative);
  game_world_->game_object_layer_->addChild(rocket_trail_);

  SOUND_ENGINE->playEffect("bottle_rocket.wav");
}

We begin by changing the current display frame. We then unschedule any previously scheduled selector and schedule a new one with the respective duration.

The next part is interesting, since we alter the physics of the clown's body. When the clown enters in to this state, we want him to shoot upwards at a constant velocity unaffected by gravity, so we do just that. We set the gravity scale to 0.0f, which means that the clown's body will not be influenced by the force of gravity and then set a decently high linear velocity pointing straight up.

That last bit of code will simply add a particle system to represent the jet stream of the rocket. I wonder how that clown holds on to such a powerful rocket without burning himself! The code for the StartBalloon function is similar to the StartRocket function, but the linear velocity we set is much lesser because a balloon tends to be slower than a rocket. Also, it doesn't have a jet stream so we don't add a particle system either.

However, unlike the rocket, the user can control the clown's horizontal movement via the accelerometer of course! You can find the relevant logic in the didAccelerate function inside GameWorld.cpp. So, let's discuss the callback that both these functions have scheduled, the FinishRocketBalloon function:

void Clown::FinishRocketBalloon(float dt)
{
  // after rocket/balloon, clown will be moving upwards
  SetState(E_CLOWN_UP);
  // resume normal gravity
  body_->SetGravityScale(1.0f);

  // remove any rocket jet stream if it exists
  if(rocket_trail_)
  {
    rocket_trail_->removeFromParentAndCleanup(true);
    rocket_trail_ = NULL;
  }
}

Once both the states have finished, we set the clown's state to be moving upwards and resume normal gravity scale. We also remove any glorious particle systems that we may have added. Voila, you've implemented a simple yet elegant FSM.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.168.203