Monday, August 11, 2008

SDL Basics - starting SDL with DevC++


Many people have been willing to create their own computer games but never knew where to start. The SDL (Simple Directmedia Layer) library is probably one of the easiest ways to learn how to handle the media layer of your computer hardware. It's a library for C/C++ which gives you full multimedia control: graphics, sound, keyboard, mouse and joystick inputs, CD and DVD Rom accessiblity... This set of tutorials focuses mainly on game programming and will therefore mainly handle image rendering, animations and game controls. It will later be extended to the 3D world, once the basics of gaming have all been fully understood.

The tutorials listed in this SDL section will all provide examples developed with the SDL library under the DevC++ environment. They can easily be ported under linux since the Migw compiler is a port to windows systems of the famous GNU GCC compiler. If you wish to learn SDL through DevC++, I suggest you grab the latest version on the DevC++ website. All examples you will find here were done with version 4.9.9.2 of the software.

The next thing you need to grab for SDL are the SDL libraries. Linux users (or Visual Studio users) can grab those files directly on the SDL Web Site. On the other hand, DevC++ has 
special Dev Packs which make SDL installation extremely easy ! :) You simply need to get the SDL Dev Pack and install it...

You can then start your DevC++ and choose a new project. In that window, you will have a Multimedia submenu. Go in there and you'll have an SDL application generator.
This project generator will generate a basic SDL application, that initializes SDL, opens a 640x480 pixel window in 16 bits per pixel mode and draws a rectangle in that window that changes colour as time goes by.
We will now take a closer look at the code generated to understand the basics of the SDL framework. Like any other program, the sample code starts by including the headers for SDL, string parsing and startd stdlib library headers. The <b>#include &lt;windows.h&gt;</b> has been spammed into this code by the code generator. 
Just ignore it if you don't want to look any further in micro$oft stuff. It's only used here to pop error message windows up if ressource allocations fail...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <SDL/SDL.h>
#include <windows.h>
We then need to allocate what is called an SDL_surface (Section of the video card memory which is allocated to store image data). You have to think of this as a part of your graphics card memory, which is used to draw stuff on. This surface can be directly linked to your screen, meaning it will draw whatever you put on that surface immediately, or can be a temporary buffer that will be drawn on screen at the next frame rendering. Simple buffering has side effects since you draw on the surface while it's being rendered on your screen. This can lead to flickering and isn't comfortable to look at. 

We will therefore stick to the best rendering solution offered by SDL: double buffering. You simply need to pass the right parameter to the SDL_SetVideoMode (Function used to set up a video mode with the specified width, height and bits-per-pixel) function.

SDL_Surface *screen = NULL;
The next code section is the rendering (creating an image of objects designed in a program. Rendering can simulate the appearance of real-world textures, colors, surface shadows, highlights and reflections) loop. This loop is called every time your screen is refreshed with an image. It's devided in 2 sections:

a first part where we draw on our second buffer, while the first buffer is still rendered on your screen
a second step where we switch the buffers over. The screen buffer therefore becomes the "work" buffer where you draw on
whereas the work buffer on which you have rendered everything you want is sent over onto your screen to be rendered.
static void draw ()
{
    static int direction = 0;
    static int value = 0;
    static int which = 0;
    SDL_Rect rect;
    Uint32 color;
    // This first part lets us create a Uint32 variable which codes a colour (Red, Blue, Green, Alpha).
    // The SDL_MapRGB function needs 2 different types of parameters to produce a colour :
    //      - the screen format (indicates the pixel resolution, colour depths... etc...
    //      - the RGB colour you are looking for. In our case, we have R=0, G=0, B=0 => colour is black
    color = SDL_MapRGB (screen->format, 0, 0, 0);
    // We then call SDL_FillRect() function with our black colour. This function needs and SDL_Surface
    // to draw onto. In our case, the SDL_Surface is screen (work buffer that will be rendered on the
    // screen later). The second parameter is supposed to be an SDL_Rect structure which defines a
    // rectangle, therefore the (x,y) (width,height) of the rectangle you want to draw. We specified
    // NULL here to flood the entire surface with our colour. As a result, this function simply paints
    // all our screen buffer in black.
    SDL_FillRect (screen, NULL, color);
    // You can ignore the following lines of code at the moment. They are only there to make a nice
    // colour fade which illustrates an SDL colour example...
    if (direction == 0)
    {
        value += 2;
        if (value >= 256)
        {
            value = 255;
            direction = 1;
        }
    }
    else
    {
        value -= 2;
        if (value <= 5)
        {
            value = 0;
            direction = 0;
            which++;
            if (which == 5)
                which = 0;
        }
    }
    switch (which)
    {
      case 0:
          color = SDL_MapRGB (screen->format, value, 0, 0);
          break;
      case 1:
          color = SDL_MapRGB (screen->format, 0, value, 0);
          break;
      case 2:
          color = SDL_MapRGB (screen->format, 0, 0, value);
          break;
      case 3:
          color = SDL_MapRGB (screen->format, value, value, value);
          break;
      case 4:
          color = SDL_MapRGB (screen->format, value, 0, value);
          break;
    }
    // Now is the interesting part : we declared SDL_Rect rect higher up in the code. This means we
    // have an instance of the SDL_Rect structure, which carries information such as coordinates
    // (x,y) and width (w) and height (h). The calculations below simply aim at finding the top left
    // corner coordinates of the coloured rectangle, and then its height and width.
    // NOTE: coordinate in SDL have their origin (0,0) in the top left corner.
    rect.w = screen->w / 2;
    rect.h = screen->h / 2;
    rect.x = (screen->w / 2) - (rect.w / 2);
    rect.y = (screen->h / 2) - (rect.h / 2);
    SDL_FillRect (screen, &rect, color);

    // We now switch our 2 buffers over, to use the buffer we just painted our rectangle on and display
    // it on the screen, and use the previous screen buffer to work on
    SDL_Flip (screen);
    // This is just like the unix sleep command. It just pauses the program execution for one second to
    // avoid having it run too quickly. This will also mean it won't take 100% of your CPU, and will
    // give 1ms per loop to other programs running on your system  :)
    SDL_Delay (1);
}
The important information to note in the section of code above is how coordinates work in SDL. Remember that the origin (x=0, y=0) is NOT the center of your screen... it's the top left corner. Also, when you move right from that corner, x is positive, and when you move down, y is positive. Be really carefull, because this means the Y axis is turned upside down ! We will now move onto the main() function. This is where we initialize SDL and loop on our draw() function until a click on the little cross in the top right corner of the window is clicked.
int main (int argc, char *argv[])
{
    // This char* msg is the only reason why we have included window.h in our headers.
    // If SDL_Init() function below can't start SDL, popup message box up indicates the error message...
    char *msg;
    int done;
    // SDL initialisation
    if (SDL_Init (SDL_INIT_VIDEO) < 0)
    {
        sprintf (msg, "Couldn't initialize SDL: %s\n", SDL_GetError ());
        MessageBox (0, msg, "Error", MB_ICONHAND);
        free (msg);
        exit (1);
    }
    atexit (SDL_Quit);
    // Set screen resolution, colour depth and enable double buffering
    screen = SDL_SetVideoMode (640, 480, 16, SDL_SWSURFACE | SDL_DOUBLEBUF);
    if (screen == NULL)
    {
        sprintf (msg, "Couldn't set 640x480x16 video mode: %s\n",
          SDL_GetError ());
        MessageBox (0, msg, "Error", MB_ICONHAND);
        free (msg);
        exit (2);
    }
    // Set our window title (the title text which will be in the window frame) 
    SDL_WM_SetCaption ("SDL MultiMedia Application", NULL);
    done = 0;
    // The following section handles events. We will see these in detail on later tutorials.
    // At the moment, we only look for a click on the close button in the top right corner to kill
    // the application
    while (!done)
    {
        SDL_Event event;
        // Check for events
        while (SDL_PollEvent (&event))
        {
            switch (event.type)
            {
            case SDL_KEYDOWN:
                break;
            case SDL_QUIT:
                done = 1;
                break;
            default:
                break;
            }
        }
        // Call the draw function
        draw ();
    }
    return 0;
}
Download the tutorial source HERE

No comments:

Post a Comment