我正在编写一个游戏,我希望以干净,面向对象的方式模拟其不同的状态(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等)。 / p>
我想到了这样的事情(小心!模糊的东西!):
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 ... - 暂停将不再是一个状态,而不是在有意义的状态下处理。)
如何优雅高效地完成工作?
答案 0 :(得分:10)
以下文章提供了一种管理游戏状态的简单方法:
http://gamedevgeek.com/tutorials/managing-game-states-in-c/
基本上,你保持一堆游戏状态,然后运行顶级状态。你是对的,许多州只有一个实例,但这不是一个真正的问题。实际上,你所谈论的许多州可能有多个实例。 E.g:
push TitleState
push MenuState
push LevelIntroState
change_to PlayingState
change_to GameOverState
pop (back to MenuState)
...您可以重新开始使用LevelIntroState
的新实例,依此类推。
答案 1 :(得分:3)
我正在使用某种类型的factory pattern与state pattern相结合。
代码可能有点乱,但我会尝试清理它。
这是你将从所有州推出的课程,比如菜单,游戏等等。
class GameState {
public:
virtual ~GameState() { }
virtual void Logic() = 0;
virtual void Render() = 0;
};
此类将成为处理不同状态的接口。您可以动态地动态添加和ID。
class State {
public:
State();
virtual ~State();
void Init();
void Shutdown();
void SetNext( std::string next_state );
void Exit();
bool Logic();
void Render();
protected:
bool Change();
std::string state_id;
std::string next_state;
GameState *current_state;
std::vector<std::string> state_ids;
StateFactory *state_factory;
bool is_init;
};
我正在使用仿函数来处理不同GameState衍生物的创建。
class BasicStateFunctor {
public:
virtual GameState *operator ()() = 0;
};
template<class T>
class StateFunctor : public BasicStateFunctor {
public:
StateFunctor() { }
GameState *operator ()() {
return new T;
}
typedef T type;
};
最后一家工厂将存储和管理不同的州。
class StateFactory {
public:
StateFactory();
virtual ~StateFactory();
bool CheckState( std::string id );
GameState *GetState( std::string id );
template<class T> void AddState( std::string id );
private:
typedef std::map<std::string, BasicStateFunctor*>::iterator StateIt;
std::map<std::string, BasicStateFunctor*> state_map;
};
在您的定义文件中: 在这里,我确实遗漏了很多东西,但希望你能得到这个想法。
bool StateFactory::CheckState( std::string id )
{
StateIt it = state_map.find( id );
if( it != state_map.end() )
return true;
else
return false;
}
GameState *StateFactory::GetState( std::string id )
{
StateIt it = state_map.find( id );
if( it != state_map.end() )
{
return (*(*it).second)();
} else {
//handle error here
}
template<class T> void StateFactory::AddState( std::string id )
{
StateFunctor<T> *f = new StateFunctor<T>();
state_map.insert( state_map.end(), std::make_pair( id, f ) );
}
void State::Init()
{
state_factory = new StateFactory();
state_factory->AddState<Game>( "game" );
current_state = state_factory->GetState( "game" );
is_init = true;
}
void State::SetNext( std::string new_state )
{
//if the user doesn't want to exit
if( next_state != "exit" ) {
next_state = new_state;
}
}
bool State::Change()
{
//if the state needs to be changed
if( next_state != "" && next_state != "exit" )
{
//if we're not about to exit( destructor will call delete on current_state ),
//call destructor if it's a valid new state
if( next_state != "exit" && state_factory->CheckState( next_state ) )
{
delete current_state;
current_state = state_factory->GetState( next_state );
}
else if( next_state == "exit" )
{
return true;
}
state_id = next_state;
//set NULL so state doesn't have to be changed
next_state = "";
}
return false;
}
bool State::Logic()
{
current_state->Logic();
return Change();
}
以下是您使用它的方式: 初始化并添加不同的状态,我在Init()中进行。
State.Init();
//remember, here's the Init() code:
state_factory = new StateFactory();
state_factory->AddState<Game>( "game" );
current_state = state_factory->GetState( "game" );
is_init = true;
对于帧功能
State.Logic(); //Here I'm returning true when I want to quit
对于渲染功能
State.Render();
这可能不完美,但对我来说效果很好。 为了进一步推进设计,你需要为State添加Singleton,并可能将StateFactory作为State中的隐藏类。
答案 2 :(得分:2)
这是我的解决方案:
对于渲染,我正在使用具有优先级的图层。因此,每个游戏都将在透明画布上渲染,图层渲染器将以正确的顺序渲染它们。这样,每个游戏都可以更新自己的图层而不会打扰其他人正在做的事情。
答案 3 :(得分:1)
我使用带有GameStates列表的游戏状态管理器,其中列表中的每个Item都是一个实现IGameState的“GameState对象”,并且有两个方法.render()和.HandleInput()
此GameStateManager实现为单例,因此任何状态都可以通过调用
跳转到任何其他状态 GameStateManager.gi().setState("main menu")
主循环看起来像这样
while(isRunning)
{
GameStateManager.gi().getCurrentState().handleKeyboard(keysobject);
GameStateManager.gi().getCurrentState().handleMouse(mouseobject);
GameStateManager.gi().getCurrentState().render(screenobject);
}
创建状态的方法,只需创建一个实现IGameState的新类,并将其添加到GameStateManager。
(注意:这是在主游戏中制作迷你游戏的一种非常方便的方式)