Я пишу игру и хочу смоделировать ее различные состояния (полагаю, аналогия с Game Maker — кадры) чистым, объектно-ориентированным способом. Раньше я делал это следующим образом:
class Game
{
enum AppStates
{
APP_STARTING,
APP_TITLE,
APP_NEWGAME,
APP_NEWLEVEL,
APP_PLAYING,
APP_PAUSED,
APP_ENDED
};
typedef AppState(Game::*StateFn)();
typedef std::vector<StateFn> StateFnArray;
void Run()
{
// StateFn's to be registered here
AppState lastState(APP_STARTING);
while(lastState != APP_ENDED)
{
lastState = GetCycle_(lastState);
}
// cleanup
}
protected:
// define StateFn's here
AppState GetCycle_(AppState a)
{
// pick StateFn based on passed variable, call it and return its result.
}
StateFnArray states_;
};
Это было едва ли управляемым для меньшего проекта. Все переменные, которые использовались состояниями, были сброшены в класс Game, однако я хотел бы максимально сохранить объектно-ориентированность, предоставляя только те переменные, которые используются более чем одним состоянием. Я также хочу иметь возможность инициализировать новое состояние при переключении на него, а не делать это в состоянии, которое только что закончилось (поскольку оно может иметь несколько результатов — APP_PLAYING может переключиться на APP_PAUSED, APP_GAMEOVER, APP_NEWLEVEL и т. д.).
Я подумал о чем-то вроде этого (ВНИМАНИЕ! НЕЧЕТКАЯ ВЕЩЬ!):
struct AppState
{
enum { LAST_STATE = -1; }
typedef int StateID;
typedef std::vector<AppState*> StateArray;
static bool Add(AppState *state, StateID desiredID);
// return false if desiredID is an id already assigned to
static void Execute(StateID state)
{
while(id != LAST_STATE)
{
// bounds check etc.
states_[id]->Execute();
}
}
AppState() {};
virtual ~AppState() {};
virtual StateID Execute() =0; // return the ID for the next state to be executed
protected:
static StageArray stages_;
};
Проблема здесь в том, что уровни класса и экземпляра смешиваются (статические и виртуальные). Состояния должны наследоваться от AppState, но, как я себе представляю, большинство из них будут классами со полностью статическими членами или, по крайней мере, мне не понадобится более одного экземпляра из одного класса (TitleState, LevelIntroState, PlayingState , GameOverState, EndSequenceState, EditorState... — пауза больше не будет состоянием, а будет выполняться в тех состояниях, где это имеет смысл).
Как это сделать красиво и эффективно?