• Recent Tutorials

  • Tripping The Class Fantastic: State Machines

    States within States
    You may be thinking... "But, that's going to be really complicated... my game has 12 menus, an inventory screen and the game itself!"

    You don't have to put everything in one gigantic state machine. That would be truly scary.

    Break it up a little and have a number of state machines that deal with a particular aspect and then one main machine that deals with the overall state of your application, so for example:-

    Code: [View]
    WHILE (1=1) DO
      CASE MAINSTATE OF
        MS_STATE_INITIALISING : BEGIN
          .....
        END
        MS_STATE_MAINMENU : BEGIN
          .....
        END
        MS_STATE_LOADING_GAME : BEGIN
          .....
        END
        
        .....
      END
    
      IF NEXT_MAIN_STATE<>0 THEN
        MAINSTATE:=NEXT_MAIN_STATE;
        NEXT_MAIN_STATE:=0;
      END IF
    
    END WHILE
    Obviously one thing you need to be careful of in these situations is the accidental assignment of one machines state values to another machine. A method to ensure this doesn't happen is to ensure that no two state machines have the same state IDs and to detect invalid states using the ELSE clause of the machine's CASE statement.

    With this state machine within a state machine approach there are a number of decisions you'll need to make, mainly relating to flow, code organisation and controlling the state of the top level state machine.

    One approach is to have the sub-state machines (i.e. the one for handling the initialising, the one for handling the main menu etc.) as functions that return a value that is the next required main state.

    For example:-

    Code: [View]
    function mainStateInitialising:integer;
    begin
      result:=0;
      case fInitialisingState of
        .....
        // Do initialisation states here
        .....
        INIT_STATE_DONE : begin
          result:=MS_STATE_MAINMENU;
        end;
      end;
    
      if (fNextInitialisingState<>0) then
      begin
        fInitialisingState:=fNextInitialisingState;
        fNextInitialisingState:=0;
      end;
    end;
    This feeds into the main state machine like this:-

    Code: [View]
        .....
        MS_STATE_INITIALISING : begin
          nextState:=mainStateInitialising;
        END;
        .....
      END;
    
      if (nextState<>0) then
      begin
        mainState:=nextState;
        nextState:=0;
      end;
    
      .....
    Comments 3 Comments
    1. Ñuño Martínez's Avatar
      Ñuño Martínez -
      Just did a very fast reading (titles and some of the code, not much more). Looks very interesting.

      I did my own state-machine library translating the one described in"Programming Game AI by Example". Your approach seems slightly different. I'll tell you once I read your tutorial in-deep and compare both ways.
    1. AthenaOfDelphi's Avatar
      AthenaOfDelphi -
      If anyone has downloaded the attachment, the file classBaseStateMachine.pas was missing. Sorry about that, thanks to d.spinetti for pointing out my mistake.
    1. Ñuño Martínez's Avatar
      Ñuño Martínez -
      Read. Your implementation is way different than the one described in"Programming Game AI by Example" but it's a nice approach.

      Thank-you.