SDL Tutorial Basics

October 17th, 2007 by Tim Jones Leave a reply »

These tutorials are meant to teach someone with some experience using C++, or perhaps another programming language. If you have a hard time following certain ideas related to the code itself and not conceptual ideas (those relating to games), then I urge you to first read our tutorials that explain the C++ programming language. It’s not vital to understand everything about C++, but every little bit will help later on.

In these tutorials we will be referncing to CodeBlocks as our IDE of choice, with gcc and mingw for compilation. If you wish to use another IDE and compiler that is up to you, but it may be harder to follow along if you have limited experience with linking libraries. If you wish to download CodeBlocks, you can do so for free at http://www.codeblocks.org (download the one including the mingw package). We recommend you use the stable version, unless you want to spend the extra time and use the nightly builds.

These tutorials will be heavily centered around SDL (Simple DirectMedia Layer), a 2D cross-platform graphics library. This library is going to allow us to draw fancy graphics to the screen, and do all sorts of fun stuff that allows us to make a game. You’re going to need to download this library at http://www.libsdl.org; be sure to download the Mingw32 tar file under “Development Libraries” and “Win32″ and the zip file under “Runtime Libraries” and “Win32.” If you are using Visual Studio download the appropriate version instead of the Mingw32 file. Once downloaded, we recommend you put the .dll found in the zip in your system32 folder. This way whenever we open an SDL application it can find the dll file it needs.

Open the tar file now (the one that was listed under “Development Libraries”) and decompress the contents to a folder (such as C:\SDL). Now, open up CodeBlocks so we can change some settings. Click “Settings” on the menu bar, and then click “Search Directories” tab. You’ll need to add C:\SDL\include to the “Compiler” tab, and C:\SDL\lib to the “Linker” tab (change C:\SDL to wherever you decompressed the files to). Once done, click Okay.

Start a new “blank” project, calling it whatever you want. Save it some place. Click on “Project” on the menu bar, and then click on “Properties.” A dialog will open; click on the “Project’s build options…” button on the bottom right. Click on the “Linker Settings” tab and add the following to the list under “Link libraries”:

mingw32
SDLmain
SDL

The order is important, so use the arrows to rearrange the list until it matches what is shown above. If you are having a little trouble figuring out what we are doing, we’re simply “linking” code together, or, in other words, we’re taking the SDL code and combining it with our own. We do this by using the include files to compile, and the lib files to link. And once done, our code is put together to make one application.

Click Ok twice, and you should be all set to go!

Let’s create two new files called CApp.h, and CApp.cpp; these will serve as the core for our program. First, open CApp.h and add the following code, in here is where our tutorial will really begin:

#ifndef _CAPP_H_
    #define _CAPP_H_

#include <SDL.h>

class CApp {

    public:

        CApp();

        int OnExecute();

};

#endif

Now, open up CApp.cpp and add the following code:

#include "CApp.h"

CApp::CApp() {
}

int CApp::OnExecute() {
    return 0;
}

int main(int argc, char* argv[]) {
    CApp theApp;

    return theApp.OnExecute();
}

The CApp class is setting the stage for our entire program. Let me step aside to take special note of how games are typically setup. Most games consist of 5 functions that handle how the game processes. These processes are typically:

Initialize
This function handles all the loading of data, whether it be textures, maps, NPCs, or whatever.

Event
This function handles all input events from the mouse, keyboard, joysticks, or other devices.

Loop
This function handles all the data updates, such as a NPCs moving across the screen, decreasing your health bar, or whatever

Render
This function handles all the rendering of anything that shows up on the screen. It does NOT handle data manipulation, as this is what the Loop function is supposed to handle.

Cleanup
This function simply cleans up any resources loaded, and insures a peaceful quitting of the game.

It’s important to understand that games are one gigantic loop. Within this loop we find events, update data, and render pictures. Thus, the basic structure could be thought of as:

Initialize();

while(true) {
    Events();
    Loop();
    Render();
}

Cleanup();

Each iteration of the loop we do something with data, and render accordingly. Events are extra, as a means for the user to manipulate data as well. In that sense, Events aren’t required to make a game, but are needed when you want the user to be able to manipulate data (such as move a NPC to the left).

Let’s clarify this idea with an example. Say we have our Knight, the hero of the game. All we want to do is simply have him move around. If I press left, he goes left. We need to figure out how to do this within a loop. Firstly, we know we want to check for events (keyboard events). Since Events are a means to manipulate data, we also know we’ll need some sort of variables to modify. Then we can use these variables to render our Knight appropriatly on the screen. We could have:

if(Key == LEFT) X–;
if(Key == RIGHT) X++;
if(Key == UP) Y–;
if(Key == DOWN) Y++;//… somewhere else in our code …

RenderImage(KnightImage, X, Y);

This works because each loop it checks if the key is LEFT, RIGHT, etc., and if so, we decrease or increase a variable. So, if our game is running at 30 frames per second and we press LEFT, then our guy would move to the left 30 pixels per second. If you don’t understand the game loop, you soon will. Games need them to function correctly.

Going back to our conceptual code (the 5 functions), we can add these additional pages to our project:

CApp_OnInit.cpp
CApp_OnEvent.cpp
CApp_OnLoop.cpp
CApp_OnRender.cpp
CApp_OnCleanup.cpp

Go back to CApp.h, and add the following functions and variables:

#ifndef _CAPP_H_
    #define _CAPP_H_

#include <SDL.h>

class CApp {
    private:
        bool    Running;

    public:
        CApp();

        int OnExecute();

    public:

        bool OnInit();

        void OnEvent(SDL_Event* Event);

        void OnLoop();

        void OnRender();

        void OnCleanup();
};

#endif

Go through each of the 5 files we just created and create the function themselves:

#include "CApp.h"

bool CApp::OnInit() {
    return true;
}

#include "CApp.h"

void CApp::OnEvent(SDL_Event* Event) {
}

#include "CApp.h"

void CApp::OnLoop() {
}

#include "CApp.h"

void CApp::OnRender() {
}

#include "CApp.h"

void CApp::OnCleanup() {
}

Now lets go back to our CApp.cpp code to link all of these functions together:

#include "CApp.h"

CApp::CApp() {
    Running = true;
}

int CApp::OnExecute() {
    if(OnInit() == false) {
        return -1;
    }

    SDL_Event Event;

    while(Running) {
        while(SDL_PollEvent(&Event)) {
            OnEvent(&Event);
        }

        OnLoop();
        OnRender();
    }

    OnCleanup();

    return 0;
}

int main(int argc, char* argv[]) {
    CApp theApp;

    return theApp.OnExecute();
}

You’ll notice some new variables, but let’s look at what is happening first. First, we try to Initialize our game, if it fails we return -1 (an error code), thus closing our program. If everything is good, we continue on to the game loop. Within the game loop we use SDL_PollEvent to check for events, and pass them one at a time to OnEvent. Once done with Events, we go to OnLoop for move data around and what not, and then render our game. We repeat this indefinitly. If the user exits the game, we proceed to OnCleanup cleaning up any resources. Simple enough.

Now, lets look at SDL_Event and SDL_PollEvent. The first is a structure that holds information about events. The second is a function that will grab any events waiting in the queue. This queue can have any number of events, which is the reason why we have to loop through them. So, for example, lets say the user presses A and moves the mouse during the OnRender() function. SDL will detect this and put two events in the queue, one for a key press and one for a mouse move. We can grab this event from the queue by using the SDL_PollEvent, and then passing it to OnEvent to handle it accordingly. Once there are no more events in the queue, SDL_PollEvent will return false, thus exiting out of the Event queue loop.

The other variable added, Running, is our own. This is our exit out of the game loop. When this is set to false, it will end the program, and in turn exit the program. So, for example, if the user presses the Escape key we can set this variable to false, quitting the game.

You should be able to compile everything so far just fine, but you may notice you can’t exit out. You’ll probably have to use the task manager to end the program.

Now that everything is setup, let’s start by creating the window our game will be drawn in. Jump over to CApp.h and add a SDL surface variable to the code:

#ifndef _CAPP_H_
    #define _CAPP_H_

#include <SDL.h>

class CApp {
    private:
        bool            Running;

        SDL_Surface*    Surf_Display;

    public:
        CApp();

        int OnExecute();

    public:
        bool OnInit();

        void OnEvent(SDL_Event* Event);

        void OnLoop();

        void OnRender();

        void OnCleanup();
};

#endif

I suppose now would be a good time to explain exactly what a SDL Surface is. An SDL Surface is anything that can be drawn, or drawn to. Say we have a blank piece of paper, a pencil, and some stickers; this paper could be called our display “surface.” We can draw to it, put stickers on it, or whatever. The stickers we have are also surfaces; we can draw on them and put other stickers on them as well. So, Surf_Display is simply our “piece of blank paper” that we will draw all our stuff on.

Now, lets jump on over to CApp_OnInit to actually create this surface:

#include "CApp.h"

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

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

    return true;
}

The first thing we need to do is start up SDL itself, so we can access its functions. We are telling SDL to Initialize everything it has; there are other parameters you can pass, but understanding them at this point is not important. The next function we use is SDL_SetVideoMode. This bad boy is what creates our window, and our surface. It takes 4 parameters: The width of the window, the height of the window, the bit resolution of the window (recommended to be 16 or 32), and then display flags. There are quite a few display flags, but the ones shown above are fine for now. The first flag tells SDL to use hardware memory for storing our images and such, and the second flag tells SDL to use double buffering (which is important if you don’t want flickering on your screen). Another flag that may interest you now is SDL_FULLSCREEN, which makes the window go fullscreen.

Now that our display is setup, lets cleanup a little bit to make sure everything works smoothly. Open up CApp_OnCleanup.cpp and add the following:

#include "CApp.h"

void CApp::OnCleanup() {
    SDL_FreeSurface(Surf_Display);
    SDL_Quit();
}

The first thing we do is free the display surface, basically freeing any memory. Once that is done, we quit out of SDL. You should take note that in this function is where you would free other surfaces as well (probably before the Surf_Display). This keeps all your code centralized to the function its performing.

To keep things tidy, lets also set the Surf_Display pointer to NULL on the class constructor. Open up CApp.cpp and add the following:

CApp::CApp() {
    Surf_Display = NULL;

    Running = true;
}

Try compiling your code, and watching it work. You should have a nice blank window popup. You’ll notice you can’t close it though, so you’ll need to use the task manager to do so.

Now that we have a window all setup, we’re going to need a way to close it. Open up the CApp_OnEvent.cpp file, add the following:

#include "CApp.h"

void CApp::OnEvent(SDL_Event* Event) {
    if(Event->type == SDL_QUIT) {
        Running = false;
    }
}

The SDL event structure is broken down into types. These types can range from keypresses, to mouse movements; what we simply are doing here is checking the event type. The type above we are looking for is the request to close the window (i.e., when the user clicks the X button). If that event happens to take place, we set Running to false, thus ending our program. Simple enough. We’ll look more into events in a later lesson.

Now, you should everything setup, and a good structure to work with later. It may be a good idea to take this project and turn it into an “SDL template” within CodeBlocks. I won’t go over how to do that, but feel free to Google it.

Once you have a good idea what’s going on with the code we covered in this lesson, jump over to the next game tutorial lesson to learn more about SDL surfaces.

SDL Tutorial Basics – Tutorial Files:
Win32: Zip, Rar
Linux: Tar (Thanks Gaten)

Share and Enjoy:
  • Digg
  • del.icio.us
  • DZone
  • MisterWong
  • Reddit
  • StumbleUpon
  • Technorati
  • Slashdot
  • LinkedIn
  • MySpace
  • Yahoo! Buzz

Did you like this tutorial/blog post? Feel free to donate to keep more comin', and have more contests.

Advertisement

137 comments

  1. joe says:

    Is there a tutorial on this site that goes into classes a bit more in depth- or at all? It looks like this particular tut assumes a fair amount of knowledge with them.

  2. joe says:

    Can you explain the double colon? That has something to do with scope right?

    ex:

    CApp::CApp() {
    }

    • Tim Jones says:

      Joe,
      Correct, the :: is for the scope. When I say CApp::CApp, I am saying, use the CApp construct inside the scope of the CApp class. Regarding OOP tutorials, no, there are not any here. Sorry.

  3. Tim Jones says:

    Slump,
    Thanks for the compliments!

  4. Slump says:

    Just wanted to comment on how excellent and easy to follow this tutorial is. I recently finished some OOP classes for C++ in university and was interestied in doing some graphics programming for Linux. This tutorial offered a great starting point for me so I just want to yell out a big thank you to Mr. Jones!

  5. Tim Jones says:

    Claes,
    You’re doing exactly what I told some1 to do; “link the libraries.” That can either be done via a pragma like you have done (which is not portable), or by changing your project settings.

  6. Claes says:

    I had the same problem some1 had a few posts above me, but it disappeared when I added the following in my CApp.h file:

    #pragma comment(lib, “SDL.lib”)
    #pragma comment(lib, “SDLmain.lib”)

    is that necessary or have I done something else wrong?

  7. Doug says:

    Oh nevermind I got it to work now, thanks.
    :)

  8. Tim Jones says:

    Doug,
    Sounds like your window is not being created properly. Can you paste your code for SDL_SetVideoMode?

  9. Doug says:

    I saw on one of the posts about changing the project settings (in codeblocks) to get rid of the console window, to change the type to “GUI application”. I did this, and it was fine until i got near the end of the tutorial and I tried to create the window (SetVideoMode). It compiled fine but SetVideoMode() seemed to have no effect. I tried playing around with the other type options and selected “Native” and it works, but the console and SDL window flicker for a split-second and go away, instead of the infinite loop. Then I changed it back to “Console application” (which I think was the original setting), and it shows a console-styled window without the flashing input cursor, and without the SDL window. When I pressed enter nothing happened, so I used the grey exit button. CodeBlocks didn’t register it closing, (at the bottom frame it still said ‘checking for existence’ etc. So the only way I could compile again was to quit codeblocks and run it again.
    1. I’m using Code::Blocks 8.02
    2. The settings I changed were found in Project>Properties>”Build Targets” tab>Type:(a dropdown menu)

    If anyone has any suggestions, please let me know.

    PS: And thanks for reading my post!

  10. russell says:

    After fumbling around with it for several hours I finally managed to get it compiled. Setting this crap up in Windows is ridiculously difficult, but at least this outdated guide is better than the SDL documentation. Thanks.

Trackbacks /
Pingbacks

Leave a Reply