Estoy escribiendo un juego, y quiero modelar sus diferentes estados (la analogía de Game Maker sería marcos, supongo) de una manera limpia, orientada a objetos. Previamente, lo he hecho de la siguiente manera:¿Cómo se modelan los estados de aplicación?
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_;
};
Esto apenas era manejable para un proyecto más pequeño. Todas las variables que los estados estaban usando se volcaron en la clase Game, sin embargo, me gustaría mantener la orientación a objetos al máximo, solo exponiendo variables que están compartidas por más de un estado. También quiero poder inicializar un nuevo estado al cambiar a él en lugar de tener que hacerlo en el estado que acaba de finalizar (ya que podría tener múltiples resultados: APP_PLAYING puede pasar a APP_PAUSED, APP_GAMEOVER, APP_NEWLEVEL, etc.).
pensé en algo como esto (PRECAUCIÓN MATERIAL DE FUZZY!):
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_;
};
El problema aquí es que los niveles de clase e instancia están siendo revuelto (estática vs virtual). Los estados deben heredar de AppState, pero, como me imagino, la mayoría de ellos serían clases con miembros totalmente estáticos o, al menos, no necesitaré más de una instancia de una clase (TitleState, LevelIntroState, PlayingState , GameOverState, EndSequenceState, EditorState ... - la pausa ya no sería un estado, en lugar de ser atendido en los estados donde tiene sentido).
¿Cómo se puede hacer de manera elegante y eficiente?