我试图为我的游戏创建一个州经理,我有4个班级:
游戏状态:
// foward declaration to avoid circular-referency
class StateManager;
class GameState
{
public:
virtual ~GameState() { }
virtual void update(StateManager* gameManager) = 0;
virtual void draw(StateManager* gameManager) = 0;
protected:
GameState() { }
};
StateManager:
class StateManager
{
public:
StateManager();
virtual ~StateManager();
void addState(GameState* gameState);
void update(StateManager* stateManager);
void draw(StateManager* stateManager);
protected:
// store states in a unique_ptr to avoid memory leak
std::vector<std::unique_ptr<GameState> > states_;
};
游戏:
class Game : public StateManager
{
public:
void compute()
{
// call methos of statemanager
update(this);
draw(this);
}
}
MainMenu:
class MainMenu : public GameState
{
public:
// override the pure virtual methos of GameState
void update(StateManager* stateManager)
{
// problem here.
// I need to handle instance of Game in this class,
// but the pointer is one StateManager
}
void draw(StateManager* stateManager) {}
}
当我像这样初始化我的游戏时:game.addState(new MainMenu())
。
我可以在Game
中访问类MainMenu
的唯一方法是通过强制转换指针吗?
// MainMenu class
void update(StateManager* stateManager)
{
Game* game = (Game*) stateManager;
game.input.getKey(ANY_KEY);
//...
}
这是对的吗?有些东西告诉我,我做错了。
答案 0 :(得分:3)
immibis 的答案非常适合解决技术演员问题。
然而,你的设计中有些奇怪的东西。所以我想提供一个替代答案,解决设计问题。
首先StateManager
本身不是GameState
。因此,update()
和draw()
不需要具有相同的签名。你有没有预见到这些StateManager
函数中的一个在参数中被另一个StateManager
调用了?对于Game
,我认为这没有任何意义。所以我建议重构课程(并相应地调整Game
):
class StateManager {
public:
...
void update(); // they always know "this".
void draw();
protected:
...
};
接下来,似乎StateManager
拥有GameState
(使用unique_ptr<>
的受保护矢量,并且您的投射问题表明了它)。所以另一个设计也很有趣:
class GameState {
public:
virtual ~GameState() { }
virtual void update() = 0;
virtual void draw() = 0;
protected:
GameState(StateManager* gameManager) { } // GameStates are created for a StateManager
StateManager* gm; // this manager can then be used for any GameState functions that need it
};
遵循此逻辑,MainMenu
将被重构为:
class MainMenu : public GameState
{
public:
MainMenu (Game* g) : game(g), GameState(g) {}
void update()
{
// NO LONGER NEEDED: Game* game = (Game*) stateManager;
game->input.getKey(ANY_KEY);
// NO MORE PROBLEMS HERE: you always refer to the right object without any overhead
}
void draw() {}
protected:
Game *game; // the owning game.
};
这种替代设计的优点是:
StateManager
指针仅用于StateManager
抽象。 GameState
的{{1}}的Concerete实现可以直接使用它,但是应该仅将它用于特定于游戏的抽象。 主要的不便之处是:
Game
创建GameState
。 StateManager
实现中的冗余指针为代价的,例如GameState
。如果你有数百万,它可能会成为一个内存问题。 答案 1 :(得分:1)
如果您不确定stateManager
是否指向Game
,请使用:
Game *game = dynamic_cast<Game*>(stateManager);
如果game
未指向stateManager
, Game
将包含空指针,否则它将包含指向游戏的指针。
如果您 确定它总是一个Game
并且想要跳过检查(为了获得微小的性能增益),请使用:< / p>
Game *game = static_cast<Game*>(stateManager);
如果stateManager
没有指向Game
,则会产生未定义的行为。