This tutorial has been a long time coming and for anyone who has been following the other tutorials I apologize.
In this tutorial I will cover one of the most useful topics in game programming, Game States. Game States are extremely useful, lets say you have a game with a menu screen, pause screen, play screen and game over screen.
You could go ahead and do something horribly convoluted and hard to maintain such as this-
if(menuState) { // do this } if(playstate) { // do this } etc....
As you can see eventually this is gonna be extremely hard to maintain and use, so the solution is to create a way to easily switch between states, remember our Game class, everything is nicely encapsulated and set out. We can use this class with our Game State class to make Game States that are essentially like separate games, these games can be easily switched between.
So first we will create a GameState.h modelled partially on our Game.h file, so create GameState.h and start coding...
#ifndef GAMESTATE_H #define GAMESTATE_H #include "Game.h" class GameState { public: virtual void Init() = 0; virtual void Clean() = 0; virtual void Pause() = 0; virtual void Resume() = 0; virtual void HandleEvents(Game* game) = 0; virtual void Update(Game* game) = 0; virtual void Draw(Game* game) = 0; void ChangeState(Game* game, GameState* state) { game->ChangeState(state); } protected: GameState() { } }; #endif
Notice the pure virtual functions (functions denoted = 0;) we use these so that we can have all our game states inherit from this GameState base class, inheritance is a topic that you should research if this concept seems strange to you.
Also notice we are passing in a pointer to a Game object, this is because GameStates may need to access some game functions as they are essentially Games themselves.
Once more thing to look at is the protected constructor, this is used so we can implement the class as a singleton, singletons are used to ensure there is only ever one instance of an object at any time, we use a protected constructor, then use a function that returns a pointer to a static instance of the class.
OK thats it for our GameState.h, since this is a pure virtual base class we will not write a .cpp file as each individual game state will overload these functions.
Now we need to make some changes to our Game.h file so open it up....
#ifndef _GAME_H_ #define _GAME_H_ #include <SDL.h> #include "Sprite.h" #include <vector> class GameState; // make sure this class knows about the GameState class. class Game { public: Game(); void Init(const char* title, int width, int height, int bpp, bool fullscreen); void ChangeState(GameState* state); // new function void PushState(GameState* state); // new function void PopState(); // new function void HandleEvents(); // remove pointer to game class void Update(); void Draw(); void Clean(); bool Running() { return m_bRunning; } void Quit() { m_bRunning = false; } private: // the stack of states std::vector<GameState*> states; SDL_Surface* m_pScreen; bool m_bFullscreen; bool m_bRunning; }; #endif
Ok thats the header file, and now the cpp file......
void Game::HandleEvents() // take pointer out and remove function body { } void Game::Update() // remove any previous code from old tutorials { } void Game::Draw() // remove any previous code from old tutorials { SDL_Flip(m_pScreen); } void Game::Clean() // remove any previous code from old tutorials { }
OK, we removed these function bodies as we now want our individual states to handle their own drawing,updating and event handling.
Now make sure this compiles, it will just be a blank window at the moment and you will have to force it to quit.
Now we need to implement our GameStates into the Game class and also create the new functions that we wrote.
So again in the Game.cpp file
#include "Game.h" #include "GameState.h" // constructor Game::Game() { } void Game::Init(const char* title, int width, int height, int bpp, bool fullscreen) { int flags = 0; // initialize SDL SDL_Init(SDL_INIT_EVERYTHING); // set the title bar text SDL_WM_SetCaption(title, title); if ( fullscreen ) { flags = SDL_FULLSCREEN; } // create the screen surface m_pScreen = SDL_SetVideoMode(width, height, bpp, flags); m_bFullscreen = fullscreen; m_bRunning = true; printf("Game Initialised Succesfully\n"); } /* Our new functions, ChangeState() takes a pointer to a GameState as a parameter and then pushes that state onto the vector of pointers to GameStates, before that it uses the clean function to remove the old state from the stack. */ void Game::ChangeState(GameState* state) { // cleanup the current state if ( !states.empty() ) { states.back()->Clean(); states.pop_back(); } // store and init the new state states.push_back(state); states.back()->Init(); } /* Whereas ChangeState() pushes a state onto the stack and removes the previous state, PushState() pauses the previous state before pushing a new state onto the stack, this state can then be removed and the previous state resumed. Extrememly useful for pausing. */ void Game::PushState(GameState* state) { // pause current state if ( !states.empty() ) { states.back()->Pause(); } // store and init the new state states.push_back(state); states.back()->Init(); } /* Remove and resume previous state. */ void Game::PopState() { // cleanup the current state if ( !states.empty() ) { states.back()->Clean(); states.pop_back(); } // resume previous state if ( !states.empty() ) { states.back()->Resume(); } } /* These functions have now been changed so that they simply allow the current state to handle things, states.back() refers to the last element on the stack (the current state) */ void Game::HandleEvents() { // let the state handle events states.back()->HandleEvents(this); } void Game::Update() { // let the state update the game states.back()->Update(this); } void Game::Draw() { // let the state draw the screen states.back()->Draw(this); //SDL_Flip(m_pScreen); } void Game::Clean() { while ( !states.empty() ) { states.back()->Clean(); states.pop_back(); } // shutdown SDL SDL_Quit(); }
Right then our state manager is now set up, now onto creating a few states for our game.
I hope it hasn't been too hard going so far and everyone is still with me.
So check that this compiles, again you will have to force quit the program.
Now lets create a state, we will create a few states but each will simply be a different image as that is the scope of the tutorial so far, and it gives us an excuse to use our Sprite class from the previous tutorial.
So create a new file called MenuState.h
#ifndef _MENU_STATE_H_ #define _MENU_STATE_H_ #include "SDL.h" #include "GameState.h" #include "Sprite.h" class MenuState : public GameState { public: void Init(); void Clean(); void Pause(); void Resume(); void HandleEvents(Game* game); void Update(Game* game); void Draw(Game* game); // Implement Singleton Pattern static MenuState* Instance() { return &m_MenuState; } protected: MenuState() {} private: static MenuState m_MenuState; SDL_Surface* menuSprite; }; #endif
So thats our first state, it inherits from our GameState base class, we also created an SDL_Surface so that we can draw something to the screen.
So now create the MenuState.cpp....
#include <stdio.h> #include "SDL.h" #include "Game.h" #include "MenuState.h" MenuState MenuState::m_MenuState; void MenuState::Init() { menuSprite = NULL; // set pointer to NULL; menuSprite = Sprite::Load("menustate.bmp"); // load menu state bitmap printf("MenuState Init Successful\n"); } void MenuState::Clean() { printf("MenuState Clean Successful\n"); } void MenuState::Pause() { printf("MenuState Paused\n"); } void MenuState::Resume() { printf("MenuState Resumed\n"); } void MenuState::HandleEvents(Game* game) //put our exit function back in business // we can now quit with cross in corner. { SDL_Event event; if (SDL_PollEvent(&event)) { switch (event.type) { case SDL_QUIT: game->Quit(); break; } } } void MenuState::Update(Game* game) { } void MenuState::Draw(Game* game) { Sprite::Draw(game->GetScreen(), menuSprite, 0, 0); // we will write // GetScreen() in a second SDL_Flip(game->GetScreen()); }
While writing the tutorial I realised I would have to write a function to return the screen as it is private, it is bad practice to just bluff it and make it public so try not to do it. Here is the GetScreen() function and it couldn't be simpler

SDL_Surface* GetScreen() {return m_pScreen;}
Just put this function in the public: part of the Game.h file.
Ok so we have now written pretty much all we need to test our first state, so lets update our main.cpp and check it out.
#include "Game.h" #include "MenuState.h" #include <iostream> int main(int argc, char* argv[]) { Game game; game.Init("State Manager",640,480,32,false); game.ChangeState(MenuState::Instance()); while(game.Running()) { game.HandleEvents(); game.Update(); game.Draw(); } // cleanup the engine game.Clean(); return 0; }
We added our ChangeState function and set the current game state to an Instance of the MenuState class.
Wow this is a long tutorial

So we will create 2 new states PlayState and PauseState. Here is the code, paste(or type out if you like) into PlayState.h, PlayState.cpp, PauseState.h, PauseState.cpp
PlayState.h
#ifndef _PLAY_STATE_H_ #define _PLAY_STATE_H_ #include "SDL.h" #include "GameState.h" #include "Sprite.h" class PlayState : public GameState { public: void Init(); void Clean(); void Pause(); void Resume(); void HandleEvents(Game* game); void Update(Game* game); void Draw(Game* game); // Implement Singleton Pattern static PlayState* Instance() { return &m_PlayState; } protected: PlayState() {} private: static PlayState m_PlayState; SDL_Surface* playSprite; }; #endif
PlayState.cpp
#include <stdio.h> #include "SDL.h" #include "Game.h" #include "PlayState.h" PlayState PlayState::m_PlayState; void PlayState::Init() { playSprite = NULL; playSprite = Sprite::Load("playstate.bmp"); printf("PlayState Init Successful\n"); } void PlayState::Clean() { printf("PlayState Clean Successful\n"); } void PlayState::Pause() { printf("PlayState Paused\n"); } void PlayState::Resume() { printf("PlayState Resumed\n"); } void PlayState::HandleEvents(Game* game) { SDL_Event event; if (SDL_PollEvent(&event)) { switch (event.type) { case SDL_QUIT: game->Quit(); break; } } } void PlayState::Update(Game* game) { } void PlayState::Draw(Game* game) { Sprite::Draw(game->GetScreen(), playSprite, 0, 0); SDL_Flip(game->GetScreen()); }
PauseState.h
#ifndef _PAUSE_STATE_H_ #define _PAUSE_STATE_H_ #include "SDL.h" #include "GameState.h" #include "Sprite.h" class PauseState : public GameState { public: void Init(); void Clean(); void Pause(); void Resume(); void HandleEvents(Game* game); void Update(Game* game); void Draw(Game* game); // Implement Singleton Pattern static PauseState* Instance() { return &m_PauseState; } protected: PauseState() {} private: static PauseState m_PauseState; SDL_Surface* pauseSprite; }; #endif
PauseState.cpp
#include <stdio.h> #include "SDL.h" #include "Game.h" #include "PauseState.h" PauseState PauseState::m_PauseState; void PauseState::Init() { pauseSprite = NULL; pauseSprite = Sprite::Load("paused.bmp"); printf("PauseState Init Successful\n"); } void PauseState::Clean() { printf("PauseState Clean Successful\n"); } void PauseState::Resume(){} void PauseState::Pause() {} void PauseState::HandleEvents(Game* game) { SDL_Event event; if (SDL_PollEvent(&event)) { switch (event.type) { case SDL_QUIT: game->Quit(); break; } } } void PauseState::Update(Game* game) { } void PauseState::Draw(Game* game) { Sprite::Draw(game->GetScreen(), pauseSprite, 0, 0); SDL_Flip(game->GetScreen()); }
So once you have these files in your project we are going to move between them. So we will start with our MenuState.cpp, so open it up....
#include "PlayState.h" // include PlayState.h // now we will go into the MenuState::HandleEvents(Game* game) void MenuState::HandleEvents(Game* game) { SDL_Event event; if (SDL_PollEvent(&event)) { switch (event.type) { case SDL_QUIT: game->Quit(); break; case SDL_KEYDOWN: switch(event.key.keysym.sym){ case SDLK_SPACE: game->ChangeState(PlayState::Instance()); break; } } } }
And now we will go into PlayState.cpp
#include "PauseState.h" // include the pause state // in handle events void PlayState::HandleEvents(Game* game) { SDL_Event event; if (SDL_PollEvent(&event)) { switch (event.type) { case SDL_QUIT: game->Quit(); break; case SDL_KEYDOWN: switch(event.key.keysym.sym){ case SDLK_SPACE: game->PushState(PauseState::Instance()); break; } } } }
This time we use PushState(); instead of ChangeState();
now open up PauseState.cpp
void PauseState::HandleEvents(Game* game) { SDL_Event event; if (SDL_PollEvent(&event)) { switch (event.type) { case SDL_QUIT: game->Quit(); break; case SDL_KEYDOWN: switch(event.key.keysym.sym){ case SDLK_SPACE: game->PopState(); break; } } } }
OK and we are done, compile and test

You should start with a menu state and then pressing space will take you to the playstate and then pressing space will pause and unpause the playstate. I know this is quite a simple demonstration but the code written here can be used in all of your 2d games, you can create states as individual levels or anything you want. Try making a menu in the menu state and then possibly a moving sprite in the playstate.
Wow that was a long tutorial, now you know why it took so long to appear. Next tutorial will focus on creating a game object base class and deriving some objects from it.
Happy Coding, as usual any questions welcome.
Here are the files I used in the project, sorry they're big


Number of downloads: 1726

Number of downloads: 1386

Number of downloads: 1294
Kakashi is cool

This post has been edited by stayscrisp: 06 December 2012 - 03:46 AM
Reason for edit:: Small typo fixed