Friday, 30 September 2011

Basic Game Tutorial #18 - Redefining controls

Basic Tutorials
Basic Game Tutorial #18 - Redefining controls
Redefining controls

 Introduction
This tutorial will demonstrate how to allow a user to redefine the controls for a game.
Compile and run tutorial18. You will be prompted to enter the controls to move the ship. Once you have entered them all you can move the ship around and fire using the controls you defined. Pressing Escape will allow you to redefine the controls again, pressing escape whilst on this screen will exit the program.

 An indepth look
The basic idea behind redefining controls is that you specify what key or joystick button has to be pressed to perform the onscreen action. This is accomplished by creating another Control structure to store your defined values. When you press a key or joystick button, this value is compared against the custom controls to see if one of them matches. If it does then the appropriate action will be performed.
The program comprises of two parts, defining the controls and then moving the ship with the defined controls. In defs.h we add a couple of defines to assist with the program flow
#define IN_REDEFINE 0
#define IN_GAME 1
and add a varaible called status to the Game struct
typedef struct Game
{
 int score, status;
 SDL_Surface *screen;
 TTF_Font *font;
 SDL_Joystick *joystick;
} Game;
We will start with defining the controls. In structs.h we add another structure to assist with prompting the user to input their custom controls. It is very basic but serves its purpose for the tutorial.
typedef struct Redefine
{
 int redefineIndex;
 char redefineString[255];
} Redefine;
redefineIndex simply stores what key we're trying to redefine and redefineString is for the onscreen text. A fully fledged game would present the user with a menu and allow them to choose which control they want to redefine.
In main.c we initialize the redefineIndex 0 and blank the redefineString.
/* Reset the redefine index */

redefine.redefineIndex = 0;
redefine.redefineString[0] = '\0';
We then enter the main loop and examine the Game status to determine whether we should be prompting the user to customize the controls or playing the game.
if (game.status == IN_REDEFINE)
{
 /* Handle the key redefining */
 
 doRedefine();
}

else
{
 /* Get the input */
 
 getInput();
 
 /* Update the player's position */
 
 doPlayer();
 
 /* Update the entities */
 
 doEntities();
 
 /* Do the collisions */
 
 doCollisions();
 
 /* Draw everything */
 
 draw();
}
We will look at the doRedefine function first. In redefine.c, we check if the redefineString is blank. If it is then we will use the redefineIndex to determine which control we are currently prompting the user to define and generate the appropriate string. Next we call flushInputs, this function is in input.c and is defined as follows
void flushInputs()
{
 SDL_Event event;

 while (SDL_PollEvent(&event)) {}
}
This code simply clears out any pending events so prevent a key press being picked up before the user is ready. Next we wait until the user presses a key or joystick button
int getSingleInput()
{
 int key;
 SDL_Event event;

 key = -2;

 if (SDL_PollEvent(&event))
 {
  switch (event.type)
  {
   case SDL_QUIT:
    exit(0);
   break;

   case SDL_KEYDOWN:
    key = event.key.keysym.sym;
   break;
   
   case SDL_JOYAXISMOTION:
    if (abs(event.jaxis.value) > DEAD_ZONE)
    {
     key = -3;
    }
   break;

   case SDL_JOYBUTTONDOWN:
    key = event.jbutton.button;
   break;
  }
 }
 
 if (key == SDLK_ESCAPE)
 {
  exit(0);
 }

 return key;
}
This function will return -2 if no key is pressed, so if this happens we will continue to wait until a valid input is received. If the user presses Escape during this loop then the program will simply exit. Note that while joystick buttons are captured and processed, joystick movement events will be ignored, but the program will acknowledge that a valid input has been entered. This is because capturing joystick movement is not very simple since it is analog based. Once a valid button key has been captured, we can assign the value to our custom Control structure. Again, we use the redefineIndex to determine which control to assign the value to. Next, we increment the redefineIndex to move to the next control and blank the redefineString.
switch (redefine.redefineIndex)
{
 case 0:
  customControl.left = key;
  break;
  
 case 1:
  customControl.right = key;
  break;
  
 case 2:
  customControl.up = key;
  break;
  
 case 3:
  customControl.down = key;
  break;
  
 default:
  customControl.fire = key;
  break;
}

redefine.redefineIndex++;

redefine.redefineString[0] = '\0';

if (redefine.redefineIndex == 5)
{
 redefine.redefineIndex = 0;
 
 game.status = IN_GAME;
}
If the redefineIndex is equal to 5 then we have input all the available controls and can then let the user try them out. We set the status of the Game structure to IN_GAME and reset the redefineIndex to 0. When the main loop checks the status of Game it will present the ship on the screen and allow the user to the move it around. We will now look at how the custom controls are handled in input.c. The follow code snippet demonstrates this
case SDL_KEYDOWN:
 key = event.key.keysym.sym;
 
 if (key == customControl.left)
 {
  input.left = 1;
 }

 else if (key == customControl.right)
 {
  input.right = 1;
 }

 /* Remainder omitted */
 break;

case SDL_KEYUP:
 key = event.key.keysym.sym;
 
 if (key == customControl.left)
 {
  input.left = 0;
 }

 else if (key == customControl.right)
 {
  input.right = 0;
 }
 
 /* Remainder omitted */
 break;
 
case SDL_JOYBUTTONDOWN:
 key = event.jbutton.button;
 
 if (key == customControl.left)
 {
  input.left = 1;
 }

 else if (key == customControl.right)
 {
  input.right = 1;
 }
 
 /* Remainder omitted */
 break;
 
case SDL_JOYBUTTONUP:
 key = event.jbutton.button;
 
 if (key == customControl.left)
 {
  input.left = 0;
 }

   else if (key == customControl.right)
 {
  input.right = 0;
 }
 
 /* Remainder omitted */
 break;
 
case SDL_JOYAXISMOTION:
 /* Horizontal movement */
 
 if (event.jaxis.axis == 0)
 {
  if (event.jaxis.value < -DEAD_ZONE)
  {
   input.left = 1;
  }

  else if (event.jaxis.value > DEAD_ZONE)
  {
   input.right = 1;
  }

  else
  {
   input.left = 0;
   input.right = 0;
  }
 }
 
 /* Vertical movement */
 
 if (event.jaxis.axis == 1)
 {
  if (event.jaxis.value < -DEAD_ZONE)
  {
   input.up = 1;
  }

  else if (event.jaxis.value > DEAD_ZONE)
  {
   input.down = 1;
  }

  else
  {
   input.up = 0;
   input.down = 0;
  }
 }
 break;
As always, when an event is detected, we determine its type and get the value. In the case of the keyboard, we get the value of the key and check to see if it matches any of our custom controls. If it does then we set the relevant control on the input structure. The same is true for joystick buttons. As you will see, joystick movement handling is handled as in the previous tutorial.

 Conclusion
Probably the hardest thing about allowing a user to customize controls is building an elegant interface to allow them to do so. How major games companies are able to spend tens of millions of dollars on games and then overlook something so simple is a mystery.

 Downloads

Source Code - tutorial18.tar.gz

Basic Game Tutorial #17 - Joystick movement

Basic Tutorials
Basic Game Tutorial #17 - Joystick movement
Joystick movement

 Introduction

This tutorial will demonstrate how to use the joystick to move an image around the screen.
Compile and run tutorial17. You can control the ship by using the joystick's joypad or analog stick. Pressing the fire button will fire a bullet.

 An indepth look

The structure for the joystick is an SDL_Joystick and holds all the information related to the joystick. It is stored in the Game struct:
typedef struct Game
{
 SDL_Joystick *joystick;
 SDL_Surface *screen;
} Game;
In defs.h we add the following definition:
#define DEAD_ZONE 3200
This controls the joystick's analog control sensitivity, this will be discussed later. Before using a joystick in SDL it must be opened. This is performed in the SDL_Init function in init.c
if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO|SDL_INIT_JOYSTICK) < 0)
The number of joysticks detected by SDL can be queried by the function SDL_NumJoysticks
joystickCount = SDL_NumJoysticks();
Since this tutorial deals with using the joystick for movement, we will exit the program if no joysticks were detected. A game that allows joystick or keyboard input would want to handle this event differently. Now that we have detected a joystick, we need to open it and store the reference to the joystick. This is done with the following.
game.joystick = SDL_JoystickOpen(0);
This will open the first joystick detected by SDL. If you have multiple joysticks attached to the system then we can open them instead by specifying the index number. As always, the index number starts at 0 and must be less than SDL_NumJoysticks. Specifying a negative number or a number greater than SDL_NumJoysticks will cause a segfault. A joystick opened by SDL must be closed when the program exits to free the resources. The following lines
buttonCount = SDL_JoystickNumButtons(game.joystick);

printf("Joystick has %d buttons\n", buttonCount);

printf("Joystick has %d axes\n", SDL_JoystickNumAxes(game.joystick));
Are simply for information. In your game you might wish to raise an error if, say, the number of detected buttons is less than the number that your game requires. The only other addition to init.c is the closing of the joystick in the cleanup function.
/* Close the joystick */

if (game.joystick != NULL)
{
 SDL_JoystickClose(game.joystick);
}
This will close the joystick and free the resources allocated to it. In input.c we add in some extra code to detect the events produced from the joystick
case SDL_JOYBUTTONDOWN:
 switch (event.jbutton.button)
 {
  case 0:
   input.fire = 1;
  break;
 }
 break;

case SDL_JOYBUTTONUP:
 switch (event.jbutton.button)
 {
  case 0:
   input.fire = 0;
  break;
 }
 break;
These two case statements deal with the first joystick button being pressed and released. This raises an important point: There is no way to know what button is the first one. On a PS2 joypad it is the Triangle button, but on other joysticks it might be different, so you will have to try all the buttons until the ship fires a shot. Don't worry though, the button doesn't change every time you start the program! The next code snippet deals with moving the ship.
case SDL_JOYAXISMOTION:
 /* Horizontal movement */
 
 if (event.jaxis.axis == 0)
 {
  if (event.jaxis.value < -DEAD_ZONE)
  {
   input.left = 1;
  }

  else if (event.jaxis.value > DEAD_ZONE)
  {
   input.right = 1;
  }

  else
  {
   input.left = 0;
   input.right = 0;
  }
 }
 
 /* Vertical movement */
 
 if (event.jaxis.axis == 1)
 {
  if (event.jaxis.value < -DEAD_ZONE)
  {
   input.up = 1;
  }

  else if (event.jaxis.value > DEAD_ZONE)
  {
   input.down = 1;
  }

  else
  {
   input.up = 0;
   input.down = 0;
  }
 }
 break;
SDL_JOYAXISMOTION is the event produced when the directional buttons on the joystick are pressed. When this happens, we need to check which axis was moved, either the horizontal or vertical. We do this by checking the event.jaxis.axis value. 0 signifies a horizontal movement and 1 signifies a vertical movement. The next part is important when reading input from analog joysticks:
if (event.jaxis.value < -DEAD_ZONE)
{
 input.left = 1;
}
The value generated from event.jaxis.value is between -32767 and 32767, with 0 as no movement. Some analog joysticks can be slightly sensitive or simply damaged and will always generate a small movement event, which the input code would pick up. The best solution is to ignore these small values to stop the ship from moving when the player doesn't want it to. The variable DEAD_ZONE, tells the code to not process movement that is below its value. This is currently set to 3200 and is stored in defs.h. This value is fairly reasonable but if you are still experiencing movement when the joystick is idle you should increase this value and recompile. A fully fledged game would allow this value to be configurable by the player.

 Conclusion

Adding joystick support is quite simple, but as you can see, the joystick buttons may not be mapped in a desirable fashion. In the next tutorial, we will look at adding support for configuring controls.

 Downloads

Source Code - tutorial17.tar.gz

Tuesday, 27 September 2011

Basic Game Tutorial #10 - A Basic Game

Basic Tutorials
Basic Game Tutorial #10 - A Basic Game
A simple game

 Introduction

This tutorial combines everything that we have learnt so far to create a basic rock dodging game. In this tutorial we will make use of reading input, collision detection, animation, playing sounds and SDL_TTF to create the game.
Compile and run tutorial10. Use the arrow keys (not the ones on the numeric pad) to move the ship around the screen. Your score will increase for as long as you don't hit a rock. If you hit a rock then the ship will vanish and your score will stop increasing. The higher your score is, the more rocks that will appear on the screen.

 An indepth look

This is the most complex tutorial so far, but many of the methods used in this tutorial have been covered in previous tutorials so you should be quite familiar with them. We will start, as always, with structs.h:
The animation structure has changed slightly. This is because we cannot store the current frame index or the frame counter in the structure without making all of the rocks spin in synchronization.
typedef struct Animation
{
    int frameCount;
    SDL_Surface **frame;
} Animation;
The current frame index and the counter have been moved to the Entity structure:
typedef struct Entity
{
    int active, speed;
    int x, y, w, h;
    int currentFrame, animID, frameTimer;
    SDL_Surface *sprite;
    void (*action)(void);
    void (*draw)(void);
} Entity;
We will use the Entity structure for both the player and the rocks. The currentFrame is used to store the index of the current image of the animation for this Entity. animID is the index of the animation that this Entity is using and frameTimer is the delay between moving onto the next frame in the animation. The rest of the variables should be familiar as they are used in previous tutorials. The other structures should also be familiar so we will move onto animation.c. The loadAnimation function has changed. We now pass in the index of the ID of the animation we wish to load up and the data file of the animation.
void loadAnimation(int id, char *name)
{
    /* Load up the data file that describes the animation */
    
    int i;
    FILE *fp = fopen(name, "rb");
    char frameName[20];
    
    if (fp == NULL)
    {
        printf("Failed to load animation file %s\n", name);
        
        exit(1);
    }
    
    /* Read the frame count */
    
    fscanf(fp, "%d", &animation[id].frameCount);
    
    /* Allocate space for the animation */
    
    animation[id].frame = (SDL_Surface **)malloc(animation[id].frameCount
    * sizeof(SDL_Surface *));
    
    if (animation[id].frame == NULL)
    {
        printf("Ran out of memory when creating the animation for %s\n", name);
        
        exit(1);
    }
    
    /* Now load up each frame */
    
    for (i=0;i<animation[id].frameCount;i++)
    {
        fscanf(fp, "%s", frameName);
        
        animation[id].frame[i] = loadImage(frameName);
        
        if (animation[id].frame[i] == NULL)
        {
            printf("Failed to load animation frame %s\n", frameName);
            
            exit(1);
        }
    }
}
So instead of using a pointer to the animation, we are using an array of Animations. This will make adding extra animations easy in the future. We have added an extra function to animation.c:
void loadAllAnimations()
{
    loadAnimation(ROCK_ANIMATION, "data/anim/rock.dat");
}
loadAllAnimations simply loads up the rock animation. If there were more animations then this function would load those too. There is also another new function, freeAnimations:
void freeAnimations()
{
    int i;
    
    for (i=0;i<MAX_ANIMATIONS;i++)
    {
        freeAnimation(&animation[i]);
    }
}
This function loops though all the animations and frees them. This function will be called by cleanup in init.c. We will skip over audio.c since it has not changed.
In collisions.c, we have changed the implementation of the function doCollisions
void doCollisions()
{
    int i;

    /* Check each entity against the player, skipping over inactive ones */
    
    if (player.active == 0)
    {
        return;
    }

    for (i=0;i<MAX_ENTITIES;i++)
    {
        if (entity[i].active == 0)
        {
            continue;
        }

        if (collision(entity[i].x, entity[i].y, entity[i].w, entity[i].h,
        player.x, player.y, player.w, player.h) == 1)
        {
            /* If a collision occured, kill the player */
            
            player.active = 0;
            
            playSound(EXPLOSION_SOUND);
        }
    }
}
Since we are only checking against the player, we will return from the function immediately if the player is not active. If the player is active however, we loop through the Entities, skipping over any inactive ones and check for a player - Entity collision. If one has occured then we set the player to inactive and call playSound to play an explosion. We will skip over draw.c since it does not contain any functions or calls that have not been seen in previous tutorials.
entity.c contains a new function for dealing with animation:
void drawAnimatedEntity()
{
    drawImage(animation[self->animID].frame[self->currentFrame], self->x, self->y);
}
drawAnimatedEntity makes use of the self Entity pointer. We use the animID and currentFrame variables of the Entity. font.c, graphics.c and input.c do not have any function calls not seen in previous tutorials so we will not cover them.
As noted earlier, the cleanup function in init.c calls the freeAnimations function. Apart from that it is the same.
In main.c, we load up our required resources, reset the starfield and then enter the main loop:
/* Get the input */

getInput();

/* Update the player's position */

doPlayer();

/* Add a rock */

addRock(game.score);

/* Update the entities */

doEntities();

/* Update the stars */

doStars();

/* Do the collisions */

doCollisions();

/* Draw everything */

draw();

/* Increment the player's score while the player's still alive */

if (player.active == 1)
{
    game.score += 10;
}

/* Sleep briefly to stop sucking up all the CPU time */

delay(frameLimit);

frameLimit = SDL_GetTicks() + 16;
The function addRock will attempt to add a new rock to the playfield. We will look at this function later. We also increment the player's score by 10 each frame. We will use the score to determine the odds of adding another rock to the playfield. We have made a small change to the drawPlayer function in player.c:
void drawPlayer()
{
    /* Draw the image in the player structure */
    
    if (player.active == 1)
    {
        drawImage(player.sprite, player.x, player.y);
    }
}
This means that if the player is not active, i.e. it has been destroyed, then it will not be drawn on the screen. Finally, we will look at rock.c rock.c has two functions:
void addRock(int score)
{
    int i, rockChance;
    
    /* The chance of a rock being added depends upon the player's score */
    
    if (score < 10000)
    {
        rockChance = rand() % 60;
    }
    
    else if (score < 20000)
    {
        rockChance = rand() % 45;
    }
    
    else if (score < 30000)
    {
        rockChance = rand() % 30;
    }
    
    else
    {
        rockChance = rand() % 15;
    }
    
    if (rockChance != 0)
    {
        return;
    }
    
    i = getFreeEntity();
    
    if (i == -1)
    {
        return;
    }
    
    entity[i].x = SCREEN_WIDTH + rand() % 100;
    entity[i].y = rand() % SCREEN_HEIGHT;
    entity[i].action = &moveRock;
    entity[i].speed = 1 + (rand() % 5);
    entity[i].draw = &drawAnimatedEntity;
    entity[i].animID = ROCK_ANIMATION;
    entity[i].currentFrame = 0;
    entity[i].frameTimer = 3;
    entity[i].w = animation[entity[i].animID].frame[entity[i].currentFrame]->w;
    entity[i].h = animation[entity[i].animID].frame[entity[i].currentFrame]->h;
}
We pass in the player's score to addRock. The intention to make the game more difficult by increasing the probability of a rock appearing based upon the player's score. So when the player's score is less than 10,000 points, the chance of a rock appearing is 1 in 60, which means that a new rock will appear once a second. At 20,000 points the odds increase to 1 in 45, then 1 in 30 at 30,0000 and finally 1 in 15. The rand function returns a random positive number which we use the modulus operator on the value to give us a number between 0 and n - 1, where n is the modulus value. If this value is not 0 then we will not create a rock and simply exit the function.
Provided we need to create a rock, we call getFreeEntity as we have in previous tutorials. We then set the x and y coordinates to random values and also set the speed to a random value between 1 and 5. We set the animID to ROCK_ANIMATION which is the rock animation index. Since we have moved the animation frame index and frame counter to the Entity structure, we set them here, using currentFrame and frameTimer respectively. We also set the width and height of the entity to the animation's current frame's width and height. The Entity's action and draw functions are set to moveRock and drawAnimatedEntity. The moveRock function moves the rock from right to left and also updates its animation:
static void moveRock(void)
{
    /* Move the rock from right to left, once it goes off the screen, kill it */
    
    self->x -= self->speed;
    
    if (self->x <= -self->w)
    {
        self->active = 0;
    }
    
    /* Update the animation frame */
    
    self->frameTimer--;
    
    if (self->frameTimer <= 0)
    {
        self->frameTimer = 3;
        self->currentFrame++;
        
        if (self->currentFrame >= animation[self->animID].frameCount)
        {
            self->currentFrame = 0;
        }
    }
}
The rock will move horizontally from right to left based upon its speed. When its x value is less than or equal to its negative width (i.e. it has completely moved off the left hand side of the screen), then it is made inactive. Otherwise we decrement its frameTimer and if that value becomes less than or equal to 0 then we move to the next animation frame and reset the frameTimer. If the currentFrame is greater than or equal to the animation's frameCount we reset the currentFrame to 0. The stars.c file is the same as in the previous tutorial so we will pass over it too.

 Conclusion

Hopefully if you have read all the tutorials then you will see that putting together a very simple game does not take too much effort and that a lot of the functions from earlier tutorials are just reused or extended as needed. If you are comfortable with this tutorial then you could try extending it to allow the player to fire bullets and destroy the rocks. In the next tutorial we will move onto more intermediate topics and create a basic platform game.

 Downloads

Source Code - tutorial10.tar.gz

Basic Game Tutorial #9 - Starfields

Basic Tutorials
Basic Game Tutorial #9 - Starfields
A simple starfield

 Introduction

This tutorial demonstrates how to display a moving starfield on the screen.
Compile and run tutorial09. The stars on the screen will move from right to left at various speeds. Pressing Escape or closing the window will exit the program.

 An indepth look

Creating a moving starfield is very simple and there are only a few files of note for this tutorial. First, we define a structure for our starfield in structs.h:
typedef struct Star
{
    int x, y, speed;
} Star;
The x and y variables define the star's location on the screen and the speed defines its movement speed. Next we will look at the stars.c file. stars.c contains 4 functions:
void resetStars()
{
    int i;

    for (i=0;i<MAX_STARS;i++)
    {
        star[i].x = rand() % SCREEN_WIDTH;
        star[i].y = rand() % SCREEN_HEIGHT;
        star[i].speed = 1 + (rand() % 12);
    }
}
resetStars randomly places all of the stars on the screen with a random speed. We need to do this before entering our main loop otherwise all of the stars might suddenly all appear on the right hand side of the screen which wouldlook wrong.
void doStars()
{
    int i;

    for (i=0;i<MAX_STARS;i++)
    {
        star[i].x -= star[i].speed;

        if (star[i].x < 0)
        {
            star[i].x = SCREEN_WIDTH + rand() % 20;
            star[i].y = rand() % SCREEN_HEIGHT;
            star[i].speed = 1 + (rand() % 12);
        }
    }
}
doStars loops though all of the stars and moves them across the screen from right to left by subtracting their speed from their current x position. If a star's x position becomes negative then it has moved off the screen, so we randomly set its x position to be the screen's width plus a random amount to stop it appearing immediately and also set its y and speed.
void drawStars()
{
    int i;
    SDL_Rect rect;

    for (i=0;i<MAX_STARS;i++)
    {
        if (star[i].x < SCREEN_WIDTH)
        {
            rect.x = star[i].x;
            rect.y = star[i].y;
            rect.w = 1;
            rect.h = 1;
    
            SDL_FillRect(screen, &rect, getStarColor(star[i].speed));
        }
    }
}
drawStars is another simple function. We loop through all the stars and, if their x position is on the screen, we draw it using SDL_FillRect. We determine its colour by calling the function getStarColor which is described below.
int getStarColor(int speed)
{
    SDL_Color color;
    
    switch (speed)
    {
        case 1:
        case 2:
        case 3:
            color.r = 159;
            color.g = 159;
            color.b = 159;
            break;
                
        case 4:
        case 5:
        case 6:
            color.r = 191;
            color.g = 191;
            color.b = 191;
            break;
                
        case 7:
        case 8:
        case 9:
            color.r = 223;
            color.g = 223;
            color.b = 223;
            break;
            
        default:
            color.r = 255;
            color.g = 255;
            color.b = 255;
            break;
    }
    
    return SDL_MapRGB(screen->format, color.r, color.g, color.b);
}
getStarColor takes a simple parameter, the speed we wish to test. We then switch this speed and set the RGB values of the SDL_Color. The lower the speed, the darker the star will be, since it's further away. Finally, we use SDL_MapRGB to get the correct value for the colour we've chosen and return it. In main.c we animate the stars as follows:
/* Initialise the stars */

resetStars();

/* Loop indefinitely for messages */

while (go == 1)
{
    getInput();
    
    /* Update the stars */
    
    doStars();
    
    /* Blank the screen */
    
    SDL_FillRect(screen, NULL, 0);
    
    /* Draw the stars */
    
    drawStars();
    
    /* Swap the buffers */
    
    SDL_Flip(screen);
    
    /* Sleep briefly to stop sucking up all the CPU time */
    
    SDL_Delay(1);
    
    delay(frameLimit);
    
    frameLimit = SDL_GetTicks() + 16;
}
First we position the stars before entering the main loop by calling resetStars. Then, in our main loop, we call doStars to update their positions and drawStars to draw them. That's really all there is to it.

 Conclusion

Now that we can accept user input, perform collision detection, use SDL_TTF to render text to the screen and animate sprites, we can put this all together to make a small game. We will do this in the next tutorial.

 Downloads

Source Code - tutorial09.tar.gz

Basic Game Tutorial #8 - Sprites and Animation

Basic Tutorials
Basic Game Tutorial #8 - Sprites and Animation
An animating star

 Introduction

The easiest way to approach an animation is to think of it as a series of images that are shown one after the other. This tutorial will show you one way of achieving this.
Compile and run tutorial08. The star will spin on the screen until the window is closed or Escape is pressed.

 An indepth look

We will start this tutorial by looking at the animation file, data/anim/star.dat. The animation data file contains two lines. The first line is the number of frames in the animation and the rest are the names of the individual frames to load. This file is read by the loadAnimation function in animation.c which we will look at later. For the moment we will start with structs.h.
We define a structure for our animation as follows:
typedef struct Animation
{
    int frameCount, frameIndex, counter;
    SDL_Surface **frame;
} Animation;
The frameCount variable stores the number of frames in the Animation. frameIndex is the frame number that we are currently indexing. counter is the timer we will count down from before incrementing the frameIndex. Finally frame is an array of pointers to SDL_Surfaces. We will now look at the animation.c file. The animation.c file contains 4 functions. As always, we will look at each one in turn.
void loadAnimation(char *name, Animation *anim)
{
    /* Load up the data file that describes the animation */
    
    int i;
    FILE *fp = fopen(name, "rb");
    char frameName[20];
    
    if (fp == NULL)
    {
        printf("Failed to load animation file %s\n", name);
        
        exit(1);
    }
    
    /* Read the frame count */
    
    fscanf(fp, "%d", &anim->frameCount);
    
    /* Allocate space for the animation */
    
    anim->frame = (SDL_Surface **)malloc(anim->frameCount * sizeof(SDL_Surface *));
    
    if (anim->frame == NULL)
    {
        printf("Ran out of memory when creating the animation for %s\n", name);
        
        exit(1);
    }
    
    /* Now load up each frame */
    
    for (i=0;iframeCount;i++)
    {
        fscanf(fp, "%s", frameName);
        
        anim->frame[i] = loadImage(frameName);
        
        if (anim->frame[i] == NULL)
        {
            printf("Failed to load animation frame %s\n", frameName);
            
            exit(1);
        }
    }
    
    /* Set the initial frame index to 0 */
    
    anim->frameIndex = 0;
    
    /* Set the animation counter */
    
    anim->counter = ANIM_SPEED;
}
The loadAnimation function takes 2 parameters, the animation file to load and the address of the animation structure we wish to use. Firstly, we try and read the animation file and if this attempt fails then we flag the error and exit. Once we have successfully opened the file we read the first line of the file which is the number of frames in the animation and set this to the frameCount. We then use this value to allocate the memory required for the frames. Again, if this fails then we flag the error and exit. Now we loop through the frameCount, read the location of the image and load it using the loadImage function. We then assign the image to the current frame of the animation. If any of the images fails to load the will log the error and exit. Once all the frames have been loaded, we assign the frameIndex to 0, which is the first frame in the animation and set the counter to ANIM_SPEED.
void freeAnimation(Animation *anim)
{
    int i;
    
    /* Loop through each of the frames and free it */

    if (anim->frame != NULL)
    {
        for (i=0;i<anim->frameCount;i++)
        {
            if (anim->frame[i] != NULL)
            {
                SDL_FreeSurface(anim->frame[i]);
            }
        }
        
        free(anim->frame);
    }
}
freeAnimation works by simply looping through all of the frames in the animation and frees each one of them. We call this function once we are done with the animation. Performing the animation is relatively straight forward:
void doAnimation(Animation *anim)
{
    anim->counter--;
    
    if (anim->counter <= 0)
    {
        anim->frameIndex++;
        
        if (anim->frameIndex == anim->frameCount)
        {
            anim->frameIndex = 0;
        }
        
        anim->counter = ANIM_SPEED;
    }
}
doAnimation is the function which updates the frames in the animation. It does this by decrementing the counter of the animation. When the counter reaches zero or less, we increase the frameIndex of the animation. If our frameIndex is equal to the animation's frameCount, we set the frameIndex back to 0 to continue to loop the animation. When we move another frame, we reset the counter back to ANIM_SPEED, which is defined in defs.h. The final function in this file draws the current animation frame:
void drawAnimation(Animation *anim, int x, int y)
{
    drawImage(anim->frame[anim->frameIndex], x, y);
}
This function simply calls drawImage function and passes in the surface of the animation's current frameIndex. In graphics.c, we use the updateScreen function to draw our animation to the screen. In graphics.c, we call the drawAnimation function as follows:
drawAnimation(&starAnim, 288, 216);
We pass in the animation we wish to draw and render it to 288, 216 on the screen. Finally, in main.c, we load up the animation and update and draw it in our standard loop:
/* Load up the star animation */

loadAnimation("data/anim/star.dat", &starAnim);

/* Loop indefinitely for messages */

while (go == 1)
{
    getInput();
    
    /* Update the animation frame */
    
    doAnimation(&starAnim);
    
    /* Draw the animation to the screen */
    
    updateScreen();
    
    /* Sleep briefly to stop sucking up all the CPU time */
    
    delay(frameLimit);
    
    frameLimit = SDL_GetTicks() + 16;
}

 Conclusion

The disadvantage to the way that we have done animation is that any game objects using this method will all animate in synchronization. This might not be a bad thing though, depending upon what you want to do. In another tutorial we will look at making game objects animate independently.

 Downloads

Source Code - tutorial08.tar.gz

Basic Game Tutorial #7 - SDL_TTF

Basic Tutorials
Basic Game Tutorial #7 - SDL_TTF
Displaying score using SDL_TTF

 Introduction

In this tutorial, we will look at SDL_TTF and how we can use it to display text on the screen.
Compile and run tutorial07. You can control the ship by using the arrow keys (not the ones on the numeric pad). Pressing the space key will fire a bullet, which will move to the right from the ship's current position. If the bullet hits one of the UFOs then both it and the UFO will be removed. The bullet will also be removed if it goes past the right edge of the screen. Whenever you destroy a UFO, your score will increase by 100.

 An indepth look

A lot of the code has remained the same, so we will go through the files that have changed starting with defs.h
SDL_TTF requires the SDL_TTF.h include, so we add it here
#include "SDL/SDL_ttf.h"
Now that we have done this we can look at structs.h We have added a new structure called game to store the score, screen and font:
typedef struct Game
{
    int score;
    SDL_Surface *screen;
    TTF_Font *font;
} Game;
The font will be loaded up later on in font.c. Like SDL_Mixer, SDL_TTF must be initialised and closed after we have used it. We perform both these calls in init.c. In our init function we initialise SDL_TTF:
if (TTF_Init() < 0)
{
    printf("Couldn't initialize SDL TTF: %s\n", SDL_GetError());

    exit(1);
}
The code should be self explanitory. In our cleanup function, we need to close the font in the same way that have to free music and surfaces:
/* Close the font */

closeFont(game.font);
We will look at this function later. We also need to close SDL_TTF which we do after we have closed all used fonts:
/* Close SDL_TTF */

TTF_Quit();
Now that we have dealt with initialising and closing the library, we will look at loading and displaying a font on the screen. font.c contains 3 functions which we will look at one at a time.
TTF_Font *loadFont(char *name, int size)
{
    /* Use SDL_TTF to load the font at the specified size */
    
    TTF_Font *font = TTF_OpenFont(name, size);

    if (font == NULL)
    {
        printf("Failed to open Font %s: %s\n", name, TTF_GetError());

        exit(1);
    }
    
    return font;
}
loadFont takes 2 arguments, the filename of the file to load and the size of the font to create, in pt. We call the SDL_TTF function TTF_OpenFont, passing in the filename and the size which will return a TTF_Font. As always, we verify that the font was loaded correctly before we continue. If not, then we will exit. Otherwise we return the newly created font.
void closeFont(TTF_Font *font)
{
    /* Close the font once we're done with it */
    
    if (font != NULL)
    {
        TTF_CloseFont(font);
    }
}
closeFont is quite simple, we pass in the font we wish to close and pass it to TTF_CloseFont, provided that it is not NULL. The final, and longest function in the file, deals with rendering a String to the screen:
void drawString(char *text, int x, int y, TTF_Font *font, int centerX, int centerY)
{
    SDL_Rect dest;
    SDL_Surface *surface;
    SDL_Color foregroundColor, backgroundColor;
    
    /* White text on a black background */
    
    foregroundColor.r = 255;
    foregroundColor.g = 255;
    foregroundColor.b = 255;
    
    backgroundColor.r = 0;
    backgroundColor.g = 0;
    backgroundColor.b = 0;
    
    /* Use SDL_TTF to generate a string image, this returns an SDL_Surface */

    surface = TTF_RenderUTF8_Shaded(font, text, foregroundColor, backgroundColor);

    if (surface == NULL)
    {
        printf("Couldn't create String %s: %s\n", text, SDL_GetError());

        return;
    }
    
    /* Blit the entire surface to the screen */

    dest.x = (centerX == 1 ? (game.screen->w - surface->w) / 2 : x);
    dest.y = (centerY == 1 ? (game.screen->h - surface->h) / 2 : y);
    dest.w = surface->w;
    dest.h = surface->h;

    SDL_BlitSurface(surface, NULL, game.screen, &dest);
    
    /* Free the generated string image */

    SDL_FreeSurface(surface);
}
The drawString function takes 6 arguments, the String to be printed, the x and y coordindates of the String, the font to use and whether or not to center it horizontally and vertically on the screen respectively. The function TTF_RenderUTF8_Shaded function takes 4 arguments, the font to use, String to print and the foreground and background colours. The colours are SDL_Color structures and we set the r, g and b values of the foregroundColor to 255, which is white. We set the backgroundColor to black, indicated by 0. This function will then return an SDL_Surface which we can blit to screen as usual. If the rendering fails for any reason then a NULL surface will be returned. If the centerX or centerY variables are set then we center the surface on the screen respectively, by taking substracting the width (or height) of the surface from the width (or height) of the screen and then halving the result. When we have blitted the surface to the screen we must free it afterwards, by calling SDL_FreeSurface. We will call drawString in the draw function. In main.c, we call the loadFont function.
We have also added made a slight change to collisions.c:
if (collision(entity[i].x, entity[i].y, entity[i].sprite->w, entity[i].sprite->h,
entity[j].x, entity[j].y, entity[j].sprite->w, entity[j].sprite->h) == 1)
{
    /* If a collision occured, remove both Entities */
    
    entity[j].active = 0;
    
    entity[i].active = 0;
    
    /* Add 100 points to the score */
    
    game.score += 100;
}
When a UFO is destroyed, we add 100 points to our score variable in our game structure. We will now look at our drawing function to see how the score is displayed on the screen.
To generate the score text, we need to create a String from our score. We can do this easily using the C function sprintf.
char text[20];

/* Blank the screen */

SDL_FillRect(game.screen, NULL, 0);

/* Draw the score */

sprintf(text, "SCORE: %05d", game.score);

drawString(text, 100, 10, game.font, 1, 0);
Now that we have created a String, we can call drawString to render the text to 100, 10. Note though that since our 5th argument is set to 1, we want to center the score horizontally so the value of 100 will actually be ignored.

 Conclusion

SDL_TTF has many ways of rendering text, ranging from quick and dirty to slow and beautiful. You can view the API by clicking on this link.
In the next tutorial we will look at one way of doing animation in SDL.

 Downloads

Source Code - tutorial07.tar.gz

Basic Game Tutorial #6 - Collision Detection

Basic Tutorials
Basic Game Tutorial #6 - Collision Detection
Collision Detection

 Introduction

Collision detection is a vital part of any game. In this tutorial we will look at 2D collision detection so that you can fire a bullet and make it hit an enemy UFO.
Compile and run tutorial06. You can control the ship by using the arrow keys (not the ones on the numeric pad). Pressing the space key will fire a bullet, which will move to the right from the ship's current position. If the bullet hits one of the UFOs then both it and the UFO will be removed. The bullet will also be removed if it goes past the right edge of the screen.

 An indepth look

Most of the code has not changed very much, so we will only look at the functions that have changed or that are new. We will start will structs.h
We update the Entity structure by adding another variable called type:
typedef struct Entity
{
    int active, type;
    int x, y, thinkTime;
    SDL_Surface *sprite;
    void (*action)(void);
    void (*draw)(void);
} Entity;
The type variable will be used in the collision detection routine, since we don't want bullets to collide against each other. In main.c, we call two new functions addUFO and doCollisions. These two functions will be looked at later on.
We have made a minor change to bullet.c:
entity[i].x = x;
entity[i].y = y;
entity[i].action = &moveStandardBullet;
entity[i].draw = &drawStandardEntity;
entity[i].sprite = getSprite(BULLET_SPRITE);
entity[i].type = TYPE_BULLET;
We set the Entity type to TYPE_BULLET. This helps us identify the Entity as being a bullet so that we don't try and collide bullets against each other. The new file ufo.c, contains all the code for dealing with the UFOs. There are only two functions in this file and it is essentially the same as bullet.c:
int i = getFreeEntity();

if (i == -1)
{
    printf("Couldn't get a free slot for a UFO!\n");
 
    return;
}

entity[i].x = x;
entity[i].y = y;
entity[i].action = &moveUFO;
entity[i].draw = &drawStandardEntity;
entity[i].sprite = getSprite(UFO_SPRITE);
entity[i].type = TYPE_ENEMY;
As in the bullet.c, we get search for a free Entity to assign to our UFO and set its functions. The moveUFO function is below, but it is an empty function, essentially meaning that the UFO will do nothing. Finally, we set the Entity type to TYPE_ENEMY.
Now we will look at the collision detection. This function is located in collisions.c. We will start by looking at the actual detection routine itself:
int collision(int x0, int y0, int w0, int h0, int x2, int y2, int w1, int h1)
{
    int x1 = x0 + w0;
    int y1 = y0 + h0;

    int x3 = x2 + w1;
    int y3 = y2 + h1;

    return !(x1<x2 || x3<x0 || y1<y2 || y3<y0);
}
The 2D collision detection routine is box to box, which is not accurate, but it is very fast. There are routines for pixel perfect collision detection, but we will not be looking at these. This function takes 8 parameters, the x, y, width and height of the first box and the x, y, width and height of the second box. Using these parameters we then construct the coordinates of two rectangles and test if they overlap. The function will return 1 if they do and 0 if they don't. We will now look at its use:
void doCollisions()
{
    int i, j;

    /* Check each entity against the rest, skipping over inactive ones */

    for (i=0;i<MAX_ENTITIES;i++)
    {
        if (entity[i].active == 0)
        {
            continue;
        }

        for (j=0;j<MAX_ENTITIES;j++)
        {
            /* Don't collide with yourself, inactive entities or entities of the same type */
            
            if (i == j || entity[j].active == 0 || entity[j].type == entity[i].type)
            {
                continue;
            }
            
            /* Test the collision */

            if (collision(entity[i].x, entity[i].y, entity[i].sprite->w, entity[i].sprite->h,
            entity[j].x, entity[j].y, entity[j].sprite->w, entity[j].sprite->h) == 1)
            {
                /* If a collision occured, remove both Entities */
                
                entity[j].active = 0;
                
                entity[i].active = 0;
                
                break;
            }
        }
    }
}
In doCollisions we run two loops, one inside the other, since we need to check each Entity against all the others. In our outer loop, we first check that the Entity we are indexing is actually active, otherwise we simply pass over it. Then, in our inner loop, we skip over any Entity that is either the same type as the current Entity, is no active or is Entity that we are currently checking. We then run our collision test by passing in the x, y, width and height of both Entities and checking the response. If we get back a value of 1 then we set both the Entities to inactive and break out of the inner loop.

 Conclusion

So now you know how to move a sprite, fire a bullet and make it hit something. In later tutorials we will look at a health system so that when something is hit it doesn't die straight away, but you could always try this yourself. The theory is very simple: add a additional variable to the Entity structure to store the health. When a collison occurs you decrease the health and if it is less than or equal to 0 you remove the Entity.
In the next tutorial we will look at SDL_TTF so that we can display information such as the player's score.

 Downloads

Source Code - tutorial06.tar.gz

Basic Game Tutorial #5 - Firing bullets

Basic Tutorials
Basic Game Tutorial #5 - Firing bullets

Firing bullets

 Introduction

Now that we can move a sprite around the screen, we will look into firing bullets. These tutorials will go into less detail about loading images and sounds because they have been covered in earlier tutorials and the theory is essentially the same.
Compile and run tutorial05. You can control the ship by using the arrow keys (not the ones on the numeric pad). Pressing the space key will fire a bullet, which will move to the right from the ship's current position.

 An indepth look

To deal with the player and the bullets, we update the Entity structure as follows:
typedef struct Entity
{
    int active;
    int x, y, thinkTime;
    SDL_Surface *sprite;
    void (*action)(void);
    void (*draw)(void);
} Entity;
We will be creating an array of Entities so the easiest way to determine whether an Entity is being used is to set a variable stating this. If the active variable is 0 then this Entity is not being used and can therefore be used to create a new bullet or whatever we want to create from it. The thinkTime variable is a timer that, in this tutorial is used to limit the number of bullets we can fire per second. action and draw are simply function pointers to our action and drawing functions respectively.
The Control function has another value, fire which is used to let us know that we want to attempt to fire a bullet.
We will first look at entites.c. This file has 5 functions in it. We will look at these one at a time.
The clearEntities function loops through all of the available Entities and marks them as inactive using the C function memset. This ensures that at the start of the program, there are empty slots to fire bullets. In a larger game you would need to reset everything at the start of a level to make sure that when it starts there aren't enemies, bullets etc. appearing randomly on the screen.
getFreeEntity is a function that will be commonly used. It will loop through all the available Entities and return the index of the first one it finds that is marked as inactive. It will also initialise the Entity to ensure that all of its variables and functions are set to 0. If the function cannot find a free Entity, it will return -1 and you would deal with it as appropriate. In our code we will simply flag an error and carry on.
doEntities is used in the main game loop. This function loops through all the Entities and point its memory address to the self Entity global pointer, which we will go into more detail later on. If the Entity is marked as active, we call its action function.
drawEntities is exactly the same as doEntities, except that we call the draw function instead.
The drawStandardEntity function is similar to the drawPlayer functions from previous tutorials. Note that we are using the self pointer in this function. You will see how this is used later.
In input.c we add add the reading of the space key to the inputs.
We'll now look at player.c, specifically the doPlayer function. At the start of the function we decrease the player's thinkTime, and if it is less than or equal to 0, we set it to 0.
player.thinkTime--;

if (player.thinkTime <= 0)
{
    player.thinkTime = 0;
}
The thinkTime is what controls the player's ability to fire bullets. We constantly decrease this value in each loop until it reaches 0. We must stop it from simply decreasing continously as the integer value would eventually wrap around and then the player would not be able to fire a bullet for a very long time (since it would then become positive again). The rest of the code is the same apart from the reading of the fire button:
if (input.fire == 1)
{
    /* You can only fire when the thinkTime is 0 or less */
    
    if (player.thinkTime <= 0)
    {
        addBullet(player.x + player.sprite->w, player.y + (player.sprite->h / 2));
        
        player.thinkTime = MAX_RELOAD_TIME;
    }
}
When the program detects the space key, it will then attempt to fire a bullet. A bullet can only be fired if the thinkTime is 0 or less, so if this is true the we call the addBullet function. This function takes the starting x and y coordinates, which we set to the right hand side of the player's sprite and roughly halfway down the player's height respectively. We will look into this function in more detail later. Once we have fired a bullet, we set the thinkTime back to the MAX_RELOAD_TIME, otherwise the player would be able to fire again immediately afterwards. The lower the value is for MAX_RELOAD_TIME, the sooner the player will be able to fire another bullet. The inverse is also true, so try changing this value to see what happens. It is in defs.h.
bullet.c handles the creation and movement of the bullets. We will look at each of the functions in this file in turn:
void addBullet(int x, int y)
{
    int i = getFreeEntity();
    
    if (i == -1)
    {
        printf("Couldn't get a free slot for a bullet!\n");
        
        return;
    }
    
    entity[i].x = x;
    entity[i].y = y;
    entity[i].action = &moveStandardBullet;
    entity[i].draw = &drawStandardEntity;
    entity[i].sprite = getSprite(BULLET_SPRITE);
    
    /* Play a sound when the shot is fired */
    
    playSound(BULLET_SOUND);
}
The addBullet function first gets a free Entity by calling the getFreeEntity function. If we get back -1 then we ran out of Entities to create bullets from so we will just log the error to the console and return. Using the index number we get back we set the Entity's x and y using the values passed in. For the action, we set this to the moveStandardBullet function found later on in this file. We use drawStandardEntity for the draw function which we covered earlier in the tutorial. We also set the sprite to the BULLET_SPRITE. Finally, we call the playSound function to play the sound of a bullet being fired.
The second function, moveStandardBullet is the function called when the bullet's action function is called. Note that we use the self pointer which means that we don't need to track the Entity itself and we can also manipulate the values without having to return anything.
static void moveStandardBullet()
{
    /* Move the bullet across the screen */
    
    self->x += BULLET_SPEED;
    
    /* Kill the bullet if it moves off the screen */
    
    if (self->x >= SCREEN_WIDTH)
    {
        self->active = 0;
    }
}
The function is simple enough, the bullet will move at the speed defined by BULLET_SPEED and once it reaches the edge of the screen, it will be marked as inactive, by setting its active variable to 0. Since we are moving from left to right, we need to check the position of the left edge of the sprite to prevent it from being made inactive prematurely.

 Conclusion

Like the previous tutorial, firing a basic bullet is quite simple. In the next tutorial, we will look into collision detection.

 Downloads

Source Code - tutorial05.tar.gz

Basic Game Tutorial #4 - Controlling a sprite

Basic Tutorials
Basic Game Tutorial #4 - Controlling a sprite
Moving a sprite

 Introduction

This tutorial will demonstrate how to move a loaded image around a screen. This tutorial uses the keyboard for the input but future tutorials will explain how to use the joystick.
Compile and run tutorial04. You can control the ship by using the arrow keys (not the ones on the numeric pad). The ship is constrained to the bounds of the window.


 An indepth look

The defs.h now contains an enumeration which will be used to store the sprites used in the program. This makes storing and referencing them a lot easier. Currently though there is only one sprite that is loaded, PLAYER_SPRITE, but more will be loaded in future tutorials. A new structure has been added to structs.h. The Entity structure allows us to store the coordinates of the ship as well as a pointer to its image. We also add another structure Control which we will use to store the inputs for our directions.
In main.c we call loadAllSprites to load up all of the sprites that we will use in the program and then initialise the player by calling initPlayer, which sets the player's sprite and also centers the player to the middle of the screen. We then enter the main loop and call getInput to collect the input keys that are pressed followed by doPlayer which handles the player's movement on the screen. draw simply draws the player's sprite to the screen. Finally, to allow smooth movement of the sprite on the screen, we limit the number of frames per second to 60. At the start of the main function, we create an unsigned int called frameLimit
unsigned int frameLimit = SDL_GetTicks() + 16;
The function SDL_GetTicks gets the number of milliseconds that have passed since SDL was initialised. The value of 16 will allow us to limit the number of frames to 60, since 1000 / 60 = 16. After we call frameLimit we set the next frame limit to the current ticks + 16 again:
frameLimit = SDL_GetTicks() + 16;
In input.c add code to read the up, down, left and right arrow keys on the keyboard. We detect that the up arrow on the keyboard has been pressed with the following code:
case SDLK_UP:
    input.up = 1;
break;
This sets the up variable in our Control structure to 1. We will read this value later on to test if we need to move the player's sprite up the screen. The reason why we store this value instead of just moving the player's sprite immediately is because of keyboard repeat delay. Just like when you are typing, if you hold down a key, the letter will be printed, followed by a delay and then the letter will be printed repeatedly depending upon your keyboard's repeat rate and delay, which is not desirable. When the key is released, we will set the up variable in the structure to 0:
case SDLK_UP:
    input.up = 0;
break;
The process for detecting the left, right and down key presses are the same, so we will not cover these.
player.c contains three functions, initPlayer, doPlayer and drawPlayer. We have already covered what initPlayer does, so we will look at drawPlayer. We take the values set in our control structure and test them one at a time.
if (input.up == 1)
{
    player.y -= 3;

    /* Don't allow the player to move off the screen */

    if (player.y < 0)
    {
        player.y = 0;
    }
}
The above code will test if the up variable in our structure has been set to 1 and if it has then we will move the player's sprite 3 pixels up the screen. We need to prevent the player from being able to move off the top of the screen however, but this is quite simple. The top most pixel of the screen is 0, like in arrays and the player's sprite is drawn from the top left corner, so we just check to see if the player's position is less than 0 and if it is then we set it to 0. Preventing the player from moving off the bottom of the screen is similar, we just need to take some more details into account.
if (input.down == 1)
{
    player.y += 3;

    /* Don't allow the player to move off the screen */

    if (player.y + player.sprite->h >= SCREEN_HEIGHT)
    {
        player.y = SCREEN_HEIGHT - (player.sprite->h + 1);
    }
}
To stop the player from moving off the bottom of the screen, we need to includes the sprite's height in our check. Since the screen coordinates start at 0, the screen height will be 1 unit less, so if the player's y coordinate plus its height is greater than or equal to the screen's height then the player's sprite has left the screen. To correct this, we need to move the ship as close to the edge of the screen as possible without going over it, so we place the player's y coordinate at the screen's height and then subtract the sprite's height. Since the screen coordinates start at 0 though we are still 1 pixel over so we need to move 1 additional pixel up to correct this. Logic for the movement of the x coordinate is the same, except we check the sprite's width rather than its height. After all of this, the drawPlayer function simply calls the drawImage function, that we have used in previous tutorials, to draw the player's sprite to the screen.
Finally, we will look at the drawing. draw.c contains two functions. The draw function blanks the screen, draws the player and swaps the buffers. We also sleep briefly to return some time back to the CPU. The second function, delay, limits the program's speed to 60 frames per second. Firstly, we pass in the desired tick time, which is how many ticks need to have passed before we can continue. We then get the current ticks:
unsigned int ticks = SDL_GetTicks();
Now we check to see if the tired tick time is in the past.
if (frameLimit < ticks)
{
    return;
}
If this happens then we can continue with the program without any need to sleep. If the desired tick time is more than the current tick time + 16 milliseconds, then we will sleep for (at least) 16 milliseconds and check it again on the next cycle.
if (frameLimit > ticks + 16)
{
    SDL_Delay(16);
}
Finally, if the difference in the tick times is between 1 and 16 then we will sleep for that difference.
else
{
    SDL_Delay(frameLimit - ticks);
}
This ensures that we limit the number of frames per second of the program and also return spare cycles back to the CPU.

 Conclusion

As you can see, controlling basic movement is quite straight forward. In the next tutorial, we will look at firing a bullet and how to control reload times.

 Downloads

Source Code - tutorial04.tar.gz

Basic Game Tutorial #3 - Images and Sound

Basic Tutorials
Basic Game Tutorial #3 - Images and Sound
An SDL window displaying an image and playing a sound
 Introduction

This third tutorial deals with playing sound in SDL.
Compile and run tutorial03. By pressing Space, you can play a sound clip. Pressing Escape or closing the window will exit.

 An indepth look

Two new files are introduced audio.c and audio.h.
The defs.h file contains a new include, SDL/SDL_mixer.h. SDL is limited to playing WAV files so SDL_mixer allows us to also play music and use other sound types such as OGG. Like SDL_image, it is a very useful addition. main.h introduces a new structure, Mix_Chunk, which we will use to store the sound that we wish to play.
main.c contains an additional function call, loadSound which is stored in audio.c.
In init.c we initialise SDL's audio system in addition to its video system.
SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO)
Once again, if this call fails then we cannot continue, so we would simply exit the program. Once the system is up and running, we attempt open the audio:
Mix_OpenAudio(22050, AUDIO_S16SYS, 2, 4096)
This call can fail for a valid reason, such as another process locking the audio or the system simply not having a sound card. Best practice would be to set a flag telling the program not to play any audio, but since this tutorial is reliant on it, we would exit if we couldn't play any sounds. The Mix_OpenAudio function takes 4 arguments, the frequency of the audio, the format of the audio, the channels (1 for mono, 2 for stereo) and the chunksize of the audio. 22050 is fairly standard for most games, it can be as high as 44100, which is CD quality, but this uses up a lot of CPU time so we leave it at 22050. AUDIO_S16SYS is standard 16 bit sound, there are other formats but some of them are not portable to other systems. 2 simply tells the audio to be played in stereo rather than mono (1). 4096 is a reasonable chunksize, if the chunksize is too big then there will be a delay between when the sound is told to play and when it actually does play. Conversely, if the chunksize is too small then the sound will stutter because of the constant filling and emptying.
Like SDL_Surfacess, Mix_Chunks must be freed when they are no longer needed. The function call
Mix_FreeChunk(dexterBark);
frees the chunk. The audio system must also be closed at the end of the program. So
Mix_CloseAudio();
is called before SDL_Quit. input.c now calls the function playSound when Space is pressed. The function is stored in audio.c
graphics.c has not changed.
audio.c contains 2 function calls, loadSound and playSound. The loadSound function loads the audio file
Mix_Chunk *sfx = Mix_LoadWAV(name);
As with IMG_Load, there is no need to specify the file type as SDL_mixer will check the extension of the file. Mix_LoadWAV will return the loaded audio chunk or NULL if it fails.
playSound will play the specified audio file by calling
Mix_PlayChannel(-1, sfx, 0);
The first argument is the channel to play the sound on. You may wish to do this if you wanted to have, say, a person speaking and you wanted to have them interupted. Specifying -1 tells SDL to play the sound on the first free channel that it finds. The second argument is the sound chunk to play and the third argument is the additional number of times to play the sound, so 0 means to play it 0 additional times and 1 means to play it 1 additional time etc. Specifying -1 will loop the sound forever.


 Conclusion

Now that we have dealt with opening windows, displaying images and playing sounds, we can start looking at intermediate topics such as input control and collision detection.

 Downloads

Source Code - tutorial03.tar.gz

Monday, 26 September 2011

Basic Game Tutorial #2 - Displaying an image

Basic Tutorials
Basic Game Tutorial #2 - Displaying an image
An SDL window displaying an image

 Introduction

In this second tutorial, we will explain how to display an image in SDL. If you have not read the first tutorial, you are advised to do so as only new function calls will be explained.
Compile and run the program as before. Note that the executable is called tutorial02. If successful you should see a window similar to the one in the screenshot. As before, you can exit the program by either closing the window or pressing Escape.

 An indepth look

In the src directory, you will see that there are 2 new files: graphics.c and graphics.h. Other files have changed too though, so we will go through these as well.
The defs.h file contains a new include, SDL/SDL_image.h. SDL is limited to only loading BMP files, so this include allows us to load other file types, such as JPEGs and PNGs, so it is very useful to have. main.h includes an additional SDL_Surface, which we will use to store the image that we load.
main.c has changed slightly. We now have to additional function calls, loadImage and updateScreen. Both these functions are in graphics.c which we will look at shortly.
init.h contains a reference to the external new SDL_Surface.
init.c is slightly different. We initialise SDL as before, but we pass in an additional flag to the window creation
screen = SDL_SetVideoMode(640, 480, 0, SDL_HWPALETTE|SDL_DOUBLEBUF);
The SDL_DOUBLEBUF flag tells SDL to use double buffering which prevents flickering when displaying moving images. Double buffering works by rendering the screen to be displayed to an offscreen image and then showing it when it is complete. The onscreen image is then swapped with the offscreen image and is then used for rendering the next screen. The swapping of the buffers is not automatic though and must be called manually. The cleanup function now has an additional function call
SDL_FreeSurface(dexterImage);
All SDL_Surfaces must be freed after they have been used. The only exception to this rule is the surface used to create the SDL window. This surface is freed when SDL_Quit is called. input.h and input.c have not changed.
graphics.h contains the defs.h include and the external declarations of our 2 surfaces. Since the header files only ever contain the includes and references to external variables, they will not be discussed anymore.
graphics.h contains the defs.h include and the external declarations of our 2 surfaces. Since the header files only ever contain the includes and references to external variables, they will not be discussed anymore.
graphics.c contains 3 function calls, loadImage, drawImage and updateScreen. The loadImage function contains numerous SDL function calls:
SDL_Surface *loadImage(char *name)
{
/* Load the image using SDL Image */

SDL_Surface *temp = IMG_Load(name);
SDL_Surface *image;

if (temp == NULL)
{
printf("Failed to load image %s\n", name);

return NULL;
}

/* Make the background transparent */

SDL_SetColorKey(temp, (SDL_SRCCOLORKEY|SDL_RLEACCEL),
SDL_MapRGB(temp->format, 0, 0, 0));

/* Convert the image to the screen's native format */

image = SDL_DisplayFormat(temp);

SDL_FreeSurface(temp);

if (image == NULL)
{
printf("Failed to convert image %s to native format\n", name);

return NULL;
}

/* Return the processed image */

return image;
}
The IMG_Load function is from SDL_image and allows us to load JPEGs and PNGs. There is no need to specify what the file type is as it will use the file's extension to figure out what it is. The function returns an SDL_Surface if successful or NULL if it fails. The next function call
SDL_SetColorKey(temp,(SDL_SRCCOLORKEY|SDL_RLEACCEL), SDL_MapRGB(temp->format, 0, 0, 0));
makes one of the colours in the image transparent. This function takes 3 arguments. The first is the image to process, the second is a flag: SDL_SRCCOLORKEY makes the 3rd argument the transparent pixel in the source image of the blit. SDL_RLEACCEL improves blitting performance, which is desirable. The third argument is the color of the pixel to make transparent, we use SDL_MapRGB for this
SDL_MapRGB(temp->format, 0, 0, 0)
SDL_MapRGB takes 4 arguments, the surface's pixel format and the RGB values of the colour desired. The function then returns the pixel value of the desired colour. Finally, we need to convert the loaded image to the current display's format. This is important because otherwise SDL will need to perform this conversion every time we attempt to blit the image to the screen, and this will consume a lot of CPU power and slow down our program. The function call SDL_DisplayFormat takes care of this:
image = SDL_DisplayFormat(temp);
This function will return the converted image. It is important that we do not reassign the pointer to the unconverted surface to the newly converted surface as we need to free the old surface afterwards to avoid a memory leak. We free the old surface by calling
SDL_FreeSurface(temp);
After this, we need to check that the surface converted successfully. If it did not then we will note the error and return NULL. The second function, drawImage is used to render an SDL_Surface to the screen. We use an SDL_Rect structure to do this. The SDL_Rect contains 4 variables, x, y, w (width) and h (height). We set the width and height of the rectangle to the width and height of the surface to be rendered and the x and y to the values passed into the function. We then call
SDL_BlitSurface(image, NULL, screen, &dest);
The first argument is the image to be blitted, the second is the clipping rectangle. If we only wanted a portion of the image, we would have used another SDL_Rect and set its x, y, w and h to the required values of the portion of the image we wanted. Since we want the entire image, we can pass in NULL and SDL will assume we want the whole image. The third argument is the target surface, most usually the screen, but it could also be used to construct another image that would later be blitted to the screen. The fourth argument is the area of the target surface to render to, as specified by the values stored in the SDL_Rect.
The final function updateScreen, is the master drawing function. The first function call
SDL_FillRect(screen, NULL, 0);
is used to blank the screen. SDL_FillRect takes 3 arguments, the target surface, an SDL_Rect containing the coordinates to fill and the colour of the fill (using SDL_MapRGB). Passing in NULL for the second argument will make SDL assume that we want to fill the entire surface. 0 in the third argument is simply black. We then call drawImage to draw the image at the specified coordinates. The final call in this function
SDL_Flip(screen);
swaps the image buffers on the screen. Since we created the window with the flag SDL_DOUBLEBUF, this is required otherwise nothing will be displayed when the program is run.

 Conclusion

Hopefully you should now have an understanding of how to display images in SDL. The next tutorial will look at playing sounds.

 Downloads

Source Code - tutorial02.tar.gz