PDA

View Full Version : Game states


tentons
01-03-2005, 10:18 AM
I found this information useful since I'm refactoring my game's state management. Maybe it will also help you structure your game loop in a better, more flexible way.

http://tonyandpaige.com/tutorials/game1.html

The traditional way of handling multiple states is with a series of if statements, switches, and loops. The program begins in the intro state and loops until a key is pressed. Then the menu is displayed until a selection is made. Then the game begins, and loops until the game is over. Everytime through the game loop, the program must check to see if it should display the menu or simply draw the next frame. Also, the part of the program that handles events must check to see if your input should affect the menu or the game. All of this combines to make a main loop that is hard to follow, and therefore hard to debug and maintain.

EpicBoy
01-03-2005, 11:01 AM
That's how I structure my games and it helps immensely. Having messages/input go to the top state on the stack takes away a ton of code that would be trying to determine what to do with each one.

Bluecat
01-03-2005, 11:01 AM
Yep. The State Pattern. I love this one.

A couple of years ago I was working on an application that used switches to manage states. It was effectively a graphical editor for building training scenarios. I was reading the Design Patterns book and discovered this one. It definitely made the application much simpler.

Now, when I see switch statements, I consider if they would be better using the state pattern.

Raptisoft
01-03-2005, 11:06 AM
Ye Gods...

Guys, that's using C++ like it's C.

Make your whole game a class. Make your menu a class. Make your dialog box a class. Give each one a "process" and "draw" function.

Process and Draw whomever is appropriate at a given time. Think modular... that's what C++ is for. Too many programmers slap "object oriented" on top of plain C concepts.

Bluecat
01-03-2005, 11:24 AM
Errr. That is modular, and pretty OO.

if you have a game loop that looks like:

Game::Run()
{
while (true)
{
switch (gameState)
{
case MAINMENU:
// process for main menu
break;
case PLAY:
// process the game play
break;
}
}


You replace it with:

class MenuState : public State {...}
class PlayState : public State {...}

Game::Play()
{
State* state = new MenuState();
while (true)
{
state->processStuff();
}
}

This is fairly simplified and I've left a lot of code out, but the essense is that each state is a class. You encapsulate the code specific to the state that the game is in. In this example, the MenuState will do the processing necessary to handle the main menu screen. The PlayState will have the code that handles the actual gameplay. There could be a class for the GameOver, High Score, and Buy Screen states as well.

By encapsulating a state into a single class, all the behaviour of that particular state lives in that class. This is way more C++ than C.

cliffski
01-03-2005, 12:10 PM
That sounds like what I do in democracy. I also have a really generic "GameSubsystem" base class which all kinds of stuff uses, and a subsystem manager controls them all. It makes for real simple stuff like

GetSubSystems()->InitialiseAll()

GetSubSystems()->ProcessAll()

GetSubystems()->Release()

etc. This stuff is so important. You don't realise how easy it is to get tied in knots until youve written a few programs without thinking it through.

Bluecat
01-03-2005, 12:22 PM
This stuff is so important. You don't realise how easy it is to get tied in knots until youve written a few programs without thinking it through.

And this is so true handling states with switch or if statements. It's easy to design the initial code to be somewhat elegant, but once things start changing and features get added, that elegant code looks like a bowl of spaghetti marinara with all the little strands and octopi tentacles and fishy bits all over the place.

ggambett
01-03-2005, 01:07 PM
I do this in my framework too, with some variations. For example, not only the top mode (I call them GameModes instead of GameStates) receives events. Events are broadcasted down, and a mode can decide whether to pass it down or not. Implementing modal and modeless dialogs (which are modes) is quite easy this way.

Besides, modes have a flag saying if lower modes should receive any event and event paint the screen. So, in Betty's Beer Bar, the MenuMode pushes a PlayMode, and while the PlayMode is on the stack, the MenuMode is completely ignored. On the other hand, a mode that lets lower modes draw whatever they want lets you make an overlay map, for example. In FaceIt, the top lights are a mode above the PlayMode.

BTW, my GameMode derived classes don't have Draw(). PlayModes add sprites to the sprite manager and it does the rest (dirty rect tracking, compositing, ...).

My GameMode looks like this :

class GameMode
{
public:
virtual void onUpdate (float fTime);
virtual bool onMouseButtonDown (int nButton, int nX, int nY);
virtual bool onKeyDown (int nKey, int nMod);

protected:

void addSprite (Sprite pSprite);
void removeSprite (Sprte pSprite);
}

(gamemode.h is actually 120 lines long, but you get the point)

tentons
01-03-2005, 01:18 PM
...once things start changing and features get added, that elegant code looks like a bowl of spaghetti marinara....
Which is exactly what lead me to seek a better way to handle things. :o Lucky for me, the game is pretty well encapsulated already and the refactoring isn't too much trouble. But this shows what rushing to get finished can do to your project.

Uhfgood
01-03-2005, 01:27 PM
You have to realize that not everyone uses c++. And most of what everyone is doing up there can be done in plain c with function pointers (that's really essentially what you're doing anyways). So you have two choices either a switch/case/select statement (or if statements), or function pointer/ class c++ stuff...

I'm using blitz right now it doesn't have c++ related oop stuff, nor does it have pointers as we know them. So i'm stuck with a multi-if statement (or select/swtich/case).

Generally I have a variable for the game states themselves these could include pause state, in game state, level-end, etc. I have a seperate variable for program state, that is, menu, in game, instructions, etc... the gamestate variable is usually set to a pause mode when you exit to the menu and the program state is set from ingame to menu...

I'll agree on this point however, if you're using c++ and you're using a c way of doing it, then there's probably something wrong with the code, however... Sometimes you just want it to work, who cares HOW it works ?

Bluecat
01-03-2005, 01:37 PM
You have to realize that not everyone uses c++. And most of what everyone is doing up there can be done in plain c with function pointers (that's really essentially what you're doing anyways). So you have two choices either a switch/case/select statement (or if statements), or function pointer/ class c++ stuff...

Yep. I've used this method in Python too. It's just done a little differently. There's no reason why this method can't be used in any OO language, or in some non-OO languages, given the language functionality.


Sometimes you just want it to work, who cares HOW it works ?

Errrr. Yeah. Sometimes that's true. For production code though, that attitude can be fatal. I'm not saying all the code has to be designed with patterns in mind, or with ultimate flexibility, but some care should be given to how the software works.

HairyTroll
01-03-2005, 10:52 PM
You have to realize that not everyone uses c++. And most of what everyone is doing up there can be done in plain c with function pointers (that's really essentially what you're doing anyways). So you have two choices either a switch/case/select statement (or if statements), or function pointer/ class c++ stuff...

Yes, there are many ways to skin a cat. Disclaimer: Anyone who hates Lisp, cover your eyes. This is what my main loop looks like in Lisp. I extended the language with a new construct (macro) called WITH-EVENTS. It is basically a WHILE loop on steroids in that it handles the standard WHILE (!QUIT), abstracts away the CASE/SWITCH statements and massages the SDL message pump into something that is easier to deal with. For example, it passes all the keypress parameters into :keydown.


;Main SDL loop / Message Pump
(sdl:with-events
(:quit t) ; Quit event received, return TRUE and exit the loop.
(:keydown (state scancode key mod unicode)
(when (= key sdl:SDLK_ESCAPE)
(sdl:quit))) ; ESCAPE key pressed so exit the loop.
(:idle ; When all events are processed, handle :idle
;Set up the random rectangle
(sdl:rectangle rectangle
(random width) (random height)
(random width) (random height))

;'Render' the rectangle to the display by
;filling the display with a random color,
;using the RECTANGLE as a TEMPLATE
(sdl:background
(random 256) (random 256) (random 256) :template rectangle)
(sdl:update-display :template rectangle)))

Diodor Bitan
01-04-2005, 12:21 AM
HairyTroll, I like that. You could upgrade the macro to use some form of pattern-matching:


(sdl:with-events
(:quit t)
(:keydown (:key sdl:SDLK_F4 :alt t)
(sdl:quit))
(:keydown (:key ?key :ctrl t)
(do-ctrl-key-action ?key))
(:idle
something else))


Makes handling various messages easier, without needing the ifs and conds to differentiate between different commands with the same message type.

Gnatinator
01-04-2005, 02:06 AM
I recently contemplated and implemented a similar design (practically the same actually). I use it in extensivley in outer-rim pod digger. Switchin states is so easy with this design! It keeps everything small and easy to read/maintenance too. If you predefine numbers to screen names, you can just do something like this and switch around:

// Goto Menu Screen
CURRENT_SCREEN = SCREEN_MENU;

I dont think you can get any cleaner then that! :D When the game loops though the logic/visual loops:
- Complete the initial common screen commands
- Do a switch and find out what CURRENT_SCREEN is
- Do screen-specific logic/visual
- Complete any remaining common screen commands

I took this a step further and put in a screen initialization layer in:

int const SCREEN_MENU = 1;
int const SCREEN_GAME = 2;

Change_Screen (int _screen){

switch (_screen){
case SCREEN_MENU:
CURRENT_SCREEN = SCREEN_MENU;
// Run menu init processes...
break;

case SCREEN_GAME:
CURRENT_SCREEN = SCREEN_GAME;
// Run game init processes...
break;
}

}

So then I just call Change_Screen with something like Change_Screen(SCREEN_MENU); and im in business :)

svero
01-04-2005, 02:13 AM
Personally I see nothing wrong with mixing object oriented and old style c code. I say use whatever works best and whatever is cleanest for what you're doing. Why try and force a scenario to some type of code or way of thinking if it needlessly complicates it? I'm not saying it always does, but the idea that one should arbitrarily use class thinking if programming in c++ doesn't really jive with me.

Too many people are quick to jump into a design patterns book and the next thing you know you're trying to pass visitor objects to 1000 classes, you need to enable run time type information, and you're using smart pointers and a dozen factory classes and it was all because you were reticent to use one goto statement! Use what's best and what's cleanest and you're most comfortable with. At the end of the day you're the one who's going to have to actually work with the code.

For our state processing we've setup a system of macros! yes MACROS! It's somewhat similar to various MFC things, and it works quite nicely. We generally use 1 file per state and the states all have a set of standard calls. Things like OnLeftClick, OnGameLogic250 (called once every 250 milliseconds), OnAnimate, OnKeyboard etc...

Generally it looks like...

mygame.h

class ShooterGame : public TwSinglePlayerGame
{
DEFINE_STATE (Intro);
};

mygame.cpp (in the game::Init()) call...

REGISTER_STATE (Intro, INTRO);

And then we'll create a corresponding Intro.cpp file that has all the calls for that state. Things like...

void ShooterGame::ENTER_Intro ()

Called when the state is entered.

The are some functions like ChangeState(INTRO), PushState(PLAYING) etc... that allow you to move around from state to state. Each state has about 12 functions the engine calls.

The state engine is actually the one bit of our code that uses a c-like implementation, because that just happened to be a nice clean way to do it. I've tried various class implementations of state handling in the past and they tend to all suffer from similar problems, and while they're not necessarily bad, they're not arguably much better either.

20thCenturyBoy
01-04-2005, 04:40 AM
This guy (http://www.aaroncox.net/tutorials/) also has some useful info about state stacks.

HairyTroll
01-04-2005, 07:08 AM
HairyTroll, I like that. You could upgrade the macro to use some form of pattern-matching:

That's a very good idea. I'll add it to the next release of my Lisp<->SDL bindings, thanks. :)

-Luke

halodrake
01-04-2005, 11:52 AM
Lisp is one of those programming lan'g I've been meaning to learn these days. I mostly haven't yet because of Paul Graham's rangint about it (and I'm not a huge fan of his). I know that's a bad reason for *not* learning a language yet, but meh.

Anyway, I didn't know there was a Lisp out there with SDL bindings (or a Lisp I could embed). Then again, I don't know much about Lisp. Can you point me towards any good resources?

Back on direct topic:

Yeah this was something I figured out about two years ago, and I've been meaning on writing an article about it myself. But no just state machines in order to control flow of a game (menu->game->death->win etc), but instead how finite state machines can be used in all aspects of game and how things communicate with one another. Most of the objects in my games are state machines with specific simple states. You hit an enemy, it goes to *hurt* state, you kill it, it does the *death* state. You get hurt, it set your state, etc, etc.

Diodor Bitan
01-04-2005, 01:41 PM
Original post by halodrake
Lisp is one of those programming lan'g I've been meaning to learn these days. I mostly haven't yet because of Paul Graham's rangint about it (and I'm not a huge fan of his). I know that's a bad reason for *not* learning a language yet, but meh.

I warmly recommend the brilliant book 'On Lisp' by Paul Graham :p (available as pdf at http://www.paulgraham.com/onlisp.html). It goes straight to source of the extraordinary flexibility of Lisp: it's language mutating macros.

I also use "Common Lisp the Language" by Guy Steele (http://www.supelec.fr/docs/cltl/clm/clm.html) as a Lisp reference (go to the book's index).

EpicBoy
01-04-2005, 01:48 PM
Most of the objects in my games are state machines with specific simple states. You hit an enemy, it goes to *hurt* state, you kill it, it does the *death* state. You get hurt, it set your state, etc, etc.
This is the basis of how UnrealScript works, FWIW. The language is based around using states to control how things behave - monsters, weapons, bots, etc.

C_Coder
01-04-2005, 02:19 PM
I use C++ to encapsulate all the objects and a C inner loop. I don't see anything wrong in mixing C++ and C together.

HairyTroll
01-04-2005, 03:32 PM
I warmly recommend the brilliant book 'On Lisp' by Paul Graham :p (available as pdf at http://www.paulgraham.com/onlisp.html). It goes straight to source of the extraordinary flexibility of Lisp: it's language mutating macros.

And don't forget Casting SPELs in Lisp (http://www.lisperati.com/), a tutorial which hits the highlights of why Lisp is like it is.

-Luke

Diodor Bitan
01-04-2005, 04:15 PM
Original post by HairyTroll
And don't forget Casting SPELs in Lisp, a tutorial which hits the highlights of why Lisp is like it is.

Yeah, I googled for that one but couldn't find it. This (http://www.lisperati.com/different.jpg) image is hilarious.

Rod Hyde
01-05-2005, 12:31 AM
And don't forget Casting SPELs in Lisp (http://www.lisperati.com/), a tutorial which hits the highlights of why Lisp is like it is.

Do you recommend any particular version of Lisp for game programming?

--- Rod

halodrake
01-05-2005, 04:58 AM
Hey, thanks! That really helped out a lot.


I warmly recommend the brilliant book 'On Lisp' by Paul Graham (available as pdf at http://www.paulgraham.com/onlisp.html). It goes straight to source of the extraordinary flexibility of Lisp: it's language mutating macros.


I see nothing wrong with this book. It's devoid of the usual self-promoting angsty old man stuff his articles are made of. He is an excellent writer, though, and an excellent technical writer. Thanks for pointing something I would have missed based on political standing.

I'll read up on casting spels in lisp and the other one's too.

As Rod Hyde asked, is there any version of lisp people recommend for embedding into a C++ app?


This is the basis of how UnrealScript works, FWIW. The language is based around using states to control how things behave - monsters, weapons, bots, etc


Heh, I might have to check out some tutorials on UnrealScript then, and see how they do things. Might give me some ideas to my own implementations in Lua.

EpicBoy
01-05-2005, 05:45 AM
Halodrake

Well, if you have a copy of any of the Unreal games, you can export all of the script code from within the editor and check it out for yourself. :)

Diodor Bitan
01-05-2005, 06:26 AM
original post by halodrake
As Rod Hyde asked, is there any version of lisp people recommend for embedding into a C++ app?

I didn't look into this very closely, but so far Corman Lisp looks like it can do the job on Windows. I have used it to create small graphical applications by loading SDL/OpenGL C dynamic link libraries from a Lisp program, and the overhead for the file size is only about 1 MB.

There are also the more serious Lisps (Lispworks, Allegro Common Lisp), but the price looks a bit steep for me, and I don't know how they are doing as a far as download size is concerned.

Rod Hyde
01-05-2005, 07:20 AM
I didn't look into this very closely, but so far Corman Lisp looks like it can do the job on Windows.
Thanks very much. Funnily enough, after googling for it, I ended up on one of Hairy Troll's pages, with SDL bindings for Corman Lisp.

http://www.balooga.com/lisp_sdl.php3

--- Rod

HairyTroll
01-05-2005, 08:15 AM
Do you recommend any particular version of Lisp for game programming?

As Rod Hyde asked, is there any version of lisp people recommend for embedding into a C++ app?

I ended up on one of Hairy Troll's pages, with SDL bindings for Corman Lisp.


I have been meaning to write some actual documentation for those bindings, perhaps this week. As you can see, I also have the bindings to OpenRM (www.OpenRM.org), OpenRM - Gallery (http://www.r3vis.com/Gallery/index.html), a scene graph manager, as well as Cal3D (http://cal3d.sourceforge.net/), a character animation library.

I use Corman Lisp (www.cormanlisp.com). It allows the Lisp component to be created as a DLL and called from your C/C++ application. LispWorks (www.lispworks.com) also has this capability.

Actually embedding Lisp within a C/C++ application is a different matter entirely. I believe ECL (http://ecls.sourceforge.net/) (Embeddable Common-Lisp) is suitable for this sort of thing. The screenshot shows ECL being embedded in QuakeII.

As for the quality of the Lisp distributions, for Windows I would rank LispWorks (www.LispWorks.com) first and then Corman Lisp (www.CormanLisp.com). Where LispWorks would be equivalent to your VisualC++ quality compiler and Corman Lisp ranks somewhere below that. For Windows, OpenMCL (http://www.cliki.net/OpenMCL) is apparently pretty good. I have never used ECL (http://ecls.sourceforge.net/), but I just might after seeing that shot of QuakeII.

For a list of all Lisp distributions, look here (http://www.cliki.net/Common%20Lisp%20implementation)

On the subject of Lisp resources, an excellent Lisp book to read is Practical Common Lisp (http://www.gigamonkeys.com/book/). It is available on the web, and I have already placed an order for it on Amazon. It contains a few more practical examples than Ansi Common Lisp from Paul Graham (for example, writing a Shoutcast server). Another excellent Lisp book available for free is Successful Lisp (http://psg.com/~dlamkins/sl/cover.html).

chanon
01-07-2005, 03:16 AM
(Staying on the 'state' topic)

My approach (for our uncompleted game) is very similiar to ggambett's, but I call them windows. Each window type is a class. Windows can have child windows. It's pretty much a full windowing system, I've implemented buttons, textboxes, dropdowns, scrollbars etc. in it.

A window has onXXX methods to receive input and other messages but each onXXX method also takes in the source window of the event.

For example in:
void onLeftMouseDown(int sourceWindowId, int x, int y);
void onLeftClick(int sourceWindowId);

sourceWindowId would be the id of the window that was clicked on.

This is useful so that a parent window can respond to a click on a button that is one of its child windows.

There is no onDraw because you add sprites to windows (like ggambet's).

There are classes for the top-level windows like the main menu, the game window, and classes for the dialog boxes, for a button, etc.

But just 2 months ago I found out that windows weren't enough. I needed state management also for the gameplay state in the 'game window'.

So I added a seperate finite state machine class. It is a hierarchical finite state machine so that each state itself is a finite state machine and so each state can have a set of possible sub-states (recursively). Each state is a class that implements beginState() updateState() and endState() and has a changeCurrentChildState() method. Each state can have member data and a child state can get it's parent data or change the parent's state to another state through getParentState()

It is modular so that I can use it for other stuff like game entity states or AI states.

Just thought I would share :)

Triple_Fox
01-09-2005, 07:18 PM
Most of my code doesn't need so sophisticated a system - factoring each descrete state into a function and then letting them pass simple messages back to the main loop suffices. The configurable data gets funneled into a single object that every function has access to. The biggest loop, the main gameplay one, is resolved by giving all objects similar power to take control of the program, so that when I need an interrupt for dialogue or menus I just create an instance of the appropriate object.

It seems to work out pretty well, though the end result feels more like a skeleton with gameplay attached than a unified, intricately crafted work of art. ;)