-game state handler design-
I have always had the problem that this design has lots of weaknesses, for example state transition code and passing data from one state to the other.
For example, you have to make absolutely horrible contortions if you want to write a simple pause menu that pauses a game, allows the user to set a setting, and then continues the game.
I've designed an even better design that I now always use:
// class State { };
Yes, exactly. States are normally just not similar enough to all be handled through the same base class interface. If I want to switch from an old state to a new state, I just create a new instance of this new state (with some options), then have the new state run. After the new state has finished running, the old state can ask the new state about what's happened, then do something with the information, like running another state.
Example:
class QueryState {
public:
QueryState(string s): question(s) {};
void run_query();
string get_result();
private:
string question;
string result;
}
void QueryState::run_query() {
cout << question << flush;
cin >> result;
}
string QueryState::get_result() {
return result;
}
class MainState {
public:
void run();
}
void MainState::run() {
QueryState q("What's your favourite colour? ");
q.run_query();
if(q.get_result() != "Orange") {
cout << "Now I'm sad." << endl;
}
}
int main() {
MainState m;
m.run();
return 0;
}
Nifty, right? Try doing that with your much-too-rigid state model. Every state here has the freedom to define its own interface, states can initialize and run each other (So Fucking Useful) and exchange data so much easier (the child state instance belongs to the parent state instance, therefore the parent instance can call the child instance's methods). Also, this model is very easily extendable and can easily be enhanced for even more awesome effects. For example, say you have a graphical interface, and you need some states to handle input events. Then you can use the following:
#include "gui.h"
class GState {
protected:
void GRun();
virtual void GOnInit() {};
virtual void GOnEvent(GUI::Event * event) { if (event->isQuitEvent()) return;};
virtual void GOnLoop() {};
virtual void GOnRender() {};
virtual void GOnCleanup() {};
void GExit() {running = false};
private:
bool running;
void EventLoop();
}
void GState::GRun() {
running = true;
GOnInit();
while (running) EventLoop();
GOnCleanup();
return 0;
}
void GState::EventLoop() {
GUI::Event Event;
while(GUI::instance()->GetNextEvent(&Event)) {
GOnEvent(&Event);
if (!running) return;
}
GOnLoop();
if (!running) return;
GOnRender();
}
You'll probably recognize this code, as you've probably seen classes very similar to this before, but under a different name, such as App or Game or something. But here, every State that needs an event handler can just inherit from GState and enjoy all its features. Note that there are no public methods and attributes, so the GState class is simply a code template, and other States cannot be held liable for inheriting from GState.
But how can you actually use this GState class?
Easy: Any class GDerived that derives from GState can overload some of the GOnSomething() methods to specify its own behaviour, and if an instance of GDerived is asked to run, then it can call GRun() on itself and continue running with an event handler. Wow.