Sto scrivendo un gioco e voglio modellarne i diversi stati (l'analogia con Game Maker sarebbe un frame, suppongo) in un modo pulito e orientato agli oggetti. In precedenza, l'ho eseguito nel seguente modo:Come si modellano gli stati dell'applicazione?
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_;
};
Questo era difficilmente gestibile per un progetto più piccolo. Tutte le variabili utilizzate dagli stati sono state scaricate nella classe Game, tuttavia vorrei mantenere l'orientazione degli oggetti al massimo, solo esponendo le variabili condivise da più di uno stato. Voglio anche essere in grado di inizializzare un nuovo stato quando si passa ad esso piuttosto che doverlo fare nello stato che sta terminando (in quanto potrebbe avere più esiti - APP_PLAYING può passare a APP_PAUSED, APP_GAMEOVER, APP_NEWLEVEL, ecc.).
ho pensato a qualcosa di simile (ATTENZIONE STUFF 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_;
};
Il problema qui è che i livelli di classe e istanza sono sempre mescolate fino (statica e virtuale). Gli stati devono ereditare da AppState, ma - come immagino - la maggior parte di loro sarebbero classi con membri completamente statici, o almeno non avrò bisogno di più di un'istanza da una classe (TitleState, LevelIntroState, PlayingState , GameOverState, EndSequenceState, EditorState ... - la sospensione non sarebbe più uno stato, piuttosto che essere curato negli stati in cui ha senso).
Come può essere fatto in modo elegante ed efficiente?