In this muchly desired side tutorial we are going to look at how to implement something most commonly called Game States (or as I call them, App States). This tutorial will be based upon the code from the SDL Collision Events tutorial.

To simply explain what an app state is, try to think of your game (or application) as a parent with many child applications. In other words, your primary application can yield control to a single child when needed.

This is probably best explained with an example. Say we are working on a typical game - like a simple side scroller (such as Mario). What would the different application states be in this case?

1. Splash Screen
2. Menu
3. World Overview
4. Game
5. Credits

So, to tie into my previous explanation, when the parent application launches it first loads the child app state Splash Screen and yields control to it. This allows the Splash Screen to render a neat graphic, animate, and do its thing. Once it's done, it turns over control to the Menu. The menu then captures input events, and allows the user to make some choices. If the user clicks Play (or whatever), the menu app state will then yield control over to the World Overview app state. I think you get the picture.

The basic premise of app states is to turn over control to another app state when certain criteria is met (usually a timer or input event).

Just by understanding what's above, we could simply have a pointer that points to the active app state, and then switch between objects. And that does work. But we're going to take that a step further and introduce what I call an App State Manager. The purpose of the manager is to handle switching between app states. Why would we want something to manage that? Well, for example, when we switch between a splash screen and the main menu we could do some neat fade out / fade in effect. Without an app state manager, that would still be possible, but not nearly as neat.

Here's how that class would look (CAppStateManager.h):
#ifndef __CAPPSTATEMANAGER_H__
    #define __CAPPSTATEMANAGER_H__

#include "CAppState.h"

enum {
    // Add your Other App States Here
    APPSTATE_NONE,
    APPSTATE_INTRO,
    APPSTATE_GAME
};

class CAppStateManager {
    private:
        static CAppState* ActiveAppState;

    public:
        static void OnEvent(SDL_Event* Event);

        static void OnLoop();

        static void OnRender(SDL_Surface* Surf_Display);

    public:
        static void SetActiveAppState(int AppStateID);

        static CAppState* GetActiveAppState();
};

#endif
Notice that every member is static. Why? Basically because this class is a singleton, and we I want it easy to use. So, instead of coding something like CAppStateManager::GetInstance()->OnRender(), we can simply do CAppStateManager::OnRender(). Which looks better to you?

Notice the enum and the 3 App States that I created. We are going to create an Intro App State and Game App State for this tutorial. When you want to add more app states, you'll need to add them to this enum.

So lets continue and implement the rest of the code for this class (CAppStateManager.cpp).
#include "CAppStateManager.h"

// Refer to your Other App States Here
#include "CAppStateIntro.h"
#include "CAppStateGame.h"

CAppState* CAppStateManager::ActiveAppState = 0;

void CAppStateManager::OnEvent(SDL_Event* EventHolder) {
    if(ActiveAppState) ActiveAppState->OnEvent(EventHolder);
}

void CAppStateManager::OnLoop() {
    if(ActiveAppState) ActiveAppState->OnLoop();
}

void CAppStateManager::OnRender(SDL_Surface* Surf_Display) {
    if(ActiveAppState) ActiveAppState->OnRender(Surf_Display);
}

void CAppStateManager::SetActiveAppState(int AppStateID) {
    if(ActiveAppState) ActiveAppState->OnDeactivate();

    // Also, add your App State Here so that the Manager can switch to it
    if(AppStateID == APPSTATE_NONE)        ActiveAppState = 0;
    if(AppStateID == APPSTATE_INTRO)    ActiveAppState = CAppStateIntro::GetInstance();
    if(AppStateID == APPSTATE_GAME)        ActiveAppState = CAppStateGame::GetInstance();

    if(ActiveAppState) ActiveAppState->OnActivate();
}

CAppState* CAppStateManager::GetActiveAppState() {
    return ActiveAppState;
}
One important side note before we continue, notice how I am including the other app state classes. Just like in the header file, you'd want to include the header files for your other app states there, as well as inside the SetActiveAppState method. But, more importantly, notice how I am including the other app states inside the .cpp file instead of the header file. This is to avoid something called circular dependencies. When we begin to creat the other app state files we are going to include this CAppStateManager class. If the CAppStateManager class was including the header files for the other app states inside its header, you'd run into an include loop.

To better explain this:
#ifndef __CAPPSTATEMANAGER_H__
    #define __CAPPSTATEMANAGER_H__

#include "CAppStateIntro.h"

class CAppStateManager {
    // Other Code
};

#endif
#ifndef __CAPPSTATEINTRO_H__
    #define __CAPPSTATEINTRO_H__

#include "CAppStateManager.h"

class CAppStateIntro {
    // Other Code
};

#endif
Do you see the issue? Each is including the other and the includes will never end (This statement is not quite right. Because of the #define's I have in each header file, the files will only be compiled once in the project. However, you'll still run into issues, so the following still applies.). To remedy this we refer to the class we need inside the .cpp file.

Lets continue on and go through this class line by line. First we have a static member that refers to the current active app state. This pointer will always point to some other app state class. It's what we'll use when we want an app state to be active.

Next we have the familiar OnEvent, OnLoop, and OnRender methods. Inside those all we're simply doing is calling the respective method of the active app state. So, if the Intro app state is active, we'll let the OnEvent, OnLoop, and OnRender methods of that app state run.

Next we have SetActiveAppState which will simply do what it says, set the active app state. We pass an enum value to it from our header file. We then let the active app state know it's being deactivated. Then, find the appropriate app state matched to the enum value, and let it know it's being activated. Simple enough.

Lastly we have a simple method to retrieve the active app state.

Now that we have our manager class up and working, lets go ahead and create some app states. First, we're going to need to create a class that all our app states can inherit from. Appropriately, we'll call this class CAppState (CAppState.h):
#ifndef __CAPPSTATE_H__
    #define __CAPPSTATE_H__

#include "CEvent.h"

class CAppState : public CEvent {
    public:
        CAppState();

    public:
        virtual void OnActivate() = 0;

        virtual void OnDeactivate() = 0;

        virtual void OnLoop() = 0;

        virtual void OnRender(SDL_Surface* Surf_Display) = 0;
};

#endif
The class itself is pretty simple. All we're doing is laying out the common methods for an app state, and requiring those to be defined (that's what the '= 0' does, it requires a virtual method to be defined in a child class). Also notice that the CAppState class is inheriting CEvent.

Lets look inside the class (CAppState.cpp):
#include "CAppState.h"

CAppState::CAppState() {
}
Now, don't think about this too hard. Remember all the methods are virtual and required to be defined by classes that inherit CAppState. So, we don't really have to define much of anything inside the class itself.

Okay, now lets actually create our two app states. We'll create our Intro app state first (CAppStateIntro.h).
#ifndef __CAPPSTATEINTRO_H__
    #define __CAPPSTATEINTRO_H__

#include "CAppState.h"
#include "CSurface.h"

class CAppStateIntro : public CAppState {
    private:
        static CAppStateIntro Instance;

        SDL_Surface* Surf_Logo;

        int StartTime;

    private:
        CAppStateIntro();

    public:
        void OnActivate();

        void OnDeactivate();

        void OnLoop();

        void OnRender(SDL_Surface* Surf_Display);

    public:
        static CAppStateIntro* GetInstance();
};

#endif
This class should be pretty straightforward. Remember, in the end, our app states are going to look and behave much like our regular CApp class. So, first we have our instance of this class (which the CAppStateManager uses to set the active app state), a logo to display, and StartTime variable. We'll be using the StartTime variable to do a simple timer to show the splash and then advance to the next app state. You can grab the Logo from the Files area on the sidebar.

Here's inside the class (CAppStateIntro.cpp).
#include "CAppStateIntro.h"

#include "CAppStateManager.h"

CAppStateIntro CAppStateIntro::Instance;

CAppStateIntro::CAppStateIntro() {
    Surf_Logo = NULL;
}

void CAppStateIntro::OnActivate() {
    // Load Simple Logo
    Surf_Logo = CSurface::OnLoad("splash.png");

    StartTime = SDL_GetTicks();
}

void CAppStateIntro::OnDeactivate() {
    if(Surf_Logo) {
        SDL_FreeSurface(Surf_Logo);
        Surf_Logo = NULL;
    }
}

void CAppStateIntro::OnLoop() {
    if(StartTime + 3000 < SDL_GetTicks()) {
        CAppStateManager::SetActiveAppState(APPSTATE_GAME);
    }
}

void CAppStateIntro::OnRender(SDL_Surface* Surf_Display) {
    if(Surf_Logo) {
        CSurface::OnDraw(Surf_Display, Surf_Logo, 0, 0);
    }
}

CAppStateIntro* CAppStateIntro::GetInstance() {
    return &Instance;
}
Inside of OnActivate we load our logo, and then start the timer. Inside of OnDeactivate we free our logo. Inside of OnLoop all we're going is checking if 3 seconds have passed; if so, we change the app state. Inside of OnRender we simply blit the logo. And, of course, our GetInstance returns the Instance to the class.

Lets create your Game app state now (CAppStateGame.h).
#ifndef __CAPPSTATEGAME_H__
    #define __CAPPSTATEGAME_H__

#include "CAppState.h"

#include "CArea.h"
#include "CCamera.h"
#include "CEntity.h"
#include "CSurface.h"

#include "CPlayer.h"

class CAppStateGame : public CAppState {
    private:
        static CAppStateGame Instance;

        CPlayer    Player;
        CPlayer    Player2;

    private:
        CAppStateGame();

    public:
        void OnKeyDown(SDLKey sym, SDLMod mod, Uint16 unicode);

        void OnKeyUp(SDLKey sym, SDLMod mod, Uint16 unicode);

    public:
        void OnActivate();

        void OnDeactivate();

        void OnLoop();

        void OnRender(SDL_Surface* Surf_Display);

    public:
        static CAppStateGame* GetInstance();
};

#endif
Lots more going on here? Not really, I want you to notice that I simply moved over a lot of the code from CApp.h over to this new file. This is what our CApp.h looks like now:
#ifndef _CAPP_H_
    #define _CAPP_H_

#include <SDL.h>

#include "Define.h"

#include "CAppStateManager.h"
#include "CFPS.h"

#include "CEvent.h"
#include "CSurface.h"

class CApp : public CEvent {
    private:
        bool            Running;

        SDL_Surface*    Surf_Display;

    public:
        CApp();

        int OnExecute();

    public:
        bool OnInit();

        void OnEvent(SDL_Event* Event);

            void OnExit();

        void OnLoop();

        void OnRender();

        void OnCleanup();
};

#endif
I basically moved over OnKeyDown, OnKeyUp, the Player objects, and some #include directives (you may notice though that I added #include "CAppStateManager.h" to CApp.h).

Lets create the code for inside our class now (CAppStateGame.cpp).
#include "CAppStateGame.h"

CAppStateGame CAppStateGame::Instance;

CAppStateGame::CAppStateGame() {
}

void CAppStateGame::OnKeyDown(SDLKey sym, SDLMod mod, Uint16 unicode) {
    switch(sym) {
        case SDLK_LEFT: {
            Player.MoveLeft = true;
            break;
        }

        case SDLK_RIGHT: {
            Player.MoveRight = true;
            break;
        }

        case SDLK_SPACE: {
            Player.Jump();
            break;
        }

        default: {
        }
    }
}

void CAppStateGame::OnKeyUp(SDLKey sym, SDLMod mod, Uint16 unicode) {
    switch(sym) {
        case SDLK_LEFT: {
            Player.MoveLeft = false;
            break;
        }

        case SDLK_RIGHT: {
            Player.MoveRight = false;
            break;
        }

        default: {
        }
    }
}

void CAppStateGame::OnActivate() {
    if(CArea::AreaControl.OnLoad("./maps/1.area") == false) {
        return;
    }

    if(Player.OnLoad("yoshi.png", 64, 64, 8) == false) {
        return;
    }

    if(Player2.OnLoad("yoshi.png", 64, 64, 8) == false) {
        return;
    }

    Player2.X = 100;

    CEntity::EntityList.push_back(&Player);
    CEntity::EntityList.push_back(&Player2);

    CCamera::CameraControl.TargetMode = TARGET_MODE_CENTER;
    CCamera::CameraControl.SetTarget(&Player.X, &Player.Y);
}

void CAppStateGame::OnDeactivate() {
    CArea::AreaControl.OnCleanup();

    for(int i = 0;i < CEntity::EntityList.size();i++) {
        if(!CEntity::EntityList[i]) continue;

        CEntity::EntityList[i]->OnCleanup();
    }

    CEntity::EntityList.clear();
}

void CAppStateGame::OnLoop() {
    for(int i = 0;i < CEntity::EntityList.size();i++) {
        if(!CEntity::EntityList[i]) continue;

        CEntity::EntityList[i]->OnLoop();
    }

    //Collision Events
    for(int i = 0;i < CEntityCol::EntityColList.size();i++) {
        CEntity* EntityA = CEntityCol::EntityColList[i].EntityA;
        CEntity* EntityB = CEntityCol::EntityColList[i].EntityB;

        if(EntityA == NULL || EntityB == NULL) continue;

        if(EntityA->OnCollision(EntityB)) {
            EntityB->OnCollision(EntityA);
        }
    }

    CEntityCol::EntityColList.clear();
}

void CAppStateGame::OnRender(SDL_Surface* Surf_Display) {
    SDL_Rect Rect;
    Rect.x = 0;
    Rect.y = 0;
    Rect.w = WWIDTH;
    Rect.h = WHEIGHT;

    SDL_FillRect(Surf_Display, &Rect, 0);

    CArea::AreaControl.OnRender(Surf_Display, -CCamera::CameraControl.GetX(), -CCamera::CameraControl.GetY());

    for(int i = 0;i < CEntity::EntityList.size();i++) {
        if(!CEntity::EntityList[i]) continue;

        CEntity::EntityList[i]->OnRender(Surf_Display);
    }
}

CAppStateGame* CAppStateGame::GetInstance() {
    return &Instance;
}
You'll notice all the same things from our Intro class, and you'll also notice all the code that used to be inside of the CApp_* files. OnActivate code was taken from CApp_OnInit, OnLoop code was taken from CApp_OnLoop, OnRender code was taken from CApp_OnRender, and OnDeactivate code was taken from CApp_OnCleanup. We also moved over the code for the keyboard controls for Yoshi. We moved all the code over because it's the job of the Game app state now to actually run the game.

So, lets take a look at the CApp_* files.

CApp_OnInit:
#include "CApp.h"

bool CApp::OnInit() {
    if(SDL_Init(SDL_INIT_EVERYTHING) < 0) {
        return false;
    }

    if((Surf_Display = SDL_SetVideoMode(WWIDTH, WHEIGHT, 32, SDL_HWSURFACE | SDL_DOUBLEBUF)) == NULL) {
        return false;
    }

    SDL_EnableKeyRepeat(1, SDL_DEFAULT_REPEAT_INTERVAL / 3);

    CAppStateManager::SetActiveAppState(APPSTATE_INTRO);

    return true;
}
CApp_OnEvent:
#include "CApp.h"

void CApp::OnEvent(SDL_Event* Event) {
    CEvent::OnEvent(Event);

    CAppStateManager::OnEvent(Event);
}

void CApp::OnExit() {
    Running = false;
}
CApp_OnLoop:
#include "CApp.h"

void CApp::OnLoop() {
    CAppStateManager::OnLoop();

    CFPS::FPSControl.OnLoop();

    char Buffer[255];
    sprintf(Buffer, "FPS: %d", CFPS::FPSControl.GetFPS());
    SDL_WM_SetCaption(Buffer, Buffer);
}
CApp_OnRender:
#include "CApp.h"

void CApp::OnRender() {
    CAppStateManager::OnRender(Surf_Display);

    SDL_Flip(Surf_Display);
}
CApp_OnCleanup:
#include "CApp.h"

void CApp::OnCleanup() {
    CAppStateManager::SetActiveAppState(APPSTATE_NONE);

    SDL_FreeSurface(Surf_Display);
    SDL_Quit();
}
All of this should be self-explanatory, but to help you out, all we're doing really is calling the appropriate method inside of the CAppStateManager class. This will in turn call the appropriate class inside of the app state itself.

And that's it! Try making the splash screen fade, jump, or do other crazy things. Also, try adding animations for when app states change.