你如何模拟应用程序状态? [英] How do you model application states?
问题描述
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将在
AppState lastState(APP_STARTING);
while(lastState!= APP_ENDED)
{
lastState = GetCycle_(lastState);
}
//清理
}
protected:
//定义StateFn在这里
AppState GetCycle_(AppState a)
{
//根据传递的变量选取StateFn,调用它并返回其结果。
}
StateFnArray states_;
};
这对于一个较小的项目来说几乎不能管理。状态所有使用的变量被转储到Game类中,但是我想要保持面向对象的最大化,只暴露由多个状态共享的变量。我也希望能够在切换到它时初始化一个新状态,而不是在刚刚完成的状态下进行初始化(因为它可能有多个结果 - APP_PLAYING可以切换到APP_PAUSED,APP_GAMEOVER,APP_NEWLEVEL等)。 / p>
我想到了这样的东西(注意!FUZZY STUFF!):
struct AppState
{
枚举{LAST_STATE = -1; }
typedef int StateID;
typedef std :: vector< AppState *> StateArray;
static bool Add(AppState * state,StateID desiredID);
//如果需要,则返回false ID是已分配给
的空白static void Execute(StateID state)
{
while(id!= LAST_STATE)
{
// bounds check etc
states_ [id] - > Execute();
}
}
AppState(){};
virtual〜AppState(){};
virtual StateID Execute()= 0; //返回要执行的下一个状态的ID
protected:
static StageArray stages_;
};
这里的问题是类和实例级别变得混乱(静态vs虚拟)。状态需要从AppState继承,但是我可以想像,大部分都是具有全静态成员的类,或至少我不需要一个类的多个实例(TitleState,LevelIntroState,PlayingState ,GameOverState,EndSequenceState,EditorState ... - 暂停不再是一个状态,而不是在有意义的州被照顾)。
如何
以下文章给出了一种很好的简单的管理游戏状态的方法:
http://gamedevgeek.com / tutorials / manage-game-states-in-c /
基本上,你维护一堆游戏状态,只是运行最高状态。你是对的,许多州只会有一个实例,但这不是一个真正的问题。实际上,你说的很多州可能有多个实例。例如:
push TitleState
push MenuState
push LevelIntroState
change_to PlayingState
change_to GameOverState
pop(返回MenuState)
...你可以重新开始一个新的实例 LevelIntroState
,等等。
I'm writing a game, and I want to model its different states (the Game Maker analogy would be frames, I guess) in a clean, object-oriented way. Previously, I've done it in the following way:
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_;
};
This was hardly manageble for a smaller project. All the variables that the states were using were dumped in the Game class, however I'd want to keep object-orientedness to a maximum, only exposing variables that are shared by more than one state. I also want to be able to initialize a new state when switching to it rather than having to do it in the state that's just finishing (as it might have multiple outcomes - APP_PLAYING can switch to APP_PAUSED, APP_GAMEOVER, APP_NEWLEVEL, etc.).
I thought of something like this (CAUTION! FUZZY STUFF!):
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_;
};
The problem here is that the class and instance levels are getting jumbled up (static vs virtual). The states need to inherit from AppState, but - how I'd imagine - most of them would be classes with all-static members, or, at least I won't need more than one instance from one class (TitleState, LevelIntroState, PlayingState, GameOverState, EndSequenceState, EditorState... - pausing would no longer be a state, rather than being taken care of in the states where it makes sense).
How can it be done elegantly and efficiently?
The following article gives a nice, simple way to manage game states:
http://gamedevgeek.com/tutorials/managing-game-states-in-c/
Basically, you maintain a stack of game states, and just run the top state. You're right that many states would only have one instance, but this isn't really a problem. Actually, though, many of the states you're talking about could have multiple instances. E.g.:
push TitleState
push MenuState
push LevelIntroState
change_to PlayingState
change_to GameOverState
pop (back to MenuState)
... and you can start over with a new instance of LevelIntroState
, and so on.
这篇关于你如何模拟应用程序状态?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!