maybe you use static clas for game management
No, there is no reason to do this.
Secondly, don’t use switch cases to manage such a large web of possible states. There are many patterns for this, one of which I have documented here: General Game/Engine Structure.
A simple switch over a game state enumeration is the easiest ways of implementing a simple game. Your state system, while a better fit for larger projects, is probably unnecessary here. The OP is still working out how to get simpler classes to work, let alone a more complex polymorphic solution.
@OP Your Application class should look more like this:
class Application {
public:
Application(const std::map<std::string, std::string> &theme);
void run();
private:
Sprite background;
Sprite buttonOn;
Sprite buttonOff;
Sprite logo;
};
The sprites should be initialised in a constructor. One way to do this is to use an initialiser list:
Application::Application(const std::map<std::string, std::string> &theme)
:
background(theme["Background"]),
buttonOn(theme["ButtonOn"]),
buttonOff(theme["ButtonOff"]),
logo(theme["Logo"])
{
}
Your initialisation code should not be invoking a "run" method, this is quite surprising. Also, passing an integer and treating it as a boolean, even if you include this hint in the name, is confusing. A better approach:
void Application::run() {
bool running = true;
while(running) {
// ...
}
}
As for separating the code to deal with different states, you don't need a polymorphic solution (though it would save you a little typing):
typedef std::map<std::string, std::string> Settings;
enum GameState {
STATE_INTRO,
STATE_MENU,
STATE_INGAME
}
class Intro {
static const int INTRO_TIME_MS = 5 * 1000;
public:
Intro(const Settings &settings)
:
logo(settings["logo"]),
timer(INTRO_TIME_MS),
finished(false)
{
}
void reset() {
timer.set(INTO_TIME_MS);
finished = false;
}
GameState nextState() {
if(finished) {
return STATE_MENU;
}
return STATE_INTRO;
}
void onEvent(const InputEvent &event) {
// Skip intro if the player hammers an input device!
finished = true;
}
void update(const TimeSpan &time) {
timer.update(time);
if(timer.elapsed()) {
finished = true;
}
}
void render(Screen &screen) {
screen.apply(logo);
}
private:
Timer timer;
Sprite logo;
bool finished;
};
// MainMenu and Game classes in a similar vein
class Application {
public:
Application(const Settings &settings)
void run();
private:
GameState currentState;
Intro intro;
MainMenu mainMenu;
Game game;
Screen screen;
};
Application::Application(const Settings &settings)
:
currentState(STATE_INTRO),
intro(settings),
mainMenu(settings),
game(settings),
screen(settings)
{
}
void Application::run() {
bool running = true;
TimeSpan time;
while(running) {
time.update();
InputEvent event;
GameState nextState;
switch(currentState) {
case STATE_INTRO:
while(pollEvent(event)) {
intro.onEvent(event);
}
intro.update(time);
intro.render(screen);
nextState = intro.nextState();
break;
case STATE_MENU:
while(pollEvent(event)) {
menu.onEvent(event);
}
menu.update(time);
menu.render(screen);
nextState = menu.nextState();
break;
case STATE_INGAME:
while(pollEvent(event)) {
game.onEvent(event);
}
game.update(time);
game.render(screen);
nextState = game.nextState();
break;
default:
assert(false, "Not implemented!");
break;
}
if(nextState != currentState) {
switch(currentState) {
case STATE_INTRO: intro.reset(); break;
case STATE_MENU: menu.reset(); break;
case STATE_INGAME: game.reset(); break;
default: assert(false, "Not implemented!"); break;
}
currentState = nextState;
}
}
}
Just a simple, untested example.