Sunday, 27 November 2011

The Legend of Edgar 0.94

The Legend of Edgar has been updated to 0.94. This release fixes a bunch of minor issues:

  • The crash on Windows has been fixed
  • Fixed a problem with the ice cubes moving too quickly
  • Jacob no longer drops infinite swords
  • The rift summoned by Mataeus no longer plays its sound effect endlessly
  • Fixed a problem with The Sewer Dweller's attacks being unblockable
  • Ice cubes now vanish after a few seconds instead of hanging around forever
  • Dark Summoners no longer get stuck after casting lightning

Download 0.94 from SourceForge

Sunday, 2 October 2011

Intermediate Game Tutorial #4 - Tile Based Map Collision Detection

Intermediate Tutorials
Intermediate Game Tutorial #4 - Tile Based Map Collision Detection
Collision Detection
 Introduction

This tutorial deals with collision detection.
Compile and run tutorial14. The program will read the map data file and display the map on the screen. Use the arrow keys (not the ones on the numeric pad) to move the character around the screen. Pressing space will make the character jump. Should the character fall out of the map screen it will reappear after a couple of seconds.

 An indepth look

We reintroduce the Entity structure and add a few extra variables to it:
typedef struct Entity
{
    int w, h, onGround;
    int thinkTime;
    float x, y, dirX, dirY;
} Entity;
The onGround variable is used to determine if the Entity is on the ground. We use this to determine whether or not the Entity is allowed to jump or perform any other ground based action. dirX and dirY are used to apply directional force to the x and y variables. This is important when working with gravity or friction. We will see how this is used later. We will only look at three files in this tutorial since the other files and functions have been covered numerous times in previous tutorials.
map.c contains an extra function to center an Entity on the screen:
void centerEntityOnMap(Entity *e)
{
    map.startX = e->x - (SCREEN_WIDTH / 2);
    
    if (map.startX < 0)
    {
        map.startX = 0;
    }
    
    else if (map.startX + SCREEN_WIDTH >= map.maxX)
    {
        map.startX = map.maxX - SCREEN_WIDTH;
    }
    
    map.startY = e->y - (SCREEN_HEIGHT / 2);
    
    if (map.startY < 0)
    {
        map.startY = 0;
    }
    
    else if (map.startY + SCREEN_HEIGHT >= map.maxY)
    {
        map.startY = map.maxY - SCREEN_HEIGHT;
    }
}
We take the Entity's current horizontal position, subtract half the screen's width from it and assign this to startX. We then perform the usual bounds check to make sure that the startX value to make sure we don't attempt to draw non existant parts of the map. We do the same with the vertical position. We will now look at the code to handle the player, which is in player.c:
void doPlayer()
{
    if (player.thinkTime == 0)
    {
        player.dirX = 0;
    
        /* Gravity always pulls the player down */
    
        player.dirY += GRAVITY_SPEED;
        
        if (player.dirY >= MAX_FALL_SPEED)
        {
            player.dirY = MAX_FALL_SPEED;
        }
        
        if (input.left == 1)
        {
            player.dirX -= PLAYER_SPEED;
        }
        
        else if (input.right == 1)
        {
            player.dirX += PLAYER_SPEED;
        }
        
        if (input.jump == 1)
        {
            if (player.onGround == 1)
            {
                player.dirY = -11;
            }
            
            input.jump = 0;
        }
        
        checkToMap(&player);
        
        centerEntityOnMap(&player);
    }
    
    if (player.thinkTime > 0)
    {
        player.thinkTime--;
        
        if (player.thinkTime == 0)
        {
            initPlayer();
        }
    }
}
The first check we perform is that the player's thinkTime is greater than 0 and if it is then we can perform actions on it. We first set the dirX to 0. This means that the player will stop moving instantly when we release the key. If we wanted to make the player slowly stop then we could do something like the following:
player.dirX *= 0.98;
This would mean that when we released the arrow key the player would take a few frames to come to a complete halt. This could be used to give the illusion that the player is on ice where there is a low coefficient of friction. Next we apply gravity to the player's vertical movement. Not that we do not simply set the dirY to the amount of gravitational pull, but we increment it instead. This will mean that anything currently moving up will start to be pulled down after a while. We also limit the maximum speed at which the player will fall. The left and right movements should be self explanitory. When jump is detected we need to first check if the player is on the ground, otherwise they could jump any time they wanted, which is undesirable. Provided they can jump, we set the dirY to -11, which in this game is a reasonable amount to jump by. Note that this value will be decremented by gravity in the following frames. Finally, we call checkToMap and centerEntityOnMap. We will look at checkToMap shortly. If the player's thinkTime is greater than 0 then the player cannot perform any actions and we simply decrease the value. Once it hits 0 we call initPlayer to reset the player on the map. We will now look at the map collision detection. This function is stored in collisions.c:
void checkToMap(Entity *e)
{
    int i, x1, x2, y1, y2;
    
    /* Remove the user from the ground */
    
    e->onGround = 0;
    
    /* Test the horizontal movement first */
    
    i = e->h > TILE_SIZE ? TILE_SIZE : e->h;
    
    for (;;)
    {
        x1 = (e->x + e->dirX) / TILE_SIZE;
        x2 = (e->x + e->dirX + e->w - 1) / TILE_SIZE;
    
        y1 = (e->y) / TILE_SIZE;
        y2 = (e->y + i - 1) / TILE_SIZE;
        
        if (x1 >= 0 && x2 < MAX_MAP_X && y1 >= 0 && y2 < MAX_MAP_Y)
        {
            if (e->dirX > 0)
            {
                /* Trying to move right */
        
                if ((map.tile[y1][x2] != BLANK_TILE) || (map.tile[y2][x2]!=BLANK_TILE))
                {
                    /* Place the player as close to the solid tile as possible */
        
                    e->x = x2 * TILE_SIZE;
                    
                    e->x -= e->w + 1;
        
                    e->dirX = 0;
                }
            }
        
            else if (e->dirX < 0)
            {
                /* Trying to move left */
        
                if ((map.tile[y1][x1] != BLANK_TILE) || (map.tile[y2][x1]!=BLANK_TILE))
                {
                    /* Place the player as close to the solid tile as possible */
                    
                    e->x = (x1 + 1) * TILE_SIZE;
        
                    e->dirX = 0;
                }
            }
        }
        
        if (i == e->h)
        {
            break;
        }
        
        i += TILE_SIZE;
        
        if (i > e->h)
        {
            i = e->h;
        }
    }

    /* Now test the vertical movement */
    
    i = e->w > TILE_SIZE ? TILE_SIZE : e->w;
    
    for (;;)
    {
        x1 = (e->x) / TILE_SIZE;
        x2 = (e->x + i) / TILE_SIZE;
    
        y1 = (e->y + e->dirY) / TILE_SIZE;
        y2 = (e->y + e->dirY + e->h) / TILE_SIZE;
        
        if (x1 >= 0 && x2 < MAX_MAP_X && y1 >= 0 && y2 < MAX_MAP_Y)
        {
            if (e->dirY > 0)
            {
                /* Trying to move down */
                
                if ((map.tile[y2][x1] != BLANK_TILE) || (map.tile[y2][x2]!=BLANK_TILE))
                {
                    /* Place the player as close to the solid tile as possible */
                    
                    e->y = y2 * TILE_SIZE;
                    e->y -= e->h;
        
                    e->dirY = 0;
                    
                    e->onGround = 1;
                }
            }
        
            else if (e->dirY < 0)
            {
                /* Trying to move up */
        
                if ((map.tile[y1][x1] != BLANK_TILE) || (map.tile[y1][x2]!=BLANK_TILE))
                {
                    /* Place the player as close to the solid tile as possible */
        
                    e->y = (y1 + 1) * TILE_SIZE;
        
                    e->dirY = 0;
                }
            }
        }
        
        if (i == e->w)
        {
            break;
        }
        
        i += TILE_SIZE;
        
        if (i > e->w)
        {
            i = e->w;
        }
    }

    /* Now apply the movement */

    e->x += e->dirX;
    e->y += e->dirY;
    
    if (e->x < 0)
    {
        e->x = 0;
    }
    
    else if (e->x + e->w >= map.maxX)
    {
        e->x = map.maxX - e->w - 1;
    }
    
    if (e->y > map.maxY)
    {
        e->thinkTime = 60;
    }
}
This function is fairly complex, so we will look at it in sections. Firstly, we set the onGround variable to 0. During our map tests we may set this variable back to 1. The easiest way to test map collisions is to check the horizontal and vertical movements separately. We will start with the horizontal movement. Determining whether or not an Entity has collided with a map block is a case of checking the value of the map tile that each of the 4 corners of the Entity will be in after the movement has taken place. This approach will only work though if the Entity's horizontal and vertical sizes are less than or equal to the TILE_SIZE, otherwise a very large Entity will be able to move through the map tiles since its corners may not necessarily collide with a tile even though its midsection does. To get around this problem we will break the Entity's height into portions:
i = e->h > TILE_SIZE ? TILE_SIZE : e->h;
In our example, the player sprite is 55 pixels tall, i will initially be TILE_SIZE. We next enter a loop and calculate the corners that the player will be in after it has moved:
x1 = (e->x + e->dirX) / TILE_SIZE;
x2 = (e->x + e->dirX + e->w - 1) / TILE_SIZE;

y1 = (e->y) / TILE_SIZE;
y2 = (e->y + i - 1) / TILE_SIZE;
x1 is the left side, x2 is the right side, y1 is the top side and y2 is the bottom. We will combine these values together to calculate the coordinates of the corners. Next we check that the values the 4 sides are within the bounds of the tile array. If they are not then we skip over the calculation since the player has gone outside the bounds of the screen, either because they have jumped at the top of the screen or they may have fallen out of the map, and we don't want to attempt to index an array outside of its bounds. Provided the values are legal we then check if the player is moving right:
if (e->dirX > 0)
{
    /* Trying to move right */

    if ((map.tile[y1][x2] != BLANK_TILE) || (map.tile[y2][x2] != BLANK_TILE))
    {
        /* Place the player as close to the solid tile as possible */

        e->x = x2 * TILE_SIZE;
        
        e->x -= e->w + 1;

        e->dirX = 0;
    }
}
We want to check the top right and bottom right corners of the Entity so we use y1 and y2 for the top and bottom values and x2 since it is the right side of the Entity. We then check the tile type at these two corners and if either of the tiles is not empty, then we have hit a map tile. We then move the Entity as close to the tile as possible and set the dirX to 0. If we are moving left then the code is fairly similar:
else if (e->dirX < 0)
{
    /* Trying to move left */

    if ((map.tile[y1][x1] != BLANK_TILE) || (map.tile[y2][x1] != BLANK_TILE))
    {
        /* Place the player as close to the solid tile as possible */
        
        e->x = (x1 + 1) * TILE_SIZE;

        e->dirX = 0;
    }
}
We still use y1 and y2 but we use x1 since this is the left side of the Entity. Next we check if we have to test the next block of the body.
/* Exit this loop if we have tested all of the body */

if (i == e->h)
{
    break;
}

/* Test the next block */

i += TILE_SIZE;

if (i > e->h)
{
    i = e->h;
}
If the current value of i is equal to the Entity's height then we have completed testing the horizontal movement and can exit the loop. Otherwise we increment i by TILE_SIZE to test the next block. If i is greater than the Entity's height then we set it to the Entity's height since we don't want to test outside of this. We then test the vertical movement, which is similar to the horizontal checking, except that during the vertical checking, if we are moving down and we encounter a solid tile, we set the onGround variable to 1. Finally, we apply the dirX and dirY to allow the Entity to move:
/* Now apply the movement */

e->x += e->dirX;
e->y += e->dirY;

if (e->x < 0)
{
    e->x = 0;
}

else if (e->x + e->w >= map.maxX)
{
    e->x = map.maxX - e->w - 1;
}

if (e->y > map.maxY)
{
    e->thinkTime = 60;
}
Note that dirX and dirY may have been set to 0 in which case no movement will take place. We also prevent the player from being able to move off the left and right hand edges of the map. If the player's y variable is greater than the maxY of the map then the Entity has fallen out of the map, in which case we set the Entity's thinkTime to 60, which is about 1 second. As seen earlier, this will make the player reappear at the start of the map.

 Conclusion

The code for checking the map may seem very long, but in reality we are simply performing the same check 4 times, one for each corner. You can use the map editor in the previous tutorial to modify the map included in this tutorial. In the following tutorials we will look at more animation and additions to the map.

 Downloads

Source Code - tutorial14.tar.gz

Intermediate Game Tutorial #3 - A Tile Based Map Editor

Intermediate Tutorials
Intermediate Game Tutorial #3 - A Tile Based Map Editor
The Map Editor
 Introduction

In this tutorial we will create a basic map editor
Compile and run tutorial13. The program will read the map data file and display the map on the screen. Use the arrow keys (not the ones on the numeric pad) to scroll the map around. Moving the mouse cursor will move the tile block around. Clicking the left mouse button or pressing space will place a tile at the current mouse position. Clicking the right mouse button will blank the tile at the current map position. Pressing the comma or minus key (not the one on the numeric pad) will select the previous map tile. Pressing the period or plus key (not the one on the numeric pad) will select the next map tile. Pressing S will save the map data and pressing L will load the map data.

 An indepth look

We have increased the maximum number of tiles to 400 x 300 and also updated the Map structure:
typedef struct Map
{
    char *filename;
    int startX, startY;
    int maxX, maxY;
    int tile[MAX_MAP_Y][MAX_MAP_X];
    SDL_Surface *background;
} Map;
The filename variable simply stores the path to the map data so that we can load and save it. The background variable is the background image. Currently this is hardcoded but later tutorials will see the filename stored as part of the map data. We also create a structure to handle the tile cursor as follows:
typedef struct Cursor
{
    int x, y, tileID;
} Cursor;
The x and y variables are screen coordinates and the tileID is the current ID of the selected tile. We also have a message structure
typedef struct Message
{
    char text[MAX_MESSAGE_LENGTH];
    int counter;
} Message;
This structure is used to display messages on the screen. The message will be displayed on the screen as long as the counter is greater than 0. The map loading code in map.c has changed to allow dynamic setting of the maximum horizontal and vertical scrolling:
void loadMap(char *name)
{
    int x, y;
    FILE *fp;

    fp = fopen(name, "rb");

    /* If we can't open the map then exit */

    if (fp == NULL)
    {
        printf("Failed to open map %s\n", name);

        exit(1);
    }

    /* Read the data from the file into the map */
    
    map.maxX = map.maxY = 0;

    for (y=0;y<MAX_MAP_Y;y++)
    {
        for (x=0;x<MAX_MAP_X;x++)
        {
            fscanf(fp, "%d", &map.tile[y][x]);
            
            if (map.tile[y][x] != BLANK_TILE)
            {
                if (x > map.maxX)
                {
                    map.maxX = x;
                }
                
                if (y > map.maxY)
                {
                    map.maxY = y;
                }
            }
        }
    }
    
    map.maxX++;
    map.maxY++;
    
    /* Set the start coordinates */
    
    map.startX = map.startY = 0;
    
    /* Set the maximum scroll position of the map */
    
    map.maxX = MAX_MAP_X * TILE_SIZE;
    map.maxY = MAX_MAP_Y * TILE_SIZE;
    
    /* Set the filename */
    
    map.filename = name;

    /* Close the file afterwards */

    fclose(fp);
}
Before reading the map data, we set the maxX and maxY values to 0 and then, while reading the map data, we check if the current map value is not the blank tile. If this is true then we check if the current x value is greater than the current maxX value and if it is, then we set it to x. We apply the same logic to the maxY variable. Since this is a map editor however, limiting the scrolling to the bounds of the size of the map would not allow us to expand the map so, we set the size of the map to the maximum possible size. In the next tutorial we will not perform this step. Finally, we set the filename of the map. The save map code is similar in places:
void saveMap()
{
    int x, y;
    FILE *fp;

    fp = fopen(map.filename, "wb");

    /* If we can't open the map then exit */

    if (fp == NULL)
    {
        printf("Failed to open map %s\n", map.filename);

        exit(1);
    }

    /* Write the data from the file into the map */

    for (y=0;y<MAX_MAP_Y;y++)
    {
        for (x=0;x<MAX_MAP_X;x++)
        {
            fprintf(fp, "%d ", map.tile[y][x]);
        }
        
        fprintf(fp, "\n");
    }

    /* Close the file afterwards */

    fclose(fp);
}
First we open the file that the map was loaded from, then we loop through the map from processesing each row and column and writing each map value to the file. We also terminate each row with a carriage return which will improve readability if we need to open the map in a text editor. Also in this file is the loadMapTiles function:
void loadMapTiles()
{
    int i;
    char filename[40];
    FILE *fp;
    
    for (i=0;i<MAX_TILES;i++)
    {
        sprintf(filename, "gfx/map/%d.png", i);
        
        fp = fopen(filename, "rb");
        
        if (fp == NULL)
        {
            continue;
        }
        
        fclose(fp);
        
        mapImages[i] = loadImage(filename);
        
        if (mapImages[i] == NULL)
        {
            exit(1);
        }
    }
}
Since we have multiple map tiles now, the easiest way to load them all is to give each file an id number, starting with 0 for the blank tile and incrementing the number for each subsequent file. We check that the file exists for the id number we are trying to load and if it does then we load it. The freeMapTiles function is a standard loop that frees our images so we will skip over it. In input.c, we add the ability to read the mouse buttons as follows:
case SDL_MOUSEBUTTONDOWN:
    switch(event.button.button)
    {
        case SDL_BUTTON_LEFT:
            input.add = 1;
        break;
        
        case SDL_BUTTON_RIGHT:
            input.remove = 1;
        break;
        
        default:
        break;
    }
break;

case SDL_MOUSEBUTTONUP:
    switch(event.button.button)
    {
        case SDL_BUTTON_LEFT:
            input.add = 0;
        break;
        
        case SDL_BUTTON_RIGHT:
            input.remove = 0;
        break;
        
        default:
        break;
    }
break;
The code should be self explainitory as it is very similar to reading key presses. We also read in the position of the mouse cursor:
/* Get the mouse coordinates */

SDL_GetMouseState(&input.mouseX, &input.mouseY);

input.mouseX /= TILE_SIZE;
input.mouseY /= TILE_SIZE;

input.mouseX *= TILE_SIZE;
input.mouseY *= TILE_SIZE;
SDL_GetMouseState sets the x and y mouse coordinates to the passed in variables. The x and y coordinates are relative to the SDL window and also does not include the window decoration. We also want to snap the coordinates to a grid. This simply requires dividing the mouseX by the TILE_SIZE and multiplying it back up again. Since mouseX is an integer, we will not get any decimal part when we divide the value. cursor.c contains the functions for manipulating the on screen cursor:
void doCursor()
{
    cursor.x = input.mouseX;
    cursor.y = input.mouseY;
    
    if (cursor.y >= SCREEN_HEIGHT - TILE_SIZE)
    {
        cursor.y = SCREEN_HEIGHT - TILE_SIZE * 2;
    }

    if (input.left == 1)
    {
        map.startX -= TILE_SIZE;

        if (map.startX < 0)
        {
            map.startX = 0;
        }
    }
    
    else if (input.right == 1)
    {
        map.startX += TILE_SIZE;

        if (map.startX + SCREEN_WIDTH >= map.maxX)
        {
            map.startX = map.maxX - SCREEN_WIDTH;
        }
    }
    
    if (input.up == 1)
    {
        map.startY -= TILE_SIZE;

        if (map.startY < 0)
        {
            map.startY = 0;
        }
    }
    
    else if (input.down == 1)
    {
        map.startY += TILE_SIZE;
        
        if (map.startY + SCREEN_HEIGHT >= map.maxY)
        {
            map.startY = map.maxY - SCREEN_HEIGHT;
        }
    }
    
    if (input.add == 1)
    {
        map.tile[(map.startY + cursor.y) / TILE_SIZE]
        [(map.startX + cursor.x) / TILE_SIZE] = cursor.tileID;
    }
    
    else if (input.remove == 1)
    {
        map.tile[(map.startY + cursor.y) / TILE_SIZE]
        [(map.startX + cursor.x) / TILE_SIZE] = BLANK_TILE;
    }
    
    if (input.previous == 1)
    {
        do
        {
            cursor.tileID--;
            
            if (cursor.tileID < 0)
            {
                cursor.tileID = MAX_TILES - 1;
            }
        }
        
        while (mapImages[cursor.tileID] == NULL);
        
        input.previous = 0;
    }
    
    if (input.next == 1)
    {
        do
        {
            cursor.tileID++;
            
            if (cursor.tileID >= MAX_TILES)
            {
                cursor.tileID = 0;
            }
        }
        
        while (mapImages[cursor.tileID] == NULL);
        
        input.next = 0;
    }
    
    if (input.save == 1)
    {
        saveMap();
        
        setStatusMessage("Saved OK");
        
        input.save = 0;
    }
    
    if (input.load == 1)
    {
        loadMap(map.filename);
        
        setStatusMessage("Loaded OK");
        
        input.load = 0;
    }
    
    if (input.left == 1 || input.right == 1 || input.up == 1 || input.down == 1)
    {
        SDL_Delay(30);
    }
}
First, we prevent the cursor from being able to move into the bottom row of the screen, since we will use that to display messages. We then handle the left, right, up and down arrow key presses as per the previous tutorial. When the add input is 1, we place a tile at the current screen and mouse cursor position. We do this by taking the startX value and adding the cursor.x value and then dividing this value by TILE_SIZE to give us the nearest tile. We do the same for the vertical coordinate. We then set this tile value to the tileID of the cursor. The remove input does the same thing, except that it always sets the tile's value to BLANK_TILE. When the next input is set, we increment the cursor's tileID. If the image referenced by the tileID is NULL, we move to the next tile and continue to do so until we encounter a tile that is not NULL. We also wrap the tileID value around if it is greater than or equal to MAX_TILES. The previous input behaves in the same way, except that we decrement the tileID instead. If the save input is 1 then we call saveMap followed by setStatusMessage. We will look at this function later. We preform a similar process when load is set to 1, except we call loadMap. Finally, if any of our navigation inputs are true, then we call SDL_Delay to prevent the map from scrolling too fast. Also in this file is the function to draw the cursor. This draws the image referenced by the tileID. The status panel code in status.c contains 3 functions:
void doStatusPanel()
{
    message.counter--;
    
    if (message.counter <= 0)
    {
        message.counter = 0;
    }
}
doStatusPanel decrements the message's counter. This is very similar to the thinkTime variable seen in earlier tutorials.
void drawStatusPanel()
{
    SDL_Rect dest;

    dest.x = 0;
    dest.y = SCREEN_HEIGHT - TILE_SIZE;
    dest.w = SCREEN_WIDTH;
    dest.h = TILE_SIZE;
    
    SDL_FillRect(screen, &dest, 0);
    
    if (message.counter > 0)
    {
        drawString(message.text, 0, SCREEN_HEIGHT - TILE_SIZE, font, 1, 0);
    }
}
The drawStatusPanel function uses a lot of function calls seen in previous tutorials. Firstly we fill the bottom row of the map in black and then, if the message counter is greater than 0, we print the text stored in the message structure. Finally,
void setStatusMessage(char *text)
{
    strncpy(message.text, text, MAX_MESSAGE_LENGTH);
    
    message.counter = 120;
}
setStatusMessage sets a new message and resets the counter to 120, which is approximately 2 seconds. The remaining files and functions have been covered in the basic tutorials so we will not look at them.

 Conclusion
The editor is incredibly basic, but in future tutorials we will improve it to meet the needs of the game. In the next tutorial we will implement collision detection to allow an entity to move around the map.

 Downloads

Source Code - tutorial13.tar.gz

Intermediate Game Tutorial #2 - Scrolling a Tile Based Map

Intermediate Tutorials
Intermediate Game Tutorial #2 - Scrolling a Tile Based Map
Scrolling a tile based map
 Introduction

This tutorial demonstrates how to scroll around a tile based map.
Compile and run tutorial12. The program will read the map data file and display the map on the screen. Use the arrow keys (not the ones on the numeric pad) to scroll the map around. Closing the window or pressing Escape will exit the program.

 An indepth look

In the previous tutorial, the map size was restricted to 20 x 15 tiles. We have updated the Map structure to allow scrolling and also allow dynamic map sizes. Below is the change we have made to structs.h:
typedef struct Map
{
    int startX, startY;
    int maxX, maxY;
    int tile[MAX_MAP_Y][MAX_MAP_X];
} Map;
There are 4 new variables in the structure now. The startX and startY variables define the starting horizontal and vertical positions when drawing our map. The mapX and mapY variables are used to limit the amount of scrolling that the map can do. This stops unused areas of the map from being displayed on the screen. The map loading code in map.c has changed to make use of these new variables:
void loadMap(char *name)
{
    int x, y;
    FILE *fp;

    fp = fopen(name, "rb");

    /* If we can't open the map then exit */

    if (fp == NULL)
    {
        printf("Failed to open map %s\n", name);

        exit(1);
    }

    /* Read the data from the file into the map */

    for (y=0;y<MAX_MAP_Y;y++)
    {
        for (x=0;x<MAX_MAP_X;x++)
        {
            fscanf(fp, "%d", &map.tile[y][x]);
        }
    }
    
    /* Set the start coordinates */
    
    map.startX = map.startY = 0;
    
    /* Set the maximum scroll position of the map */
    
    map.maxX = MAX_MAP_X * TILE_SIZE;
    map.maxY = MAX_MAP_Y * TILE_SIZE;

    /* Close the file afterwards */

    fclose(fp);
}
We set the startX and startY values of the map to 0. This will start drawing the map from the top left corner. We also set the maxX and maxY values to the maximum allowed size of the map. Later tutorials will look at setting this value more dynamically. We have also added another function to this file:
void doMap()
{
    if (input.left == 1)
    {
        map.startX -= SCROLL_SPEED;
        
        if (map.startX < 0)
        {
            map.startX = 0;
        }
    }
    
    else if (input.right == 1)
    {
        map.startX += SCROLL_SPEED;
        
        if (map.startX + SCREEN_WIDTH >= map.maxX)
        {
            map.startX = map.maxX - SCREEN_WIDTH;
        }
    }
    
    if (input.up == 1)
    {
        map.startY -= SCROLL_SPEED;
        
        if (map.startY < 0)
        {
            map.startY = 0;
        }
    }
    
    else if (input.down == 1)
    {
        map.startY += SCROLL_SPEED;
        
        if (map.startY + SCREEN_HEIGHT >= map.maxY)
        {
            map.startY = map.maxY - SCREEN_HEIGHT;
        }
    }
}
doMap processes the input passed to it, much in the same way that we processed player movement in previous tutorials. As always, we do not allow the startX and startY values to be less than 0. The maximum values are slightly more complicated though. We must not allow the screen to scroll past the maxX value, but we must also take into account the screen's width when checking this. So, if the startX plus the screen's width is greater than or equal to the maxX, we set the startX to maxX and subtract the screen's width from it. We do the same for the vertical movement too. The final function is a revised version of the map drawing function:
void drawMap()
{
    int x, y, mapX, x1, x2, mapY, y1, y2;

    mapX = map.startX / TILE_SIZE;
    x1 = (map.startX % TILE_SIZE) * -1;
    x2 = x1 + SCREEN_WIDTH + (x1 == 0 ? 0 : TILE_SIZE);
    
    mapY = map.startY / TILE_SIZE;
    y1 = (map.startY % TILE_SIZE) * -1;
    y2 = y1 + SCREEN_HEIGHT + (y1 == 0 ? 0 : TILE_SIZE);
    
    /* Draw the background */
    
    drawImage(backgroundImage, 0, 0);

    /* Draw the map starting at the startX and startY */
    
    for (y=y1;y<y2;y+=TILE_SIZE)
    {
        mapX = map.startX / TILE_SIZE;
        
        for (x=x1;x<x2;x+=TILE_SIZE)
        {
            if (map.tile[mapY][mapX] != 0)
            {
                drawImage(brickImage, x, y);
            }
            
            mapX++;
        }
        
        mapY++;
    }
}
When drawing a scrolled map, we must be mindful to draw tiles that are only partially on the screen. If we don't do this then tiles will pop in and out of existance when scrolling the map. We first get the starting horizontal index by dividing the startX by the TILE_SIZE. This will give us the tile to start with, including any tiles that are to be partially drawn. Next, we set the starting x coordinate, x1, to the remainder of the startX divided by the TILE_SIZE. This will require slightly more explaination:
mapX = map.startX / TILE_SIZE;
Suppose the startX is 24. This will make the mapX 0 (since it is an int and therefore we get no decimal part). This means that we start at index 0 for the horizontal drawing. However, we also need to know where on the screen to start drawing the tiles.
x1 = (map.startX % TILE_SIZE) * -1;
The remainder will be 24, but we do not want to start drawing at 24 pixels. Since we have moved 24 pixels to the right, tile 0 must be 24 pixels off the left hand side of the screen, so we will set the value to negative to achieve this. Finally, we need to know where to stop drawing:
x2 = x1 + SCREEN_WIDTH + (x1 == 0 ? 0 : TILE_SIZE);
We take the starting coordinate and add on the screen width. We also have to bear in mind though that we may have started drawing off screen. If we did then we need to draw an extra tile otherwise we will have a few blank pixels at the end of the screen. The easiest way to check this is to test if our starting value is 0 and, if it was not, then we add on an extra tile at the end. We apply the same logic to the vertical drawing. Finally we, draw the blocks to the screen:
for (y=y1;y<y2;y+=TILE_SIZE)
{
    mapX = map.startX / TILE_SIZE;
    
    for (x=x1;x<x2;x+=TILE_SIZE)
    {
        if (map.tile[mapY][mapX] != 0)
        {
            drawImage(brickImage, x, y);
        }
        
        mapX++;
    }
    
    mapY++;
}
The loop is simple enough, we start at y1 and loop through to y2. mapY and mapX are our tile indexes. At the start of the outer loop, we reset mapX because we will increment it in our inner loop. In our inner loop, we test the value of the tile at the current index and, if it is not 0, we draw the tile to the coordinates in our loop. Notice that we only want to draw what's going to be displayed on the screen. Drawing the entire map every single frame, particularly if the map is very big will waste a lot of CPU and slow down a game. The remaining files and functions have been covered in the basic tutorials so we will not look at them.

 Conclusion

Implementing scrolling does require a small amount of care to ensure that it is done correctly. The map is in the same format as before so you can again edit the file to change the map layout. In the next tutorial we will create a map editor to assist with the creation of maps.

 Downloads

Source Code - tutorial12.tar.gz

Intermediate Game Tutorial #1 - Displaying a Tile Based Map

Intermediate Tutorials
Intermediate Game Tutorial #1 - Displaying a Tile Based Map

A tile based map
 Introduction

We will now look at platform games and the use of tiles to display maps.
Compile and run tutorial11. The program will read the map data file and display the map on the screen. Closing the window or pressing Escape will exit the program.

 An indepth look

When creating a tile based game, we break the screen up into a grid. In this tutorial we create a tile size of 32x32 pixels giving us 20 tiles horizontally and 15 tiles vertically. We will look at structs.h to begin with:
typedef struct Map
{
    int tile[MAX_MAP_Y][MAX_MAP_X];
} Map;
We define our Map structure which contains an multidimensional array of ints. The maximum size of this array is defined by MAX_MAP_Y and MAX_MAP_X which are defined in defs.h. We have made a minor change to init.c. The following command
SDL_ShowCursor(SDL_DISABLE);
will hide the mouse cursor when it is moved inside the SDL window. In main.c, we load up the brick image, a background image and read the map data as follows:
/* Load the brick image */

brickImage = loadImage("gfx/brick.png");

/* If we get back a NULL image, just exit */

if (brickImage == NULL)
{
    exit(1);
}

/* Load the background image */

backgroundImage = loadImage("gfx/background.png");

/* If we get back a NULL image, just exit */

if (backgroundImage == NULL)
{
    exit(1);
}

loadMap("data/maps/map01.dat");
The loadImage function is our standard image loading function. The loadMap function takes the filename of the map file that we wish to load. We will look at this shortly. Once we have successfully loaded all of our resources we enter the standard main loop and wait for user input. We will now at the map loading functions. map.c contains two functions to deal with loading the map and rendering it to the screen.
void loadMap(char *name)
{
    int x, y;
    FILE *fp;

    fp = fopen(name, "rb");

    /* If we can't open the map then exit */

    if (fp == NULL)
    {
        printf("Failed to open map %s\n", name);

        exit(1);
    }

    /* Read the data from the file into the map */

    for (y=0;y<MAX_MAP_Y;y++)
    {
        for (x=0;x<MAX_MAP_X;x++)
        {
            fscanf(fp, "%d", &map.tile[y][x]);
        }
    }

    /* Close the file afterwards */

    fclose(fp);
}
loadMap takes the filename of the map we wish to load. The data file is a very simple text file containing 0s and 1s to describe the map layout. We will take a brief look at this file:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 1 1 0 1 1 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1
A 0 denotes a blank tile and 1 denotes a brick tile. Note that we must include either a space or carriage return between the digits so that scanf treats them as individual numbers and not one large number. Once we have successfully opened the file, we read in the map data one row at a time, one column at time. Once we are done we close the file. This gives us a multidimensional array containing our map data. The second function in the file deals with drawing the map:
void drawMap()
{
    int x, y;
    
    /* Draw the background */
    
    drawImage(backgroundImage, 0, 0);

    /* Draw the map */

    for (y=0;y<MAX_MAP_Y;y++)
    {
        for (x=0;x<MAX_MAP_X;x++)
        {
            if (map.tile[y][x] != 0)
            {
                drawImage(brickImage, x * TILE_SIZE, y * TILE_SIZE);
            }
        }
    }
}
First, we draw the background, just so the screen has some more colour to it. We then loop through the map from top to bottom, from left to right and if the tile at the array index is not 0, then we will draw a brick. Note that we multiply the x and y values up by TILE_SIZE. This is because we only have 20 tiles horizontally and 15 vertically as defined by MAX_MAP_X and MAX_MAP_Y, so we need to scale up the position of the tile according to TILE_SIZE. The remaining files and functions have been covered in the basic tutorials so we will not look at them.

 Conclusion

Displaying a tile based map on the screen does not require a lot of code. Since the map data file is just a text file, it would be worth editing the file and changing some of the 0s and 1s to produce a different map. Ensure though that you have 20 digits on each row and 15 rows in the file to ensure that the file loads up correctly.

 Downloads

Source Code - tutorial11.tar.gz

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