User Guide: II States - Suraii/Azurite GitHub Wiki
States are what you could call "scenes" in game programming. For example, the main menu of your game can be a State, the actual game (where you play) can be another state, the pause menu and the scoreboard could also be states.
To create a state you just have to create a class that inherit the state abstract class, named AState.
#include <Azurite/Game.hpp> // The game mother class header
#include <Azurite/StateMachine.hpp> // The state machine header, used for anything state-related
#include <Azurite/modules/SfmlModule.hpp> // Read the module chapter for this
class MainState : public Azurite::AState { // Creating our own state
void onStart(Azurite::Game &instance) {
// Will be executed when the game starts
}
void onTick(Azurite::Game &instance) {
// Will be executed on each game tick (100 times / second)
}
void onStop(Azurite::Game &instance) {
// Will be executed when the game stops
}
// We'll talk about theses later
void onPause(Azurite::Game &instance) {}
void onResume(Azurite::Game &instance) {}
};
int main()
{
Azurite::Game game;
// Read the modules chapter, seriously
std::unique_ptr<Azurite::AModule> sfml_mod(new Azurite::SfmlModule(game));
game.addModule("sfml", std::move(sfml_mod))
.useAsInputModule()
.useAsAudioModule()
.useAsAssetModule()
.useAsDisplayModule();
MainState state; // Creating an instance of our state
game.stateMachine.setState(std::make_unique<MainState>(state)); // Adding our state in the state machine
game.run(); // This sould run our state, aka a black screen atm
}
Events are used to communicate between states and the rest of the game. Each Event is attached to one state. You'll see how to create Events in the systems chapter.
You can read events in the states, you often want to read them on tick.
void onTick(Azurite::Game &instance) {
while (auto event = readEvent()) { // For each events
// Do something with event
}
}
Let's see how to manage multiple states in your game.
Let's start with 2 states :
- A main menu state called MainState
- A game state called GameState (very creative)
...
class MainState : public Azurite::AState {
void onStart(Azurite::Game &instance) {}
void onTick(Azurite::Game &instance) {
while (auto event = readEvent()) {
}
}
void onStop(Azurite::Game &instance) {}
void onPause(Azurite::Game &instance) {}
void onResume(Azurite::Game &instance) {}
};
class GameState : public Azurite::AState {
void onStart(Azurite::Game &instance) {}
void onTick(Azurite::Game &instance) {
while (auto event = readEvent()) {
}
}
void onStop(Azurite::Game &instance) {}
void onPause(Azurite::Game &instance) {}
void onResume(Azurite::Game &instance) {}
};
int main()
{
...
MainState state; // We want our game to start with the main menu
game.stateMachine.setState(std::make_unique<MainState>(state));
game.run();
}
Okay let's imagine our main menu with 2 buttons :
- One 'start' button to start the game
- One 'exit' button to exit the game
Okay now, we want our MainState to switch to the GameState when the start button is clicked.
class MainState : public Azurite::AState {
void onStart(Azurite::Game &instance) {}
void onTick(Azurite::Game &instance) {
while (auto event = readEvent()) {
if (event.name = "Game Started") { // We'll see how to send events in the system chapter dw
GameState state; // Creating our new state
instance.stateMachine.setState(std::make_unique<GameState>(state)); // Switching to it (this will destroy the current main state)
}
}
}
void onStop(Azurite::Game &instance) {}
void onPause(Azurite::Game &instance) {}
void onResume(Azurite::Game &instance) {}
};
Okay and now we can implement the exit mechanic.
class MainState : public Azurite::AState {
void onStart(Azurite::Game &instance) {}
void onTick(Azurite::Game &instance) {
while (auto event = readEvent()) {
if (event.name = "Game Started") {
...
} else if (event.name = "Game Exited")
instance.leaveCurrentState(); // This will leave the current state, making the game 'stateless' and causing it to stop
}
}
void onStop(Azurite::Game &instance) {}
void onPause(Azurite::Game &instance) {}
void onResume(Azurite::Game &instance) {}
};
Okay now let's add a third State to our system, a state for a pause menu named PauseState.
class PauseState : public Azurite::AState {
void onStart(Azurite::Game &instance) {}
void onTick(Azurite::Game &instance) {}
void onStop(Azurite::Game &instance) {}
void onPause(Azurite::Game &instance) {}
void onResume(Azurite::Game &instance) {}
};
Okay now let's implement the pause in our GameState.
class GameState : public Azurite::AState {
void onStart(Azurite::Game &instance) {}
void onTick(Azurite::Game &instance) {
while (auto event = readEvent()) {
if (event.name == "Game Paused") {
PauseState state; // Creating our new state
instance.stateMachine.stackState(std::make_unique<GameState>(state));
}
}
}
void onStop(Azurite::Game &instance) {}
void onPause(Azurite::Game &instance) {}
void onResume(Azurite::Game &instance) {}
};
You can notice that here, we used stackState
instead of setState
, what's the difference ?
-
setState
destroy the current state and replace it with the input state -
stackState
pause the current state (calling itsonPause
method) and put the input state "above" it. The input state therefore become the new current state.
Okay now we just need one last little change, resuming, being stuck forever in a pause menu ins't really optimal, let's quickly add this in the pause menu.
class PauseState : public Azurite::AState {
void onStart(Azurite::Game &instance) {}
void onTick(Azurite::Game &instance) {
while (auto event = readEvent()) {
if (event.name == "Game Resumed")
instance.stateMachine.leaveCurrentState(); // Doing this will destroy the current state
// But here the game won't stop like before, because there is another stack below the current one
// So instead and exiting because it is stateless, the game will simply switch to the below state
}
}
void onStop(Azurite::Game &instance) {}
void onPause(Azurite::Game &instance) {}
void onResume(Azurite::Game &instance) {}
};
Doing this will also call the resumed state's onResume
method.
Wow, that was a long one, hope I was clear enough !
- States are used to manage the different game scenes
- Events are used to communicate between states and the other parts of the game
- You can either replace states by other ones or stack them, pausing the below ones
- When you leave the current state the game switch to the below one, if you leave the last state, the game stops