Reputation: 162
I'm trying to create a state manager for my game, and I have 4 classes:
GameState:
// 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_;
};
Game:
class Game : public StateManager
{
public:
void compute()
{
// call methos of statemanager
update(this);
draw(this);
}
}
And 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) {}
}
When I initialize my game like so: game.addState(new MainMenu())
.
The only way I can access the class Game
in MainMenu
is by casting the pointer?
// MainMenu class
void update(StateManager* stateManager)
{
Game* game = (Game*) stateManager;
game.input.getKey(ANY_KEY);
//...
}
Is this right? Something tells me I'm doing it wrong.
Upvotes: 2
Views: 2935
Reputation: 73366
The answer of immibis is perfect for addressing the technical casting issue.
Nevertheless there's something weird in your design. So I'd like to provide an alternative answer, addressing the design issues.
First a StateManager
is not itself a GameState
. So there is no need to have the same signature for update()
and draw()
. Do you ever foresee to have one of these StateManager
functions called with another StateManager
in argument ? For a Game
, I think this makes no sense. So I'd recommend to refactor the class (and adapt Game
accordingly):
class StateManager {
public:
...
void update(); // they always know "this".
void draw();
protected:
...
};
Next, it seems that the StateManager
owns the GameState
(the protected vector using a unique_ptr<>
,and your casting question suggest it). So another design could be interesting as well:
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
};
Following this logic, the MainMenu
would be refactored as:
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.
};
The advantages of this alternative design are:
StateManager
pointer is used only for the StateManager
abstraction. GameState
, which rely on Game
, can use it directly, but should use it only for Game specific abstractions. The main inconveniences are:
GameState
must always be created for one specifc StateManager
. GameState
implementations such as MainMenu
. If you have millions of them, it could become a memory issue. Upvotes: 3
Reputation: 58848
If you're not sure whether stateManager
points to a Game
, then use:
Game *game = dynamic_cast<Game*>(stateManager);
game
will then contain a null pointer if stateManager
did not point to a Game
, otherwise it will contain a pointer to the game.
If you are sure it's always a Game
and want to skip the check (for a tiny performance gain), use:
Game *game = static_cast<Game*>(stateManager);
which will produce undefined behaviour if stateManager
doesn't point to a Game
.
Upvotes: 1