02:06
03 Sep 2010

News

Latest Updates
Recent Comments
Most Popular Downloads

Projects

Blob Wars : Blob And Conquer
Blob Wars : Metal Blob Solid
JGameLaunch
The Legend of Edgar
Project: Starfighter
Random Name Generator
Random Shooter
TANX Squadron
Virus Killer
XMAME GUI

Older Files

List of Older Downloads

Medals

Main & Signup
Recently Awarded
Player List

Games
Blob Wars : Metal Blob Solid
Legend of Edgar
Virus Killer

Online Manuals

Blob Wars : Blob And Conquer
Blob Wars : Metal Blob Solid
Project: Starfighter
The Legend of Edgar
Virus Killer

LBP Beta Code!

Beta Code Giveaway!

Game Tutorials

Overview and Comments

Basic Series
1.01 - Opening a Window
1.02 - Graphics
1.03 - Sound
1.04 - Input and Movement
1.05 - Simple Shooter Pt. 1
1.06 - Simple Shooter Pt. 2
1.07 - Simple Shooter Pt. 3
1.08 - Sprites and Animation
1.09 - Starfields
1.10 - A Basic Game

Intermediate Series
2.01 - Displaying a Tile Based Map
2.02 - Scrolling a Tile Based Map
2.03 - A Tile Based Map Editor
2.04 - Tile Based Map Collision Detection
2.05 - Advanced Animation

Articles

Making of TANX Squadron
Making of Starfighter
Making of Metal Blob Solid
Making of Blob And Conquer

Blob Wars Review #1
Blob Wars Review #2
Blob Wars Review #3

Blob And Conquer Review #1

Interview with Stephen J Sweeney

Gallery

3D Renders

Help and FAQs

Installation and Licensing Help

About

Contact Information

THE HONOUR OF THE KNIGHTS
The new Creative Commons Licensed
novel from Stephen J Sweeney

Visit the official website,
www.battleforthesolarsystem.com
to start reading online!

Buy Now! from Amazon.co.uk

Buy Now! from Book Depository

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();

/* 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;
    
    /* The chance of a rock being added depends upon the player's score */
    
    score /= 6000;
    score = 20 - score;
    
    if (score <= 0)
    {
        score = 1;
    }
    
    score = rand() % score;
    
    if (score != 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 every 10 seconds by increasing the odds at which a rock will appear. Since the player's score is increasing by 10 points every frame and there are roughly 60 frames in every second, the player's score will have increased by 600 points every second and 6,000 points every 10 seconds. So we take the score and divide it by 6,000. We then take this value and divide it by 20. There is no real reason for picking 20 but the lower this number is, the higher the chance of a rock appearing and the shorter the game will be. If after subtracting the score from 20, we end up with a value that is 0 or less, we set it to 1. Finally, we get a random value between 0 and score. If the value we get back is not 0 then we will exit this 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

 Comments

Name
Homepage
Email
Comments
-- Homepage must start with http://
-- Javascript and Cookies must be enabled to use this form
 

1,991,113 pages served

Copyright © 1996 - 2010 Parallel Realities :: About :: License