Sign in to follow this  
no hit wonder

Game State Control

Recommended Posts

I've never liked how I managed my game state. It worked, but I always felt constrained by the structure of it, and I've found things that were really difficult to do with it, that should be easy [like interacting with states that weren't the top state]. I'm sick of this system that I'd made, and am now looking for a new method of doing this, and am wondering how you've all gone about organizing game states.

Share this post


Link to post
Share on other sites
It depends on what you mean by 'state'.

Do you mean states like menu levels, etc? In this case I just use the computer's stack by calling subroutines.

If you mean states like game modes, a good way would probably be to tape out a base GameRules class with methods like OnInit, OnPlayerDie, etc, and then inherit from it for each of your modes, then toss it to a RunGame method that will poke the right parts of the mode in the right sequence.

Whatever you do, always make sure your reset method is up to date -- I've had many, many state bugs that were due to re-entering the game after exiting to the main menu. [rolleyes]

Share this post


Link to post
Share on other sites
the later is what I'm refering to, actual game states like the main menu, the splash screen, the game itself, ingame option menus ect.

The way I used to do it is with a stack, but the way it was set up made it very difficult to communicate between the states. I'm changing the way I'll deal with this issue in the future, though my current project is too far along to really change something this low-level. Trying to lay the brain-work down now so when it comes time to actually do this, I'll have it all figured out.

The way I am doing it now is just plain bad, and I've used this method on a few little stupid projects now [like a tetris clone, and a pong game, a small 2d scroller] and have never been very happy with it, and the horribleness of it is only really showing through now that I'm attempting to make a rather large project. This was a solution that I sorta patch-worked together to begin with, and never really got around to changing it because, at least at the time, it was 'good enough'.

So wondering how the big-brains of the game-forum did theirs, since obviously this is a more important piece of the puzzle then I originally gave it credit for being.

Share this post


Link to post
Share on other sites
I have a stack-based system, and if I need to communicate among different states, I just have the state being pushed store a reference to the state on the top of the stack before it gets pushed.

Share this post


Link to post
Share on other sites
I like to solve problems harder than the one in front of me. Paradoxically, it is often easier.

Over the course of a game, states spawn states. This too me looks like a tree. So instead of having a game state, you can have a game tree. Make certain that the tree is fully visible to all game states, and that the special positions of "yourself" "the root" and "the current state" are accessable to each game state node.

This places the logic and flow of your structure into data and not in code. Your code becomes a simple event loop which tosses messages at the game state tree. But it also means your game state can introspect and know it's own structure.

The disadvantage of placing logic and flow into data is that corrupted data behaves like corrupted program logic. :) Debugging can be a bitch. On the plus side, turning your code into an "interpreter" and "executer" of data is a huge force multiplier on what your code can do.

Share this post


Link to post
Share on other sites
NotAYakk: are there any situations where the tree is not just a stack (i.e. can a game state ever have two children at once)? I can't think of one offhand. However your thinking is good :) ... event messages should go in at the top of the stack and percolate down until someone wants them, imho, thus you don't have to worry about 'finding the right state', the state will find the message.

My usual recommendation on threads like this is for two state stacks: one for the game itself (current level->cinematic, doesn't often go deeper than that) and one for menus (main menu->options menu->graphics options dialogue->advanced shader options dialogue). Thus interacting with the 'current game state' from a menu is really easy; this is what you need to do almost all the time when you think you need to go digging in a normal game state stack. For the unusual cases where you need to talk to states other than (a) the top game state; (b) the state you're in; (c) your parent or (d) your child, each state should be named and there should be a GameEngine.FindState(stateName) method.

Share this post


Link to post
Share on other sites
Ya know, for the longest time, I was so gung ho about clever game state management. Stacks... carefully considered parameterization... multi-level pops... signals... I did it all.

Now? Now I use a handful of booleans and some if statements. I have a function to enter a state, and where necessary, a function to leave it. My rendering and update code has if..else statements and switch statements to handle what bushy inheritance hierarchies did before. When I notice code duplication, I factor it out into a CloseAllMenus or something, and move on.

See, I've come to the conclusion that good OO style in this area is extremely counterproductive. By spending time worrying about an elegant parameterization of how game state works, you end up pushing yourself conceptually away from what is really a very simple problem. Every minute you've spent worrying about whether a GameState's activate() should be re-called when a state above it is popped off could have been ten seconds of coding it how you want it in that particular situation, then fifty seconds of doing other useful stuff.

Share this post


Link to post
Share on other sites
Quote:
Original post by Bob Janova
NotAYakk: are there any situations where the tree is not just a stack (i.e. can a game state ever have two children at once)? I can't think of one offhand. However your thinking is good :) ... event messages should go in at the top of the stack and percolate down until someone wants them, imho, thus you don't have to worry about 'finding the right state', the state will find the message.


As an example of a set of game states that don't form a stack, one game I played recently (Space Rangers 2) had space and planet interfaces/states, but you could access the ship interface/state from either. Interestingly, you could advance time in both the space and planet interfaces, but not in the ship interface.

There's usually very little that can be said about states except that they can take and yield control. So really, you need to encapsulate the data of each state and provide a process for yielding control while retaining the data of the yielding state.

A very simple way of doing this is by giving each state its own class, which inherits from something with a method like "runme(State parent)". This way, you can yield to arbitrary states (even ones you haven't designed yet) by calling "otherstate.runme(this)", and although the stack is not determined wholly by the code (rather, it's determined by the user's actions), the stack will easily keep track of which state called which. If you need access to the parent's state (for example, the amount of money a PC has will need to be accessible from most states), you can add methods to the State class to do that, or you can add a single method to propagate messages up the state stack, with messages caught like exceptions.

If you don't add any methods to the State class except runme, you could accurately call your state class a continuation, and your state maintenance would then be in "continuation passing style" (from the "otherstate.runme(this)"). The only difference from a language-level continuation would be that maintaining the state would have to be done manually; you couldn't just use callcc to return to the current location of the code.

Share this post


Link to post
Share on other sites
I've recently created a state-machine for emulating a radar system. The state machine contains about 100 state and variable objects so far with more to come.

There are two types of state object, CStateAtom, CVariableAtom. Each state change is propagated throughout the whole state machine. The state object decides if it needs to react to the state that it has received.

CVariableAtom's can receive state messages; these change its minimum, default and maximum value. The CVariableAtom can also receive direct value changes - providing the value is within its current limits. The CVariableAtom cannot affect any other state objects. I use this to store variables that change as a consequence of the state machine.

CStateAtom stores a list of states it can change to. If the received state is on the list, the atom will revert to that state. If it changes state, there is an external effector list. This list holds 'cause' versus 'effect' states, or "If I change to X state, I shall transmit Y state BACK to the state machine". This way a CStateAtom can affect other states. The state object with the most external effector states is my STARTUP_DEFAULTS atom. You post a single startup message to the state machine, and the external effector list of this atom posts all of the startup states for all of the atoms.

CStateAtom also holds gui effects, toggle behavior enable and disable states.

Transmission of state to all objects is inefficient, I should probably arrange the state-machine in some sort of tree structure, something I may implement some day. Thus far however I haven’t experienced performance problems.

All of the behavior is defined and tested within an editor. The target software loads the XML file and manages the state from there.

When errors or omissions are found in the behavior, the XML state machine definition is edited and the behavior is fixed - all without a line of code being touched. This will be useful when the customer accepts delivery. This is already useful during development because sionce the state machine is set-up, additions are clicks within the editor rather than new lines of code.

The rest of the program reads the state machine by accessing a façade (an array), rather than accessing the objects directly. When the state object changes state/value, it amends the array element associated with it.

Thus far it’s working well and found to be quite flexible.

One thing I may add is CDecisionAtom. This will say "If I receive X state, and Y state is equal to Z, then transmit state Q to the rest of the state machine". This differs from the other atoms because it checks other states rather than just managing itself.

The other advantage to this, is that once you’ve set-up your framework, its flexible to be reused in all of your programs requiring a state-machine, just change the names of the atoms and possible states and create your state machine in the editor.

From my patchy description, would others be interested in trying this out when its ready for public consumption?

Share this post


Link to post
Share on other sites
Quote:
Original post by Sneftel
Ya know, for the longest time, I was so gung ho about clever game state management. Stacks... carefully considered parameterization... multi-level pops... signals... I did it all.

Now? Now I use a handful of booleans and some if statements. I have a function to enter a state, and where necessary, a function to leave it. My rendering and update code has if..else statements and switch statements to handle what bushy inheritance hierarchies did before. When I notice code duplication, I factor it out into a CloseAllMenus or something, and move on.

Ditto.
enum GAME_STATE {
GAME_RUNNING = 1,
GAME_PAUSED = 2,
GAME_OVER = 3,
GAME_MENU = 4,
/// etc
};

GAME_STATE g_state;

Gets the job done fine without the need to overengineer. Which leaves me free to overengineer in other areas. *shrug*

Share this post


Link to post
Share on other sites
Quote:
Original post by Sneftel
Ya know, for the longest time, I was so gung ho about clever game state management. Stacks... carefully considered parameterization... multi-level pops... signals... I did it all.

Now? Now I use a handful of booleans and some if statements. I have a function to enter a state, and where necessary, a function to leave it. My rendering and update code has if..else statements and switch statements to handle what bushy inheritance hierarchies did before. When I notice code duplication, I factor it out into a CloseAllMenus or something, and move on.

See, I've come to the conclusion that good OO style in this area is extremely counterproductive. By spending time worrying about an elegant parameterization of how game state works, you end up pushing yourself conceptually away from what is really a very simple problem. Every minute you've spent worrying about whether a GameState's activate() should be re-called when a state above it is popped off could have been ten seconds of coding it how you want it in that particular situation, then fifty seconds of doing other useful stuff.


It's really not counterproductive, nor is it as troublesome or difficult as your making it sound. The whole "if( state == menu ) { } else if( state == ingame )..." thing is far too difficult to extend and ugly to read for all but the smallest of games. You're basically doing nothing but making it harder to add more states and making your code harder to read.

Share this post


Link to post
Share on other sites
Quote:
Original post by Mushu
Quote:
Original post by Sneftel
Ya know, for the longest time, I was so gung ho about clever game state management. Stacks... carefully considered parameterization... multi-level pops... signals... I did it all.

Now? Now I use a handful of booleans and some if statements. I have a function to enter a state, and where necessary, a function to leave it. My rendering and update code has if..else statements and switch statements to handle what bushy inheritance hierarchies did before. When I notice code duplication, I factor it out into a CloseAllMenus or something, and move on.

Ditto.
enum GAME_STATE {
GAME_RUNNING = 1,
GAME_PAUSED = 2,
GAME_OVER = 3,
GAME_MENU = 4,
/// etc
};

GAME_STATE g_state;

Gets the job done fine without the need to overengineer. Which leaves me free to overengineer in other areas. *shrug*


Seconded!

I have a state variable for each module I implement though. Like there is a Menu::state, a Game::state, a AI::state, a Input::state, a Model::state etc. Inside, everything is handled by simple if/else and switch statements.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Quote:
Original post by nsto119
Quote:
Original post by Sneftel
Ya know, for the longest time, I was so gung ho about clever game state management. Stacks... carefully considered parameterization... multi-level pops... signals... I did it all.

Now? Now I use a handful of booleans and some if statements. I have a function to enter a state, and where necessary, a function to leave it. My rendering and update code has if..else statements and switch statements to handle what bushy inheritance hierarchies did before. When I notice code duplication, I factor it out into a CloseAllMenus or something, and move on.

See, I've come to the conclusion that good OO style in this area is extremely counterproductive. By spending time worrying about an elegant parameterization of how game state works, you end up pushing yourself conceptually away from what is really a very simple problem. Every minute you've spent worrying about whether a GameState's activate() should be re-called when a state above it is popped off could have been ten seconds of coding it how you want it in that particular situation, then fifty seconds of doing other useful stuff.


It's really not counterproductive, nor is it as troublesome or difficult as your making it sound. The whole "if( state == menu ) { } else if( state == ingame )..." thing is far too difficult to extend and ugly to read for all but the smallest of games. You're basically doing nothing but making it harder to add more states and making your code harder to read.


How is if/else if state ments hard to expand? This is a LOT easier to impliment and maintain then your over engineer "solution". Most professional games keep this part of the engine simple, take a look at their source code. I think I'll go with what has shipped instead of your ideas. Thanks.

Share this post


Link to post
Share on other sites
Quote:
Original post by Anonymous Poster

How is if/else if state ments hard to expand? This is a LOT easier to impliment and maintain then your over engineer "solution". Most professional games keep this part of the engine simple, take a look at their source code. I think I'll go with what has shipped instead of your ideas. Thanks.


...Well, first of all, gamestates should be independent of the engine. Secondly, I've never seen a developer use if-else statements to handle their gamestates, if you can show me an example of a complex game that uses this approach, I'd like to see it.

Tell me, which is easier to understand and maintain?

this:

int Engine::Render( )
{
switch( state )
{
case MENU:
//render menu
break;
case IN_GAME:
//render game
break;
case END_GAME:
//render end credits
break;
case PAUSED:
//render IN_GAME AND PAUSED stuff.
break;
}
}





or this?

int Engine::Render( )
{
current_state.Render( );
}




If I had my render function set up like that, it would be in the area of 200 lines of code. I don't even want to imagine what my Update() function would be. Not to mention, when I add a state, I don't have to search through my entire project to find every place that I change control based on the state - it's all taken care of for me. I don't see how this is "overengineering" when the entire implementation is less than 25 lines of code and offers far more flexibility.

I think I'll go with what's easier to extend, maintain, debug, and read. Thanks.

P.S. You forgot to log in.

Share this post


Link to post
Share on other sites
The boards are seriously messed up the last couple days. I've tried to post my reply to this thread about 10 times in the last 30 hours and it always just sits there and times out after I try submitting my post. My reply is quite long, but it should still go through. Also, many links act as if they are broken, but if I hit the back button and try them again they seem to work, especially in the members Control Panel section. I'll try posting my long reply again (with my source code) in a few days.

-Edit- The broken links problem seems to be solved today, but I am still not able to post my long reply (477 lines with source code).

[Edited by - deadlydog on September 9, 2006 7:04:16 PM]

Share this post


Link to post
Share on other sites
If/else and switch statements can very quickly become difficult to manage and maintain with large state machines. The example I posted above is meant to implement from simple to very complex state machines. If youre just handling v small amount of states then the earlier solutions are appropriate.

Share this post


Link to post
Share on other sites
Quote:
Original post by nsto119
Tell me, which is easier to understand and maintain?

this:
int Engine::Render( )
{
switch( state )
{
case MENU:
//render menu
break;
case IN_GAME:
//render game
break;
case END_GAME:
//render end credits
break;
case PAUSED:
//render IN_GAME AND PAUSED stuff.
break;
}
}



or this?

int Engine::Render( )
{
current_state.Render( );
}



Uh uh uh. You forgot a few lines. Here, I'll help.

#ifndef GAMESTATE_H
#define GAMESTATE_H

class GameState
{
public:
GameState();
virtual ~GameState()
{
}

virtual void Render() = 0;
};

#endif




#ifndef GAMESTATEMENU_H
#define GAMESTATEMENU_H

class GameStateMenu : public GameState
{
public:
GameStateMenu();
virtual ~GameStateMenu()
{
}

virtual void Render()
{
// stuff
}
};

#endif



#ifndef GAMESTATEINGAME_H
#define GAMESTATEINGAME_H

class GameStateInGame : public GameState
{
public:
GameStateInGame();
virtual ~GameStateInGame()
{
}

virtual void Render()
{
// stuff
}
};

#endif



Eh, I'm bored of that. And I only got through two of the states. And I didn't even touch the fifty extra lines or so to get the clever stacking stuff that you'll need if you want to avoid having to explicitly render the "in game" stuff from the "paused" state. Or any of the cumbersome setup/teardown for the states. And I didn't deal with the fact that all those Render functions are going to be spread across several files, with common code either copy/pasted or awkwardly shoehorned into protected GameState functions (thus spreading out the impact of any changes you need to make).

I swear, at times I wonder if programmers enter some weird sort of hypnotic state while writing all this boilerplate code, and then have amnesia afterwards. Even the best code skeleton generation tools won't make all that class creation anywhere near as quick as a simple switch statement. And maintenance? The main game state stuff is probably one of the areas of your game which will change the LEAST.

One of the nice things about C++ is that you aren't forced to do things "the OO way" 100% of the time. Use that flexibility. It's there for a reason.
[/source]

Share this post


Link to post
Share on other sites
Yes, class definitions are big, but they're not in your main loop making it look ugly, they're separated from eachother, and you don't look at them often.

Quote:
Original post by Sneftel

Eh, I'm bored of that. And I only got through two of the states. And I didn't even touch the fifty extra lines or so to get the clever stacking stuff that you'll need if you want to avoid having to explicitly render the "in game" stuff from the "paused" state.


50 lines? That's rediculous. It takes 3 lines of code to render a state and the state underneath it in the stack.

Quote:
Or any of the cumbersome setup/teardown for the states.


Err... what? I don't know where you're getting that from. Maybe you just need to explain what you mean better, but there's absolutely nothing cumbersome about setting up or tearing down a state. That's certainly never been the case in my experience.

Quote:

And I didn't deal with the fact that all those Render functions are going to be spread across several files, with common code either copy/pasted or awkwardly shoehorned into protected GameState functions (thus spreading out the impact of any changes you need to make).


Yes, render functions are going to be spread across several files... one for each class that derives your state class. That's just proper code organization. I'm not sure what common code you're referring to, so I'll leave that at that. I don't see how this is a negative at all, especially as opposed to having one gigantic Render function for all possible game states.

Quote:
I swear, at times I wonder if programmers enter some weird sort of hypnotic state while writing all this boilerplate code, and then have amnesia afterwards. Even the best code skeleton generation tools won't make all that class creation anywhere near as quick as a simple switch statement. And maintenance? The main game state stuff is probably one of the areas of your game which will change the LEAST.


You shouldn't be using the method for speed of writing your code, you should be using it because in the end, it makes your code easier to read and extend. And maybe the state doesn't change much if you're working on a pong clone the states don't change much, but in any fairly large project, you're going to want to switch control flow often, and massive switch statements aren't the way to do it. I have a 10,000 line C#/MDX project right now, with about 9 different classes that derive from IGameState. Like I said before, if you can show me a large-scale project that controls program flow with a bunch of switch or if statements, I'd like to see it. (I did once see a project who's main loop consided of a switch statement with about 900 cases...).

Also, with a stack system, you can far more easily change neccesary data when the control changes. For exame, say I have an "in-game" state that has a timer, and I want to pause the timer when another state gets pushed on top of it. With a stack system, I just call state.OnPushed( ) when it gets pushed below the top and pause the timer there. Where as with an "if-else" system, I have to worry about explicitly pausing the timer every time I switch from the game to another state. Not to mention, that means the timer would have to be globally visible, which is another negative.

Quote:
One of the nice things about C++ is that you aren't forced to do things "the OO way" 100% of the time. Use that flexibility. It's there for a reason.


That's completely irrelevant, because 1) No language forces you to do things the "OO way" and even the most OO languages allow for your type of state management, and 2) I don't choose to use the system I use because it's "more OO", I use it because of all the advantages I've listed.

Share this post


Link to post
Share on other sites
For the projects I've worked on, I've found that a combination of a straightforward enum-switch-case and a more designed state system seems to be the best (at least to my liking).

For the main game states, like SPLASHSCREEN, MAINMENU, OPTIONS, PROFILESELECT, INGAME, a simple switch-case often seems to cut it. It's easy to write, has no hidden logic flow and it's quick to manage.

But then, when the project gets larger and the number of features increase, I've found that a simple state system was hard to comprehend, because: (disclaimer: my experience, I'm not claiming the absolute truth)

1) The code easily deteriorates into a something like Input() { long switch-case; } Update() { long switch-case; } Render() { long switch-case; } In each case block, there's typically lots of code involved, so the code gets refactored into a separate function for each state. In the end, the switch-cases could have been refactored into a C-style jump table or a function pointer (or OO-style).

2) Usually you begin with the simple state-enum and switch-cases, but when the number of features increase, you need to add more and more states to the enum, and in some point you note that the different states are not orthogonal anymore, e.g. you'd have states GAMERUNNING and GAMEOVER, and you go on to add APPLYINGPOWERUP-state. In this case, you either make the system so that you can't change from GAMEOVER state to APPLYINGPOWERUP-state, or make a separate state machine for the ingame actions stuff (RELOADING, SELECTINGTARGET, APPLYINGPOWERUP). The first approach is a bit bad, since every time you add a new state, you have to go over your states and restrict all illogical transitions. The second approach I found equally bad or even worse, because the game logic would now be spread over several distinct state machines that have no knowledge over each other.

3) I wanted to have transitions between states, (fade-out/init data for next state/fade-in) for which the switch-case approach doesn't really stretch too well. Additionally, in some cases you would be interested in what was the previous state, how long we have been in the current state and so on, so I definitely needed some kind of manager.

So, I decided to use a state manager where you set all the possible states and transitions in a data file but in the code, I do a straightforward switch(StateMachine->CurState()). This way, I have more power in the states because of the help of the manager, but still can quickly change the behavior of the code. The OO-approach would be ideal IF you knew in advance all the states there will be and didn't have to change them ever. But I found it's a bit clumsy to write all these classes for each state, especially when the state would only hold a few lines of actual code.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
I use a level class

class CLevel
{
public:
CLevel();
virtual ~CLevel();

virtual void LoadResources(int id);
virtual void FreeResources();
virtual void Draw(GLib * g);
virtual int HandleEvent(int evt, WPARAM wParam, LPARAM lParam);
virtual int getID();
private:

};

Then derive classes of this for each display.

The message loop becomes

case WM_KEYUP:
case WM_CHAR:
case WM_LBUTTONDOWN:
case WM_KEYDOWN:
curlevel->HandleEvent(message, wParam, lParam);
return 0;


The display loop becomes

curlevel->Draw(&g);
SwapBuffers (hDC);
sfx.Update();
Sleep (1);

The state change code becomes
curlevel->FreeResources();
old_mode=curlevel->getID();
curlevel=levels[RequestChange];
curlevel->LoadResources(old_mode);


I don't need to build up a stack with current levels on it as the max depth of the stack for this game would be 2, so I just pass in last state to LoadResources and have a single global variable RequestChange which allows any state to trigger a swap to any other state. You could of course add a stack to it trivially.

Simple and works.




Share this post


Link to post
Share on other sites
In Game Programming Gems 5, James Boer published an article titled "Large-Scale Stack-Based State Machines", which described a simple system for handling game states. States can be accessed at any time via name, as each game state is associated with a string. The state manager maintains a map of string-gamestate pairs (the state registry) in addition to the state stack (which actually contains state names rather than the states themselves). It supports different stack commands: Push a state, Pop an arbitratry number of states, Pop all states and Replace any state in the stack. The "default" state (the bottom of the stack) can never be popped, though it can be replaced. Commands are not executed immediately, but are placed in a command queue that is processed when the game state manager's update method is called. The queue can be flushed anytime a command you send a command.

The game state interface declares 5 methods:

init - called when a state is pushed on the stack and passed the name of the state is being pushed on top of.

term - called when a state is popped from the stack and passed the name of the state that is the new top.

override - called when a state loses its position as the stack top due to a push operation.

resume - called when a state regains the top position due to a pop operation.

update - called by the game state manager's update method

When you call the state manager's update method, it first processes any stack commands in the queue, then iterates the state stack and calls the update method of each state.

This is a very flexible system. State instances don't need to know about other state instances. Anytime you want to push a state onto the stack you do so by name. This can be configured in a text file, scripted, or whatever. You can access any state in the stack, or any registered state in the map, at anytime. You can use the override method to pause rendering, pause update logic, or both. It becomes trivial to implement cascading menu screens, menus with background rendering, or whatever you can think of. One block of effort to implement the system (or adapt Boer's), and then you have a reusable, highly flexible game state management system that is much cleaner to implement that the old switch thing. I think implementing, at most, five methods for each state is peanuts compared to implementing the switch system, particularly for complex interactions.

Boer's implementation was in C++. For kicks, I have ported it to Java, D, and even C. I also implemented a C++ version that uses Lua to implement the game states. The C++/Lua version allows states to be registered via script, rather than hardcoding state registration. The Java version does the same via dynamic instantiation so that states could be defined in a script, an XML file, a properties file, a text file, a database table, or any other source by providing state name-class name pairs (actually, the C++/Lua version could do all of that as well with state name-script path pairs).

If you implement this system along with code to create an app window, change display modes, intialize input, and other basic app plumbing, it becomes trivial to put together test applications, demos and prototypes by implementing a single game state. It doesn't have to be part of a game engine, but such a framework is very, very useful.

Share this post


Link to post
Share on other sites
Quote:
Original post by nsto119
I don't see how this is a negative at all, especially as opposed to having one gigantic Render function for all possible game states.

I'm not sure where you got that from, and it seems to be behind most of your other objections to the procedural programming approach. Where did I advocate having one gigantic Render function?

In terms of commercial games using this approach, I'm afraid that I can't post the code (NDA, copyright, whatnot). Suffice to say that about 1/2-2/3 of the codebases I've seen use the OO-centric method, and 1/3-1/2 use the procedural method. And it really makes very little difference, except that the programmers who used the procedural method obviously lost very little sleep over it compared to many of the posters here.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sneftel
Quote:
Original post by nsto119
I don't see how this is a negative at all, especially as opposed to having one gigantic Render function for all possible game states.

I'm not sure where you got that from, and it seems to be behind most of your other objections to the procedural programming approach. Where did I advocate having one gigantic Render function?


Well, I suppose you didn't. I meant though, having one single function with code to render all gamestates.

And just so we're clear, I'm not the "OO or bust" type of person that this site crawls with. I prefer to actually get things done rather than write everything with a perfeclty academic OO model. I program procedurally when I think it makes things easier for me, I just really feel that this problem is made simpler when OO-ified.

Share this post


Link to post
Share on other sites
I think one of the reasons why I strongly dislike the idea of spending so much time on a clever and robust and feature-laden GameState architecture is a misconception that it somehow forms the "bedrock" of a game's architecture, and the ramifications of that (I'll get back to that last bit at the end). That without a main loop that you could post on the cover of Software Engineering Weekly there's no way to keep the rest of the code in line. It's an intuitive viewpoint; after all, the GameState stuff IS what ends up calling everything else. To some extent I agree with that intuition, inasmuch as a hairy thousands-of-lines Render() function like you describe runs far afoul of No Broken Windows. And yet it is IMHO 180 degrees wrong, because the GameState does so little, and matters so little. It changes between every few seconds and every few hours, you could count the states on your fingers. Combine the complications that often arise between switching between states and the small number of states and you have the very picture of an overcomplicated and underworked state system. Put differently, I see programmers coming up with a procedural conception of how state and state transitions work, then shoehorning that into an OO design which tries to do the same thing. I don't see a need for that.

As I write this, I'm working on the fifth iteration (the third from-scratch iteration) of my job/action dispatch system, which handles AI state, for a game I'm working on. It's heavily OO, with careful separation of responsibilities and an emphasis on no-need-to-look-back subclassing. Each iteration has been clearer and more robust. I've spent such a huge amount of time on this because it MATTERS so much. There are hundreds of states and hundreds of agents, and each agent changes state every few frames in response to a wide variety of internal and external stimuli, with a need to keep outside coordinators informed to keep tasks from slipping between the cracks. I think game programmers need to be able to identify the right tool for the job, and having 5-10 classes for the relatively simple function of remembering whether the game's paused or not sets a programmer down the onerous path of assuming that all tasks which are so simple nevertheless require the red-carpet OO approach.

Share this post


Link to post
Share on other sites
State:
updateLogic(), updateFrame(), render(), onAdded(), onRemoved(), onKey, onChar
with util methods updateLogicPrevious(), updateFramePrevious(), renderPrevious(), disableMe() and getStateManager()

StateManager:
addState(string name, State*), enableState(string name), disableState(string name)
updateLogic(), updateFrame(), render()

Reminds alot of the gems6 state design Aldacron pointed out but is different from on some key points:
*states are disabled and enabled and not popped. You enable game instead of pushing it.
*this lets you choose the state order/priority, the console is always on the top for example

I got 9 states(splash, game, press key(change binding), meny, in-game meny, pause, console input and history/messages & cheat menu). The states help me keep a clean code and not getting my head wrapped up in the different combinations I can have. With this system all the state needs to worry about is if it should render other states and/or direct input to other states.

Other systems might be good, but this works for me(and I feel good about it ;) )

hth

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this