Wednesday, August 13, 2008

Introduction

This section is dedicated to game programming. Its main goal is to help beginers learn the basics and be able to quickly create fun games using powerfull libraries. In order to keep things simple, 
all tutorials are based on a free development program: DevC++ provided by Bloodshed software. The first tutorial will explain how to install it, how to get the SDL Devpacks for DevC++ and how to 
start your first SDL project. 

The section of tutorials below sums up the basics of computer gaming. It is illustrated by tutorials on the SDL (Simple Directmedia Layer) library. The tutorials aren't all ready yet but the first ones teach the first steps in game development: starting SDL, loading bitmap images and drawing them on screen, moving things around by taking keyboard inputs into account....

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

Saturday, August 9, 2008

SDL Images and Events - Loading a bitmap and moving it on screen

This tutorial will focus on getting a basic image rendered in the middle of the screen and let the user move it around with the arrow keys of the keyboard. The windows-only function from the first tutorial have been scrapped, making lighter code.

We will stop using silly resolutions from now on and avoid having crappy colour depths. This tutorial uses 800x600 pixels screen resolution with a 32 bit per pixel colour depth. This will allow us to handle 255 layers for each colour: Red, Green, Blue and Alpha. Let's move onto the code...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <SDL/SDL.h>
We declare global variables here just to point them out and make you see what is really needed to render an image on screen. Note that in further tutorials, these variables will be encapsulated so as to have the cleanest code we can. The first new variable we declare is an SDL_Surface *image which is a pointer the the bitmap file that we load into an SDL_Surface. You therefore notice that we will be drawing an SDL_Surface on another SDL_Surface.... (drawing the *image on *screen). This drawing is done via the SDL_BlitSurface() (This performs a fast blit - copy an array of data to a bitmapped array destination - from the source surface to the destination surface)
function. The function takes 4 parameters:

the source SDL_Surface : this is simply the image surface you want to draw
the source SDL_Surface SDL_Rect : we put NULL here because we just want the entire source SDL_rect to be drawn
the target SDL_Surface : in our case, this is always the screen surface
the target SDL_Surface SDL_Rect : we use the SDL_Rect rect variable we created at the top of the code. This lets us specify the offset from the top left corner of the screen at which our image will be rendered. As a result, it simply lets us set our image position on screen...
// Allocate a pointer to an SDL_Surface for the screen buffer 
// and for the image we want to load and display on screen.
SDL_Surface *screen = NULL;
SDL_Surface *image = NULL; // We define a global SDL_Rect variable here. I know it isn't clean
// but the only aim here is to point out WHAT has been added 
// from the basic tutorial to this one
SDL_Rect rect;
Our init() function start all the SDL components. The section that is really important here is the last part, where we allocate an SDL_surface according to a bitmap image path:

image   = SDL_LoadBMP("GDO_El_Kef.bmp");
The SDL_LoadBMP (Load a Windows BMP file into an SDL_Surface. Returns the new surface, or NULL if there was an error) function simply returns an SDL_Surface pointer if it manages to load the target file.

// Function : init()    - Params : none
// SDL Initialisation function
// Instead of having a heavy and loaded main function, all the 
// setting up of our application is handled here
bool init()
{
    // Initialize SDL
    if (SDL_Init (SDL_INIT_VIDEO) < 0)
    {
        printf ("Couldn't initialize SDL: %s\n", SDL_GetError ());
        exit (1);
    }
    
    atexit (SDL_Quit);
    // Set 800x600 32-bits video mode
  screen = SDL_SetVideoMode (800, 600, 32, SDL_SWSURFACE | SDL_DOUBLEBUF);
    if (screen == NULL)
    {
        printf ("Couldn't set 800x600 32b video mode: %s\n", SDL_GetError ());
        exit (2);
    }
    
    // Set the title of our application window handler
    SDL_WM_SetCaption ("SDL MultiMedia Application", NULL);
    
    // We now load our first bitmap image.
    image   = SDL_LoadBMP("GDO_El_Kef.bmp");
    if (!image)
    {
       printf("*** Error attempting to load file: GDO_El_Kef");
       exit (3);
    }
    
    
    // This lets us set the original coordinates of our image on the screen.
    // Don't forget that top left corner is the origin (0,0)
    rect.x = 200;
    rect.y = 200;
    // We activate <font color='green'><i>keyboard repetition</i></font>. Therefore, when a key stays pressed down
    // it will keep moving the image around the screen
    // To see prcisely what this toggle does, just comment the line and recompile
    // the code...
    SDL_EnableKeyRepeat(25, 20);
    
    return true;
}
Not much new stuff here, except the use of the SDL_BlitSurface() function to draw the bitmap where we want on screen. 
// Function : draw()    - Params : none
// Frame rendering function
// This function is called on every loop, to draw the scene
// It draws then swaps the work and screen buffer to display the scene
void draw ()
{
    
    Uint32 color;
    // Create a black background using the screen pixel format (32 bpp)
    color = SDL_MapRGB (screen->format, 0, 0, 0);
    SDL_FillRect (screen, NULL, color);
    // Set the image offset
    SDL_BlitSurface(image, NULL, screen, &rect);
    
    // Flip the working image buffer with the screen buffer
    SDL_Flip (screen);
    
    // Add a little pause...
    SDL_Delay (1);
}
The main() function now handles extra events: Key Presses.

Each time a key is pressed, it is take into account as an event, which is placed in an event queue (Internally, SDL stores all the events waiting to be handled in an event queue. Using functions like SDL_PollEvent, SDL_PeepEvents and SDL_WaitEvent you can observe and handle waiting input events). Each time we iterrate through our game loop, we take the 
first event placed in the queue and act accordinly. Event Queue is FIFO (First in first out. The first element to be inserted in the queue will be the first one retrieved) type.
When an arrow key is pressed, we increment or decrement the rect.x and rect.y variables so as to set the new image offset on screen.
// Function : main()    - Params : argc, argv
// Main program function
// This function calls the init() function then loops on draw function 
// until an escape condition is reached</span>
int main (int argc, char *argv[])
{
    // Used to loop
    int done = 0;
    // The keys variable is used to store the state of the keyboard at 
    // each frame and see if which keys are pressed down
    Uint8 *keys;
    
    // Initialise SDL and all the rest...
    init();
    
    // Game Loop
    while (!done)
    {
        // This will let us track events
        SDL_Event event;
        // We fill 'event' with the first event in the event queue
        while (SDL_PollEvent (&event))
        {
            switch (event.type)
            {
            // If our event reports a key being pressed down
            // we process it
            case SDL_KEYDOWN:
                  keys = SDL_GetKeyState(NULL);
                  if ( keys[SDLK_LEFT] ) {  rect.x--;}
                  if ( keys[SDLK_RIGHT] ) { rect.x++;}
                  if ( keys[SDLK_UP] ) { rect.y--;}
                  if ( keys[SDLK_DOWN] ) { rect.y++;}
                  if ( keys[SDLK_ESCAPE] ) { done = 1;}
                  break;
            // If the event is a click on the close button in the top
            // right corner of the window, we kill the application
            case SDL_QUIT:
                done = 1;
                break;
            default:
                break;
            }
        }
        // Render the scene
        draw ();
    }
    return 0;
}
Download the tutorial source HERE

Thursday, August 7, 2008

SDL with DevC++ - Handling Transparency

This very short tutorial's objective is to explain how transparency is handled with bitmaps in SDL. Indeed, the basic tutorials only let you handle Bitmap images, that hold Red, Blue and Green pixel 
information but no Alpha channel information. Games have gone around this problem via the colour key technique, which consists in indicating to SDL that a specific colour should NOT be rendered for each SDL_Surface you load. Therefore, you can now put your sprites on a specific background colour and tell SDL to ignore that colour... 
Here's a very simple example. Consider the ball and steel images below, and the 2 different rendering results we obtained:


Note that the ball is on a white background. Therefore, if you want to draw the ball in game, in front of the steel background, you will get the bad side effect of keeping the ball's white background. Using the transparency colour key on the ball image for the white colour (R = G = B = 255) will make all the white pixels of the image transparent and produce the correct effect you see on the 4th screenshot.
The switching of the transparent key colour is done in SDL by using the SDL_SetColorKey() function as follows
SDL_SetColorKey(surface,SDL_SRCCOLORKEY|SDL_RLEACCEL,colour);
surface = SDL_DisplayFormatAlpha(surface);
This will simply assign the colour you pass in parameter as being transparent for the surface you passed in first parameter. Note that we called the SDL_DisplayFormatAlpha() on our alpha switched surface. This enables a direct copy from the surface from your RAM to the graphics card RAM and therefore improves performance. This will be detailed a little more in the next tutorial.

As for the colour parameter, most people don't know how to code a colour on a Uint32 integer. You can easily get a conversion from [R G B] to [Uint32] with the following function:
Uint32 colour = SDL_MapRGB(screen->format, R, G, B);

Wednesday, August 6, 2008

Hardware and Software SDL_Surfaces - Improving performance

Once again a very short tutorial, this time to insist on a point which often is the cause of performance issues on several SDL Applications. Indeed, most generated code samples and tutorials we see online handle SDL_Surfaces in an inapropriate way. If you look at the SDL application initialisation, it comes as follows:
screen = SDL_SetVideoMode (640, 480, 16, <b>SDL_SWSURFACE</b> | SDL_DOUBLEBUF);
What is abnormal here is that we are initialising the application focusing on system memory (RAM) instead of graphics card memory. This means every time you want to render your screen surface via a flip, you send all the screen surface data from your RAM over to your graphics card that will then draw it all on screen. On larger resolutions, this can cause a huge performance hit on your application

Other tutorials don't forget this switch in the application initialisation, and start it correctly, as follows:
screen = SDL_SetVideoMode (640, 480, 16, <b>SDL_HWSURFACE</b> | SDL_DOUBLEBUF);
Unfortunately, this is still not sufficient for optimal performance. It does help a lot because your screen buffer is now stored directly in your graphics card memory (your RAM should inflate MUCH less when you start your application). The remaining problem is that SDL_Surface pointers returned by the SDL and SDL_Image libraries return software surfaces, hence once again in system memory.
As a result, every time a surface is loaded, you have to load it to a temporary surface in your RAM (software surface), convert it to a surface in graphics card memory (hardware surface), and finally release your temporary surface in RAM. The piece of code below does it perfectly, using temp as the temporary system surface:
SDL_Surface* temp = SDL_LoadBMP("test.bmp");       //Load test.bmp to a software surface
surface = SDL_DisplayFormat(temp);                         //Convert software surface to hardware surface
SDL_FreeSurface(temp);                                           //Release software surface

Tuesday, August 5, 2008

SDL_Image - Loading more image formats

The SDL_Image (Advanced version of the basic SDL rendering library) library will allow you to greatly improve games you work on mainly because of the large amount of image formats it will let you load (BMP, PNM (PPM/PGM/PBM), XPM, LBM, PCX, GIF, JPEG, PNG, TGA, and TIFF). Furthermore, SDL_Image will allow you to use transparency layers of images you load instead of requiring a colour key for transparency like SDL does with bitmap images. 
In order to used the SDL_Image library, you will need a few extra packages that aren't shipped with the default SDL bundle. You can get ALL the Devpacks required for DevC++ HERE. Installation has to be done in a specific order (e.g you won't get SDL_Image installed until you have libpng Devpack installed ... etc...). Once the SDL_Image library is installed, it's extremely easy to switch from SDL to SDL_Image code:

- Initialisation : all you need is to include the SDL_Image.h header file in addition to the default SDL headers
#include "SDL/SDL.h" 
#include "SDL/SDL_image.h" 
- Loading an image to a surface : you will now have to replace your SDL_LoadBMP (Loads a surface from a named Windows BMP file) with the IMG_Load. The only parameter you will need to specify for the IMG_Load is the path to the file you wish to load. Finally, the fact of loading an image with or without its transparency layer will depend only on whether you use the SDL_DisplayFormatAlpha (This function takes a surface and copies it to a new surface of the pixel format and colors of the video framebuffer plus an alpha channel, suitable for fast blitting onto the display surface. It calls SDL_ConvertSurface) or SDL_DisplayFormat (This function takes a surface and copies it to a new surface of the pixel format and colors of the video framebuffer ignoring the transparency layer if there is one on the image) function to convert your software surface to a hardware surface.
SDL_Surface *temp = IMG_Load(path);
SDL_Surface *surf = SDL_DisplayFormat(temp); // Load image without alpha channel
SDL_FreeSurface(temp);
SDL_Surface *temp = IMG_Load(path);
SDL_Surface *surf = SDL_DisplayFormatAlpha(temp); // Load image with alpha channel
SDL_FreeSurface(temp);

Saturday, August 2, 2008

SDL with DevC++ - Basic SDL framework

SDL Engine Requirements :
- SDL Library installed with libraries and header files in the correct folders (use DevPack indicated in the "How To Start" section)

After having very basic and disorganised code sections, we will now focus on getting a little SDL engine on feet. I think there's no point in detailing all the construction of this engine but mainly
how it's structured, how to use it, and how to add stuff to it.


Later on, we will extend this SDL graphics engine to handle sound, networking, open GL and much more. The framework code is fully commented, making it very easy to read through and understand. It is structured around the following elements:

0. Engine config file
The config.h file is there to group all the main settings of your engine together. That's where you can set your screen resolution, pixel colour depth, frames per second... etc... Don't hesitate to add stuff to here to enhance the contorls over your first SDL game

    // SDL Video mode information
    const int SCREEN_WIDTH = 1024;
    const int SCREEN_HEIGHT = 768;
    const int SCREEN_BPP = 32;
    
    // SDL Image structure information. Define the maximum number of images the game can load
    const int IMAGE_COUNT = 128;
    
    // Timer definitions. This forces the game to wait at least T_FRAME_INTERVAL milliseconds
    // between each frame. It is the only way to get the game to NOT run faster on a faster
    // computer... Indeed, without this test, the game loop would run as fast as the computer
    // processors allows it

    const int T_FRAME_INTERVAL = 25;     // time interval (ms) used for FPS rendering
1. The CORE class
As the name states it, the CORE class handles the game engine core. It's this class that have an infinite loop function that we call the gameLoop() function in this framework. It will loop for
upcoming user events (mouse or keyboard inputs), perform the actions that correspond to those events and then render the graphics.

2. The GRAPHICS class
This class will give you full image control. When it's instanciated, it automatically calls its loadImageData() function. This function simply scans through the data/game_images.dat file for image information as below:

    # This file defines all the images loaded by the game
    # They are assigned in a static array and limited to 128 images by default
    #
    # If you need to re-assign the upper limit, you need to edit IMAGE_COUNT
    # field in config.h and recompile the game

    # Syntax to define an image
    # [image_number] [image_file_path] [RGB transparency key colour]
    
    # Also, note that all bright pink colours will be used for the transparency colour key
    # This means RGB colour [255, 0, 255] will be transparent in game
    # On the other hand, setting this to [-1 -1 -1] will disable transparency for that image</span>
    
    [0]    [data/img/ball.bmp]    [255 255 255]
    [1]    [data/img/steel.bmp]    [-1 -1 -1]


The engine supports RGB transparency key colour for each image loaded. This colour can be specified in the image data file shown just above. RGB image transparency is something new to those who have only been through the first 2 SDL tutorials. Setting that value to [-1 -1 -1] will just disable transparency for that image. For each image, you can specify a colour which will be "substracted" to your image, making that colour transparent in game.

Here's a very simple example. Consider the ball and steel images below, and the 2 different rendering results we obtained:

Note that the ball is on a white background. Therefore, if you want to draw the ball in game, in front of the steel background, you will get the bad side effect of keeping the ball's white background. Using the transparency on the ball image for the white colour [255 255 255] will make all the white pixels of the image transparent and produce the correct effect you see on the 4th screenshot.

The graphics class supports 8, 16, 24 and 32 bits per pixel colour depth raw pixel writing and line drawing functions
Pixel reading functions allow advanced collisions detection using "pixel perfect" collision models

3. What needs to be done
The engine handles all the basic SDL stuff. Basically, all you need to do is:

Find your bitmap images and fill the game_images.dat config file to get them loaded
Create your game objects class that is instanciated and controlled by CORE class
Update the gameLoop() function in the CORE class to get your object data and render it. Note that you can do this the other way round and have all your objects handle a CORE class instance pointed to render themselves. This will only require a little more adaptation.

Download the Basic SDL Engine