Page 1 of 3 123 LastLast
Results 1 to 10 of 26

Thread: State machine - requesting advice

  1. #1

    State machine - requesting advice

    Time of implementing state machine in Super Heli Land is closer and closer, so I want to ask you for advice whether my idea of doing it is correct or not.

    All states would inherit from TState class which would look more or less like this:

    Code:
    TState = class
      public
        Constructor Create;
        Destructor Destroy;
        procedure update;
        procedure draw;
      private
        {...}
    end;
    There will be also PState, which would be pointer to TState:
    Code:
    PState = ^TState;
    All children classes of TState would overload update and draw methods (first would be ran 60 times per second using Allegro timer, last would be called in main loop as fast as it can).

    Main file would contain one PState declaration:
    Code:
    var CurrentState:PState;
    and also single instances of every possible state (which in case of SHL would be only 4: Menu, options, actual game and high scores, with best high score being displayed in main menu.

    Then after creation of each state, allegro's initialization would take place, then CurrentState would be set to main menu state:
    Code:
    CurrentState:=@MainMenuState;
    then timer callback and main loop would be started.

    The update (rendering) loop would look like that:
    Code:
    repeat
    CurrentState^.draw;
    until quit;
    (quit is boolean variable) and timer callback would look like this:
    Code:
    procedure update();CDECL;
      begin
        //frame update
        CurrentState^.update;
      end;
    States would be changed by setting CurrentState so it would point to other state.

    So there's my idea. How much of it is correct way of doing this?
    And what problems I'll face down the road (this will be my first time ever I'll implement state machine)?
    Last edited by Darkhog; 13-06-2013 at 01:47 PM.

  2. #2
    In something like this you don't need pointers with class types, so you can safely delete PState type. You can do
    Code:
    CurrentState:=MainMenuState;
    It copies a reference to the MainMenuState object, not any actual data. Class variable is actually almost like pointer.

  3. #3
    You know what that is a great idea. Infact that is a genious idea. I never thought about handling the game states this way.
    I can see many benefits that such approach could have in comparison to most oftenly used approach where you simply have one gloval variable teling your software in which state it is and then you use case statment to determine which part of the code should execute.
    Main benefit would be the ability to have seperate initialization and finalization code for each state. Theese are being caled on creation or destruction of each state.
    You can also easily make each state to actually have its own substates which could also be verry handy.
    Also the same way as you are handling state specific drawing you could also handle state specific input handling. And all that by simply changing one variable.

  4. #4
    You just have to be careful on not to make it more complicated than the variable approach. Maybe i'm oldschool, but i still prefer the case-statement with variables. Main state, and possible sub-states, like:
    Code:
    TMainStates = (msIntro, msMenu, msGame);
    Actually that's all i need from states i think, most of the time. From the top of my head i cannot think of a single scenario where i'd need sub-states or more main-states than that.

  5. #5
    Quote Originally Posted by User137 View Post
    Actually that's all i need from states i think, most of the time. From the top of my head i cannot think of a single scenario where i'd need sub-states or more main-states than that.
    The place wehere I might use susbstates is right in the MainMenu for handling sub menues (New game screen, Save game screen, Load game screen, Options menu, Credists screen, etc.)

    You are handling your games by only using three states? Where do you do initialization and finalization parts? Please don't tell me that you are one of those guys who make games in a way that every ingame resouce gets loaded a t the verry start of the aplication and not only when they are needed to.

  6. #6
    Quote Originally Posted by SilverWarior View Post
    You know what that is a great idea. Infact that is a genious idea. I never thought about handling the game states this way.
    I can see many benefits that such approach could have in comparison to most oftenly used approach where you simply have one gloval variable teling your software in which state it is and then you use case statment to determine which part of the code should execute.
    Main benefit would be the ability to have seperate initialization and finalization code for each state. Theese are being caled on creation or destruction of each state.
    You can also easily make each state to actually have its own substates which could also be verry handy.
    Also the same way as you are handling state specific drawing you could also handle state specific input handling. And all that by simply changing one variable.
    Thanks! I didn't know you're flattener . Also for this:
    Also the same way as you are handling state specific drawing you could also handle state specific input handling
    I think it can be done by private function in specific child class which would be called from withing TState.update. Control doesn't have be any more precise than frame rate, anyways.

    I also want to thank User137 for thing with pointers. Since I don't particularly like them thanks to you I'd be able to avoid them.

    For switch-case thing... Well I've considered it, but I want to produce clean code. What if I'd like later on add yet another state? Then whatever function would have those switch-cases would get long (like hundreds lines of code long) and to get to actual state code (since e.g. you wanted to change something in behavior in a game state) you'd need to scroll through it. With my approach, every state can live in its own file and would be easy to modify.

    I don't just want to make a game. I want to make maintainable game.
    Last edited by Darkhog; 13-06-2013 at 09:41 PM. Reason: typo!

  7. #7
    Case structure for states can look like this:
    Code:
    // In render loop
    case MainState of
      msIntro: RenderIntro;
      msMenu: RenderMenu;
      msGame: RenderGame;
    end;
    ...
    // In the game loop
    case MainState of
      msIntro: LoopIntro;
      msMenu: LoopMenu;
      msGame: LoopGame;
    end;
    If you need to add another state, you can just make new procedures, and make related code there very cleanly. If you need substates, you can make new similar case inside LoopMenu for example. Depending on implementation, i might use menu classes of nxpascal, similar to IDE components in practise. If i click "Start game" button:
    - In component approach the onClick event would be raised and handled, and then call StartGame() in it. I could simply hide or show entire panels of buttons and things.
    - Oldschool way a StartGame() procedure would be called directly in some onMouseDown/Up/click event. There needs to be some integer variable telling index of button that was mouse-focused, and another variable telling which menu is open.
    Another variables handling fadein, fadeout of states. Usually i only set NewState:=msGame; or something in the StartGame(). After the msGame has faded out, then set MainState:=NewState; . This can happen in the general loop, common for all states.

  8. #8
    Still, seems like hacky solution to me. I JUST DON'T WANT TO FORGET MEANING OF MY CODE AFTER NOT LOOKING INTO IT FZOR 3 MONTHS OR MORE ANYMORE!!!

    Erm... Sorry for that little burst. Anyway, I think there may be a problem with setting states from within other states.

    Problem is that I won't be able to access global variable declared in main program from within other units (each state would get its own unit for clarity sake). Any ideas how to solve it?

  9. #9
    You can always add main unit in uses clause. And for avoiding ciclick dependancies you do it in implementation part of your state units.
    So for instance you have UMain unit as general game unit and several other units each for its own state.
    For you to be able to create theese state objects you need to add theese units into uses section of your UMain file.
    But if you wanna acces objects and variables declared which are in UMain file from theese state units you add UMain file into uses section which you declare in implementation section:
    Code:
    ...
    implementation
    uses UMain //Main game unit
    ...
    Do not add UMain unit into uses section on top of your states units othevise you will cause cicklic redundancies (compiler won't be able to figure out which units need which) and you won't be able to compile your project.

  10. #10
    Quote Originally Posted by Darkhog View Post
    Still, seems like hacky solution to me.
    It is not so teribly hacky. Until now I have always been using such approach. But I still think that your idea is actually better.
    Anywhay in the end it all depends on how good you implement it. You can have great idea but if your implementation of this idea is terible so will be result.

Page 1 of 3 123 LastLast

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Comodo SSL