views:

262

answers:

6

Okay: I'm fairly new to C++ and static languages on a whole. Coming from years of ruby (and other dynamic languages) I don't know if this is possible.

I've been making a game state system for... well a game. I want to make the system easy for me to cut and paste into other games without any (or very few) changes.

The two things I am wanting to improve are the way in which states switch and the way in which state pointers are held.

There could be any number of states, but there will always be at least 2 to 3 states active in memory.

Ugliness No 1.

Currently I have a state manager class with something like this in it:

void StateManager::changeState(StateID nextStateID)
{
    // UNFOCUS THE CURRENT STATE //
    if (currentState())
    {
        currentState()->onUnFocus();

        // DESTROY THE STATE IF IT WANTS IT //
        if(currentState()->isDestroyedOnUnFocus()) {
            destroyCurrentState();
        }
    }

    if (m_GameStates[nextStateID]) {
        // SWITCH TO NEXT STATE //
        setCurrentState(nextStateID);
    }
    else
    {
        // CREATE NEW STATE //
        switch (nextStateID)
        {
        case MainMenuStateID:
            m_GameStates[MainMenuStateID] = new MainMenuState;
            break;
        case GameStateID:
                        m_GameStates[MainMenuStateID] = new GameStates;
            break;
        };
        setCurrentState(nextStateID);
    }

    // FOCUS NEXT STATE //
    currentState()->onFocus();
}

This approach works but I don't feel it's very nice.

Is it possible to pass a type? And then call new on it?

new NextGameState;  // Whatever type that may be.

Can poloymophism help here? All States are derived from a class State.

Ugliness No 2.

Another thing I think needs some improvement is the way I've been storing the states.

State* m_GameStates[MaxNumberOfStates];

All the states are initialized to NULL, so I can test if a state is there, and if not it creates one when needed.

It works well as I can call the current state:

m_GameStates[m_CurrentState];

However, I don't like this for two reasons. It seems a bit of a waste having an array full of NULL pointers when there will only be 2 or 3 pointers active at any one time. [Editor's note: what is the second reason?]

I thought about shifting this into a vector_ptr, but didn't as it would create extra complications with checking to see if a state exists. And the vector seems to reinforce Ugliness No 1. as I need to have a list to check each state.

Any advice or direction appreciated.

Thanks, Phil.

+2  A: 

Use a enum(eration) to define all possible states (its something like a list with constants). Just create for one object one variable that holds the state and change the variable whenever you need to change it.

Quonux
I'm trying to avoid defining a list of all possible states. Well I'm trying to find out if there is a better way.
PhilCK
+2  A: 

As soon as you say States, I think of the State pattern.

Basically, you can derive a bunch of objects from a State base class. All actions related to a state occur against the current state maintained by the state manager. States will move from state to state via the manager.

For instance, you can have a Paused and Unpaused state, each with a buttonPressed event. When you press a button, the current state is delivered the event. If it's in Paused, and the button was the pause button, move to Unpaused. Vice versa for Unpaused.

SB
+1  A: 

For your first problem, yes, you can pass in a type, with some caveats.

I've added a comment under your question, asking for a bit more information. Until we get that, I can't really say how it should be done, but read up on templates.

You can make a function template, which can be passed a type, for example like this:

template <typename T>
void Foo() {
  T* x = new T();
  ...
}

Foo<int>() // call Foo with the type T set to 'int'

There are some limitations to this, as the types have to be specified at compile-time, but it is a very powerful language feature.

Another option, which might work better since you seem to have an association between a variable (MainState) and a type (MainMenu), might be the use of traits classes. Again, I'm unsure of exactly how it'd be done in your case, since we haven't seen the entirety of the function (in particular, what type is MainState, and how/when is it created?)

It might also be possible to solve the problem through polymorphism, but again, I'd need to see a bit more of the context to suggest a solution.

For your second problem, you can use the standard library map:

#include <map>

// I'm not sure what type m_CurrentState is, so use its type instead of KeyType below
std::map<KeyType, State*> m_GameStates;

// and to perform a lookup in the map:
GameStates[m_CurrentState];

Finally, a really really important bit of advice:

Stop using pointers everywhere. Stop calling new to create new objects. As a general rule, objects should be created on the stack (Instead of Foo* f = new Foo;, just do Foo f;

And instead of using pointers, you'll often want to just copy the object itself. Alternatively, create references instead of pointers.

And when you do need to use dynamic memory allocations, you still shouldn't use new directly. Instead, create a wrapper object, which internally allocates what it needs with new in its constructor, and frees it again in the destructor.

If you do this correctly, it pretty much solves all the headaches of memory management.

The general technique is called RAII.

jalf
Thanks for the pointer advice, this all looks really cool thanks.
PhilCK
@jalf Personaly I think a template is kind of overkill for this. It makes thinks difficult(more difficult) that are very easy and simple.
InsertNickHere
@InsertNickHere: perhaps. I mentioned it because @PhilCK asked "Is it possible to pass a type?", which is pretty much the #1 use case for templates. You're right, it may not be the best solution in this specific case, but I think it's worth mentioning the option at least. :)
jalf
@jalf You are right, its a nice and good post for learing.
InsertNickHere
I would also add the caveat that your map lookup inserts a default item if it isn't found you need to use find to check for existence.
stonemetal
+2  A: 
void StateManager::changeState(StateID nextStateID)
{
     leaveState(actualState); 
     enterState(nextStateID);
}

I really like this one - as easy as it could be. ;-)

What I want to tell you - I think doing creation/deleting your stats in the changeState Function is too much of logic in there - it just is supposed to change the state, right?

Edit: To come to your 2 question - I don't think using this array is really a waste - you are talking about 3 fields, not 300 or so. So if you like using arrays - go for it. If you don't, the map would be my choose, it makes things easy if you want to check if there is a state created or not and you are not limited to a magic number "maxStates". You could possible check if there is enough ram and then create X states, not fixed 2-3.

InsertNickHere
A: 

Take a look at Boost Statechart Library

Nemanja Trifunovic
+1  A: 

For generating states you want a factory. That way the state id stays nice an generic. For storing states I would go with a std::map

stonemetal