Game States

Started by
4 comments, last by Silgen 11 years, 4 months ago
Going back to basics and writing really simple games (tic-tac-toe, currently) but trying to use a good, clean OO structure. All states are derived from a base class cState:

[source lang="cpp"]class cState {
public:
virtual void enter() = 0;
virtual void update() = 0;
virtual void leave() = 0;
};[/source]
The code below is for the state manager
[source lang="cpp"]void cGame::changeState(cState* targetState) {
currentState->leave();
currentState = targetState;
currentState->enter();
}[/source]

I was thinking of putting each state in its own file, but this means I will need to pass a pointer to the state manager (so that the state manager knows when to switch states) and also an inclusion for all of the header files for the states that it can transition to. This sounds messy. Is there a better way of doing it?
Advertisement
I usually use state classes that use the run and return successor pattern. Basically every function called on a state object returns a smart pointer to the state that the system should be in now. This can be a pointer to the original state. This avoids needing states to know anything about what holds the states. There may not even be an explicit state manager class. Example source for this kind of system is in this thread.

Going back to basics and writing really simple games (tic-tac-toe, currently) but trying to use a good, clean OO structure. All states are derived from a base class cState:

[source lang="cpp"]class cState {
public:
virtual void enter() = 0;
virtual void update() = 0;
virtual void leave() = 0;
};[/source]
The code below is for the state manager
[source lang="cpp"]void cGame::changeState(cState* targetState) {
currentState->leave();
currentState = targetState;
currentState->enter();
}[/source]

I was thinking of putting each state in its own file, but this means I will need to pass a pointer to the state manager (so that the state manager knows when to switch states) and also an inclusion for all of the header files for the states that it can transition to. This sounds messy. Is there a better way of doing it?


I've haven't programmed stateful behaviour yet, but it was my understanding that the states themself manage the transition. (stateA->leave() returns stateB, alternatively leave() sets the next state itself). A state will include the states that it can transition to. Because of polymorphism you can then just store a pointer to the base class.
1) What do these states actually represent?
2) Who calls changeState? Can't the states' own logic trigger a switch to other states?
3) If the concrete states form an intimately related system, unique to your particular game, I'd put them in the same .cpp file. Is there any real reason to spread them around? If they grew so huge compilation started being a problem, which file they reside in would be the least of your problems.

1) What do these states actually represent?
2) Who calls changeState? Can't the states' own logic trigger a switch to other states?
3) If the concrete states form an intimately related system, unique to your particular game, I'd put them in the same .cpp file. Is there any real reason to spread them around? If they grew so huge compilation started being a problem, which file they reside in would be the least of your problems.


This is the right mindset, in my opinion.

Know your needs, and keep it simple.

+---------------------------------------------------------------------+

| Game Dev video tutorials -> http://www.youtube.com/goranmilovano | +---------------------------------------------------------------------+
Thankyou for all of your replies.

I've tried to take into consideration the points raised in the responses, and I've written a simple program to test this out. I was hoping you guys could give me some feedback - or some general pointers etc.

Extract from cState.h
[source lang="cpp"]class cState {
private:
static cState* currentState;
static bool isRunning;
public:
virtual void enter() = 0;
virtual void update() = 0;
virtual void leave() = 0;

void setCurrentState(cState*);
void updateCurrentState();
void exit();
};

class cStateA : public cState {
public:
cStateA(cState*);
~cStateA();

void enter();
void update();
void leave();
};[/source]
Extract from cState.cpp

[source lang="cpp"]#include "cState.h"

//---

cState* cState::currentState;
bool cState::isRunning = true;

void cState::setCurrentState(cState* target) {
currentState = target;
}

void cState::updateCurrentState() {
while(isRunning) {
currentState->update();
}
}

void cState::exit() {
isRunning = false;
}

//---

cStateA::cStateA(cState* caller) {
//Clean up after the caller state and initialise the current one
if(caller)
caller->leave();

setCurrentState(this);
enter();
}

cStateA::~cStateA() {
std::cout << ">> Destructor called for A" << std::endl;
}

void cStateA::enter() {
std::cout << ">> Entering State A" << std::endl;
}

void cStateA::update() {
std::cout << "Enter desired state: ";
if(getch() == 'b') {
setCurrentState(new cStateB(this));
}
}

void cStateA::leave() {
std::cout << std::endl << ">> Leaving State A" << std::endl;
delete this;
}[/source]

This topic is closed to new replies.

Advertisement