State Pattern

From GPWiki
Jump to: navigation, search
40px-broken-icon.png   This page has broken markup.
If you fix this problem, please remove this banner.

The State Pattern allows an object to alter its behavior when its internal state changes. The object will appear to change its class.

Definition

Problem

A game character frequently performs sequences of actions such as "fall down", "prone on ground", "get up", "stand" and "walk". Each character maintains a state machine and reacts on game events differently depending on the state of the character. For instance, a character lying on the "prone on ground" state may ignore all damage events origination from other characters nearby.

How do you

  • define the character behavior in each state and
  • add additional character states without modifying existing code for character states.

Context

  1. A class Context (the Character) defines the interface to clients and maintains the current state.
  2. An abstract class State (the CharacterState) defines an interface for behavior associated with the Context.
  3. One or more concrete state classes (character states) define the behavior associated with a particular state of the Context.

State pattern:

C# code:


class Character {

   // the character current state, should be initialised
   CharacterState _State = null;
   public int Life = 10;
   public Character()
   {
       ChangeState( new CharacterStandState() );
   }
   void ChangeState(CharacterState state)
   {
       if (_State != null)
           _State.OnExit();
       _State = state;
       _State.OnEnter();
   }
   public void OnMessage(Message msg)
   {
       if (msg is DamageMessage)
           _State.OnMessage(msg as DamageMessage, this);
       else if (msg is JumpMessage)
           _State.OnMessage(msg as JumpMessage, this);
   }
   public void OnGameTick()
   {
       _State.OnGameTick(this);
   }
   // set character animation
   public void SetAnimation(string animname)
   {
       // ...
   }

}

abstract class CharacterState {

   public virtual void OnEnter(Character character) { } // do nothing by default
   public virtual void OnExit(Character character) { } // do nothing by default
   public virtual void OnGameTick(Character character) { } // do nothing by default
   public virtual void OnMessage(DamageMessage msg, Character character) { } // do nothing by default
   public virtual void OnMessage(JumpMessage msg,   Character character) { } // do nothing by default

}

class CharacterStandState : CharacterState {

   public override void OnEnter(Character character)
   {
       character.SetAnimation("stand");
   }
   public override void OnGameTick(Character character)
   {
   }
   public override void OnMessage(DamageMessage msg, Character character) 
   {
       character.Life -= msg.damage;
       if (character.Life <= 0)
           character.ChangeState( new CharacterFallState() );
   }
   public override void OnMessage(JumpMessage msg,   Character character)
   {
       character.ChangeState( new CharacterJumpState() );
   } 

}

class CharacterJumpState : CharacterState {

   public override void OnEnter(Character character)
   {
       character.SetAnimation("jump");
   }
   public override void OnGameTick(Character character)
   {
       character.MoveCharacterOnJumpArc(); // do jumping arc
       if (character.OnGround()) // when character lands on ground again
           character.ChangeState( new CharacterStandState() );
   }

}

class CharacterFallState : CharacterState {

   public override void OnEnter(Character character)
   {
       character.SetAnimation("fall");
   }
   public override void OnGameTick(Character character)
   {
       character.MoveCharacterOnFallPath(); // do fall path
       if (character.IsOnGround()) // when character hits the ground
           character.ChangeState( new CharacterProneState() );
   }

}

Comments

Using the State pattern groups the behavior per character state of a game character into separate classes. The State pattern also provides better readability, extendability and maintainability. The Singleton Pattern should be used for the concrete state classes. Note that the state classes do not contain any state themselves.

External Links