Skip to main content
GameDev.net gamedev.net
🔒 Locked

Game sections like state machine (C++)

Started by Samurai Jack Mar 12, 2007 at 8:56 AM 11 replies 1.9k views
Original Post
Samurai Jack
Samurai Jack
Greetings! I really have a design problem on that one. Feel free to post any better suggestions or hints. So I was wondering, how could I implement virtual sub applications in my game like a state machine? The idea is as follows: Let us say, we have 3 states, Menu, Configuration and Game Play. I would like a class from which everything is derived like this:

class CMenu : public CApplet etc...
class COptions : public CApplet etc...
class CPlaytime : public CApplat etc..

And then in the main loop:

CApplication app;
app.add(new CMenu(), 100);
app.add(new COptions(), 200);
app.add(new CPlaytime(), 300);
The main problem is, how should the CMenu for example tell CApplication that it is time to go to CPlaytime ? One of my ideas was as following:

bool CMenu::Update(float fTimeElapsed, int* changeID)
{
//if anything went wrong, return false it will safe tear down the game
if (KeyDown(DIK_F1)) *changeID = 300;
return true; //update slice was successful
}
And now the CApplication class would check was the update successful? If yes then check if changeID != 0, if so, then change the state. In case that the update failed tear down the game. Does anyone have a better suggestion? I would like to avoid something like that:

int WINAPI ProcessMessages(....)
{
switch (uMsg)
{
case WM_PAINT:
if (State == MENU) DrawMenu();
if (State == GAME) DrawGame();
break;
}
}
The idea is allso that the CApplet does not need to now about the CApplication. I would like to avoid useless parameter passing all the way long. Each CApplet could have an uniqe ID and so the applets could switch. Perhaps there is allready a state machine implemented that does exactly that? Each game dev book I read uses its own state machine for just one state - game play. I do not need a state machine for just one state. I want several states. Thank you in advance!
Sneftel
Sneftel
I make this recommendation every time the subject of FSMs comes up in the context of application state, but here it is again: Don't do the funky pluggable object-oriented thing. It gains you nothing except a hundred lines of boilerplate code which obscures actual program flow. Use an enumeration to represent the current state, and a switch statement to delegate actions for each. Despite the fact that application state is so fundamental to the application, it's actually a really trivial feature which is much better implemented without the 100-ton OO framework.
Kylotan
Kylotan
What's the point of having all these classes if you're just going to refer to them by arbitrary numerical IDs? At least have an enum corresponding to the states.

Then, why not have Update() just return the next state each time. I can't see why an Update() function might fail anyway.
MilfredCubicleX
MilfredCubicleX
Quote:
Original post by Sneftel
Don't do the funky pluggable object-oriented thing


Hmm, I disagree. In my engine, there is a GameState class that is simply a collection of GameObjects. I have one GameState for gameplay, one for menu, etc. To change states, I simply set the m_ActiveGameState variable. The Update method of the GameEngine only calls Update on the objects inside m_ActiveGameState. So if i'm in-game, the menu objects don't get Updated (and thus don't get displayed).. It's not bloated and it looks and works nicely.



Paul_Williams
Paul_Williams
I've also utilized a State Design pattern in my final project. It worked out great. The code for different sections of the game was encased in it's own "State" class... this in turn helped out in debugging and testing the application.
Sphet
Sphet
We use a task manager that has an enumerate list of states. All the tasks (function pointers, really) are added to a list with states (bitmask) they care about. Each frame we walk the list and check if the state is valid - if not the update function isn't called.

When we change states we just call TaskManager::SetNextState( enum ). The Enum is turned into a bitmask with shifting. It's pretty simple but it works.

This has been our approach on five or six titles - it's simple and straightforward. We used task objects just so that we can wrap profiling information into their base class as well as hide some assertions that certain things get called before others. Our initial implementation just used a function pointer per task.

Xai
Xai
I do the non-funky, non-object-oriented solution to these things, ala 1985 good programming practices for C apps.

The "global" game state is logically a global variable, so like any other modular coder who liks to have debuggable programs I have wrap it up in a method to modify it ... something like:

SetApplicationState(ApplicationState newState);

where of course "ApplicationState" is the enumeration.

I also happen to have a "ApplicationSubState" which is just an int, which is used differently by each state as needed (and some don't even need it), but this level could actually be handled very well by the OO style solutions.

The purpose of the simple function rather than hard-coded setting is two fold.

1. A central point to put breakpoints, to use Find-in-files for chaning, and for validating values.

2. The ability to do common things, log state change, trigger OnStateChange notifications, etc, detect if the state is being assign to the same value and therefore no change need happen, etc.
Sneftel
Sneftel
Quote:
Original post by Anonymous Poster
If it's a global ANYTHING in your code can change it and make it a royal pain to track down.
How so? Just stick a data breakpoint on it. Or just grep through your code and see if anything besides SetApplicationState modifies the variable. A GetApplicationState would be preferable, sure, but it's not a particularly serious problem.
Xai
Xai
first of all I said it was "logically" a global variable. In some apps it is "physically" a global variable, in some apps it is physically a private data member of the "Game" class. At either scope I only access it via the getter and setter type methods I have ... so that I CAN debug it.

Look, I am not worried about what the compiler can enforce, I'm worried about what people actually do.

Is it possible to access global variables directly, yes. Is it possible to write different functions and variables that diff only by case yes. Is it possible to have lots of classes with the same name in different name spaces that often need to be used together (requiring full qualification all the time), yes. Do I care, no. Because developers I work with are professionals. When you have a "right" / "official" way to do things that is highly visible and known to all, if you go around that method you damn well better have a good reason when the shit hits the fan and your in danger of getting fired for cowboy coding. It just plain doesn't happen.

I have had to work around bugs in the Microsoft compiler my whole history of development, it hasn't stopped us from shipping products ... you just know what to do, and what not to do ... what is "possible" is largely irrelevant.

Humans needs to prioritize what parts of the system to abstract and harden, the same way you prioritize what parts to optimize. For optimizing you look at a profiler's output and focus your effort where needed, for abstraction you reflect on your common tasks, and bugs that have occured in the past and you focus where it will matter. Its a never ending cycle of increasing abstraction - but you can only add so many layers CORRECTLY in a given amount of time. Its easy to wrap everything, its not so easy to wrap only those things which you know enough about to actually wrap correctly and improve the developer's situation.
davepermen
davepermen
I don't understand the reason to have a state-enum returned if you in the end use state-objects. That is useless abstraction that gains nothing.

Here is more or less how I did it:

Game-States

It's in C#, but it should be portable to c++, too.

In my Engine it consists of about 5 lines of code in the Application class, an interface with about 3 methods, and the rest are modular plugged in and out classes that inherit from that interface. Clean, object oriented, and really no overhead.

And the switch-statements are completely gone. Even the branching is gone, as i can just say currentState.Update(gameTime) and currentState.Draw(). So in the end, it could be even FASTER *harrharr* :)
If that's not the help you're after then you're going to have to explain the problem better than what you have. - joanusdmentia
My Page davepermen.net | My Music on Bandcamp and on Soundcloud
GroZZleR
GroZZleR
How does the switch-statement enum handle multiple gamestates at once? How would you put the main menu over the game (stopping the game temporarily)?
Sneftel
Sneftel
Quote:
Original post by GroZZleR
How does the switch-statement enum handle multiple gamestates at once? How would you put the main menu over the game (stopping the game temporarily)?

It would depend on what, exactly, you'd want to accomplish. A lot of times people will advocate a stack of states, rather than a single state. I think that can be useful in certain circumstances, but in most cases the stackings are restricted enough that you can just boil them down to a normalized enumeration.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.