Managing object behavior with states

Games as a whole, and individual objects or characters, can often be thought of (or modeled as) passing through different states or modes. Modeling states and changes of state (due to events or game conditions) is a very common way to manage the complexity of games and game components. In this recipe, we create a simple three-state game (game playing, game won, and game lost), using a single GameManager class.

How to do it...

To use states to manage object behavior, perform the following step:

  1. Add the following C# script class to the Main Camera:
    // file: GameManager
    using UnityEngine;
    using System.Collections;
    
    public class GameManager : MonoBehaviour {
      private float gameStartTime;
      private float gamePlayingTime;
    
      private enum GameStateType {
        STATE_GAME_PLAYING,
        STATE_GAME_WON,
        STATE_GAME_LOST,
      }
    
      private GameStateType currentState;
        
      private void Start () {
        NewGameState( GameStateType.STATE_GAME_PLAYING );
      }
    
      private void NewGameState(GameStateType newState) {
        // (1) state EXIT actions
        switch( currentState ) {
        case GameStateType.STATE_GAME_PLAYING:
          gameStartTime = Time.time;
          break;
        }
    
        // (2) change current state
        currentState = newState;
    
        // (3) state ENTER actions
      }
    
      private void Update () {
        switch( currentState ) {
        case GameStateType.STATE_GAME_PLAYING:
          StateGamePlayingUpdate();
          break;
        case GameStateType.STATE_GAME_WON:
          StateGameWonUpdate();
          break;
        case GameStateType.STATE_GAME_LOST:
          StateGameLostUpdate();
          break;
        }
      }
    
      private void OnGUI () {
        switch( currentState ) {
        case GameStateType.STATE_GAME_PLAYING:
          StateGamePlayingGUI();
          break;
        case GameStateType.STATE_GAME_WON:
          StateGameWonGUI();
          break;
        case GameStateType.STATE_GAME_LOST:
          StateGameLostGUI();
          break;
        }
      }
      
      private void StateGamePlayingGUI() {
        GUILayout.Label("state: GAME PLAYING - time since game started = "  + gamePlayingTime);
        bool winGameButtonClicked = GUILayout.Button("WIN the game");
        bool loseGameButtonClicked = GUILayout.Button("LOSE the game");
        
        if( winGameButtonClicked )
          NewGameState( GameStateType.STATE_GAME_WON );
    
        if( loseGameButtonClicked )
          NewGameState( GameStateType.STATE_GAME_LOST );
      }
    
      private void StateGameWonGUI() {
        GUILayout.Label("state: GAME WON - game duration = " + gamePlayingTime);
      }
    
      private void StateGameLostGUI() {
        GUILayout.Label("state: GAME LOST - game duration = " + gamePlayingTime);
      }
      
      private void StateGameWonUpdate() {	print("update - state: GAME WON"); }
      private void StateGameLostUpdate() {	print("update - state: GAME LOST"); }
    
      private void StateGamePlayingUpdate() {
        gamePlayingTime = (Time.time - gameStartTime);
        print("update - state: GAME PLAYING :: time since game started = " + gamePlayingTime); 
      }
    
    }

How it works...

As can be seen in the following state chart figure, this recipe models a simple game which starts in the GAME PLAYING state, depending on the button clicked by the user, the game moves either into the GAME WON state or the GAME LOST state. The three possible states of the system are defined using the enumerated type GameStateType. The current state of the system at any point in time is stored in currentState variable.

How it works...

Key aspects of state driven games include the following:

  • When a state changes, there may be actions as a particular state is exited, then the system changes it data to represent the new state, then there may be actions as the new state is entered

    In our example, this is the responsibility of the NewGameState() method. The code for this method is in three parts, implementing the logic for the following steps: old state exit, set new state, and new state entered.

  • When the GameManager object receives messages (for example, every frame for Update() and OnGUI()), its behavior must be appropriate for the current state. So we see in these methods switch statements that call state-specific methods. For example, if the current state is STATE_GAME_PLAYING, then when an Update() message is received, the StateGamePlayingGUI() method will be called, and when an OnGUI() message is received, the StateGamePlayingGUI()method will be called.

However, the size and number of methods in our GameManager class will grow significantly with more states, and more complex game logic is needed for non-trivial games. The next recipe takes a more sophisticated approach to state-driven games, where each state has its own class.

See also

  • The Managing complex object behavior with the state pattern recipe
..................Content has been hidden....................

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