domingo, 29 de junio de 2014

Kill! Kill! Kill!

At this point, the only thing missing for the game to make any sense is to add our player the ability to attack and kill the enemies. For achieving this we're going to move some members from our SCharacter struct to a new struct that we can call SMovingObject, which will be used then by SCharacter and a new struct called SShot:

// Represents different types of elements that can be positioned in the game world
struct SMovingObject 
{
    int         x, z;             // Coordinates in tile map
    SVector2    PositionDeltas;   // The position inside the tile
    float       Direction;        // rotation angle in radians
    float       Speed;            // cells/second
};

// Inherits
members from SMovingObject for storing position and physics information
struct SCharacter : public SMovingObject
{   // Inheritance prevents duplicating code and facilitate higher-level constructs
    SCharacterPoints MaxPoints; // the maximum game points that can be set to a particular character
    SCharacterPoints CurrentPoints; // the current value of the character points, updated on game events
};

// Also inherits members from SMovingObject for storing position and physics information
struct SShot : public SMovingObject
{
    int Damage; // Value that will be substracted from the character's health points
};


This makes all members declared in SMovingObject to be part of SCharacter and SShot:

SShot myShot;
myShot.Speed = 10;
// Speed is originally from SMovingObject

SCharacter myChar;
myChar.Direction = 0.0f;
// the same as Direction


We can now add a new member into our game struct in order to store the shot list:

struct SGame
{
    SMap Map;
    SCharacter Player;
    std::vector<SCharacter> Enemies;
    std::vector<SShot> Shots; // Declare the shot list
};


We're going to need some code to setup a new shot and release it into the game world:

void shoot( SGame* gameObject, SPoint2 origin, float direction )
{
    SShot newShot;
    newShot.Damage           = 10; // 10 HP
    newShot.Speed            = 10.0f; // 10 tiles per second
    newShot.PositionDeltas   = origin; // the position where the shot was generated
    newShot.Direction        = direction; // the direction of the shot trajectory
    newShot.x = newShot.z = 0; // reset this so it's updated with refreshPosFromDeltas()
    refreshPosFromDeltas( &newShot ); // update tile coordinates
    gameObject->Shots.push_back( newShot ); // copy as new element at the end of the shot list
}


The update() step will also require a new function for updating position of the world shots, handle hits to enemies and remove shots that get outside the map. In order to remove the shots from the list, we're going to use the std::vector::iterator which is like a cursor of the vector. I would never use std::vector but it's good for beginners. The code for removing a shot from the list would look something like the one below:

    std::vector<SShot>::iterator iShot = gameObject->Shots.begin(); // get iterator at the begin of the list
    int indexShot = 0; // we still need to keep track of the index
    while( iShot != gameObject->Shots.end() ) // while the iterator didn't reach the end
    {
        SShot* currentShot = &gameObject->Shots[indexShot];


       
// remove shot if reached the end of the screen

        if( currentShot->x < 0 || currentShot->z < 0
            || currentShot->x >= gameObject->Map.Width || currentShot->z >= gameObject->Map.Depth
        )
        {
            iShot = gameObject->Shots.erase( iShot ); // get valid iterator
            continue; // keep at the current index
        }

        iShot++; // move iterator to the next position
        indexShot++; // next position means next index

    }

We can also use similar code to remove the shot if it hit an enemy:

        if( gameObject->Map.EnemyCells[currentShot->z][currentShot->x] != INVALID_ENEMY )
        { // damage enemy and remove shot
            gameObject->Enemy[gameObject->Map.EnemyCells[currentShot->z][currentShot->x]].CurrentPoints.HP -= iShot->Damage;
            gameObject->Player.CurrentPoints.XP += iShot->Damage;
            iShot = gameObject->Shots.erase( iShot );
            continue; // keep at the current position/index
        }


or to remove the enemy when it reached 0 health:

        if( currentEnemy->CurrentPoints.HP <= 0 )
        { // remove enemy if zero health
            gameObject->Player.CurrentPoints.XP += iEnemy->MaxPoints.HP;
            iEnemy = gameObject->Enemy.erase( iEnemy ); 
            continue; // keep at the current enemy index
        }


Now we're only missing to update the shot position from the direction and speed and time. For this we define a basis vector (x=1, y=0) and rotate it by the angle to get the final direction:

        // Calculate direction vector
        SVector2 dirVector = SVector2(1, 0).Rotate( currentShot->Direction );


        // integrate speed*time*direction
        shotDeltas->x += currentShot->Speed*fLastFrameTime*dirVector.x;
        shotDeltas->y += currentShot->Speed*fLastFrameTime*dirVector.y; 


        // refresh cell coordinates now that we have accumulated the distance
        refreshPosFromDeltas( currentShot );


I also decided we can change the display if the player win or lost and I took as reference the player health and the amount of enemies to not overcomplicate things, but the right way of doing it is to have a variable to keeps track of the game states, so you must NOT do things like this:

// Use this function to draw our game data
void draw( const SGame* gameObject )

{
    printf("- draw() called.\n");

    if( gameObject->Player.CurrentPoints.HP <= 0 )
        drawGameOver(false); // lose
    else if( gameObject->Enemy.size() == 0 )
        drawGameOver(true); // win
    else
    {
        drawASCIIMap( gameObject );
    }
    drawASCIIGameInfo( gameObject );
};


You can find the complete working code for this tutorial and other tutorials in the repository at google code. I hope you liked the game we just wrote -a program which can be written in one or two days if you're experienced in programming-. In the following tutorial series I will be explaining how to create a proper Windows window and add some nice 2D sprite animations to this game.

sábado, 28 de junio de 2014

Improving the direction system.

Until now, our SVector2D structure had only the ability to hold two values for representing the point or vector we needed to use. This was enough back then but it makes very complicated to perform operations between vectors because we had to manually update those values on the game functions.

Fortunately, C++ allows something called operator overloading and it allows us to define operators that makes easier to work with our mathematical structures. This enables us to work with vectors more comfortably by defining the operations as part of the SVector2 struct, so we can do something like:

SVector2 vA = {1, 2}, vB = {3, 4}, vC; // define two vector variables and assign some values
vC = vA+vB; // add vector A to vector B and store the result in vector C


We can do this by adding the following definition as part of our SVector2 class:

struct SVector2

    // add another vector to the current vector and return the result
    SVector2 operator + ( const SVector2& other ) const
    {   // The const modifier ensures no member variables of the structure are modified
        SVector2 result;
        result.x = x + other.x;
        result.y = y + other.y;
        return result;
    }
};


The method above will declare a new vector where to store the result, and take the x and y values from the left vector and add them to the x and y values of the vector placed at the right of the operator +. The vector instance storing the resulting values will then be returned as result of the operation.

We can also add methods to our SVector2 structure in a similar manner:

struct SVector2

    // Multiply vector elements by a scalar value
    void    Scale( float _scalar )
    {   // This method can't be const because it requires modifying member variables
        x *= _scalar;
        y *= _scalar;
    };     

};

This allows us to modify the values of our member variables without having the arithmetic operations inside our game functions. In this case the previous code enables scaling the vector by a simple call to the Scale() member function:

SVector2 vA = {1, 2}; // define a vector
vA.Scale( 2.0f ); // multiply vector elements by 2

C++ also enables to define constructors, which are methods that are called automatically when creating a new instance from a structure. This enables to make the code simpler by allowing us to initialize the member values from parameters, much like this:

SVector2 vA(1, 2); // initialize vA with constructor
SVector2 vC = vA + SVector2(3, 4); // initialize SVector2 with constructor

Constructors are named as the structure they construct:

typedef struct SVector2
{
    // --- member variables
    float x, y;

    // constructors
    SVector2(){ /* do nothing */ };
    SVector2( float _x, float _y ){ x = _x; y = _y; };

};

I won't dive into vector math right now because it would be too long and there are plenty of information on vector math for games out there. You can find the other methods added by looking at our point.h file, but now we are going to assume they're already there and start using them for something more interesting.

We're now going to improve our character direction system by taking advantage of our new vector methods and operators, because currently the direction is calculated only for updating the position but then it gets lost, and this is a problem because we will want to add other functions related to the player's direction (such as shooting)  and we need the information to remain available for other update functions. In order to achieve this, we're going to define a new member variable at the SCharacter struct called Direction, which will hold the rotation angle of the character in relation to a reference vector, which we're going to define as (x=1, y=0).

struct SCharacter
{
    float Direction; // rotation angle in radians
    /* ... more members follow ... */


We can then update our player direction code in order to calculate this value from the keys pressed:

void updatePlayer( SGame* gameObject, float fLastFrameTime  )
{

    // Query key states
    bool _keyUP     = GetAsyncKeyState('W') ? true : false,
        _keyDOWN    = GetAsyncKeyState('S') ? true : false,
        _keyLEFT    = GetAsyncKeyState('A') ? true : false,
        _keyRIGHT   = GetAsyncKeyState('D') ? true : false;

    SVector2 dir = {0,0};
    // Determine final direction from keys pressed
    if(_keyRIGHT)
        dir.x ++; //dir.x = 1;
    if(_keyLEFT)
        dir.x --; //dir.x = -1;
    if(_keyUP)
        dir.y --; //dir.y = -1;
    if(_keyDOWN)
        dir.y ++; //dir.y = 1;


    // normalize the new direction vector (make it a unit length so we can multiply it by speed to get velocity vector)
    dir.Normalize();


    // we want to walk only if a direction key was pressed
    bool dirKeyPressed = _keyRIGHT || _keyLEFT || _keyUP || _keyDOWN;
    if( dirKeyPressed )
    {
        gameObject->Player.Direction = SVector2(1, 0).AngleWith(dir); // get angle in radians
        if( dir.y < 0 ) // negate rotation if more than 180º
            gameObject->Player.Direction = gameObject->Player.Direction*-1;

        // Increase 50% speed if left shift pressed.
        float fSpeed = GetAsyncKeyState(VK_LSHIFT) ? gameObject->Player.Speed*1.5f : gameObject->Player.Speed;

        // scale direction depending on speed and time to get total displacement
        dir.Scale(fSpeed*fLastFrameTime);

        // integrate our calculated displacement
        gameObject->Player.PositionDeltas = gameObject->Player.PositionDeltas + dir;
    }

    /* .. more update code ... */

This will give us a better direction system that will allow us to determine the direction for projectiles shot by the player and enable a more precise rotation control for game characters.We can also translate this value to other rotation or direction representations by taking advantage of our new SVector2 methods:

    // Calculate direction vector from angle in radians.
    SVector2 dirVector = SVector2(1, 0).Rotate(gameObject->Player.Direction);
    // Calculate rotation degrees from angle in radians.
    float degrees = gameObject->Player.Direction/(float)GAME_2PI*360;


We can display these values at our draw() step in order to check they're working properly:

    printf( "- Player direction: (%f, %f)\n"
            "- Player angle: %f radians or %f degrees\n"
            dirVector.x, dirVector.y, gameObject->Player.Direction, degrees );


The complete code for this tutorial can be downloaded from the repository. In the next tutorial we're going to see how to use this information to add a shooting system to the player character, which will transform our current program into a playable game. See you there!



lunes, 23 de junio de 2014

Improving character movement.

In this tutorial we're going to add a smoother movement with a configurable speed for the characters of our previous program. For this we're going to be using a new data type which is called float and allows storing real numbers (or an approximation to them):

float mySpeed = 4.5678f; // units per second

By taking advantage of the frame time information received at the update() function, we can move our character the fraction corresponding to that time instead of moving it an entire cell.

if( GetKeyState(VK_UP) )
   
player->y -= player->Speed*fLastFrameTime; // decrease by speed*time as we learned in high school


The problem with our previous implementation is that our x and y coordinates are declared as integer types for using as array indices, which causes all the information after the decimal point to get lost:

float mySpeed = 4.5678f; // units per second
int myIntegerSpeed = mySpeed; // myIntegerSpeed will store only the integer value 4 and the decimal information such as 0.5678 will be lost.

So in order to circumvent this we will add a new structure for holding continuous space coordinates like this one:

struct SPoint2D
{
    float x, y;
};


typedef SPoint2D SVector2D; // Gives our structure another name (SVector2D)

We can now use this structure inside our character structure to store the distance between the cell origin (x=0 , y=0) to the player position, which will always be less than 1 because if we reached 1 of one cell it means that we are in the 0 of the next cell (because 1 is the size of a cell). The structure will then look similar to this one:

struct SCharacter // holds character data
{
    int x, z; // Coordinates in tile map
    SVector2D PositionDeltas; // location inside the (x, z) cell, being (0.5, 0.5) the center of the cell
    float Speed; // cells per second
 
    /* other character variables follow... */
};

We can then configure some speed for the enemies during the setup() step: 

// use this function to initialize enemies 
void setupEnemies( SGame* gameObject )
{
#define INITIAL_ENEMY_COUNT 5
    for( int iEnemy=0; iEnemy < INITIAL_ENEMY_COUNT; iEnemy++ )
    {
        SCharacter newEnemy;
        newEnemy.Speed = float(iEnemy);
// generate the speed from the enemy index
        newEnemy.PositionDeltas = {0,0}; // reset this to a valid value between 0 and 1
        /* other setup code... */


And also to our player character:

// Use this function to setup player at level startup.
void setupPlayer( SGame* gameObject )
{
    gameObject->Player.PositionDeltas = {0,0};
    gameObject->Player.Speed    = 5.0f;
    /* other player initialization code... */


Now that we have our data structures set up and the values initialized, we can start using them during the update() step to calculate the movement that happened in the time step of the frame:

void updatePlayer( SGame* gameObject, float fLastFrameTime  )
{
    SVector2D* playerDelta = &gameObject->Player.PositionDeltas; // get memory address of our data (pointer to data)

    float fSpeed = gameObject->Player.Speed; // get player speed
    // Update position from speed*time

    if(GetAsyncKeyState(VK_UP))
        playerDelta->y -= fSpeed*fLastFrameTime; // decrease by speed*time
    if(GetAsyncKeyState(VK_DOWN))
        playerDelta->y += fSpeed*fLastFrameTime; // increase by speed*time
    if(GetAsyncKeyState(VK_RIGHT))
        playerDelta->x += fSpeed*fLastFrameTime; // increase by speed*time
    if(GetAsyncKeyState(VK_LEFT))
        playerDelta->x -= fSpeed*fLastFrameTime; // decrease by speed*time

    // refresh tile coordinates after accumulating the distance walked
    refreshPosFromDeltas( &gameObject->Player );

}

We define an auxiliary function that will update the x, z coordinates of the tile for each unit accumulated in our deltas after calculating the displacement from the time. Then if we have x = 10 and deltas.x = 2.5f after accumulating the displacement, we shall increase x and decrease deltas.x by one until x = 12 and deltas.x = 0.5:

void refreshPosFromDeltas( SCharacter* character )
{
    SVector2D* charDeltas = &character->PositionDeltas; // get pointer to deltas
   
    // Update X coordinate until deltas.x  < 1
    while( charDeltas->x >= 1.0f )
    {
        character->x += 1.0f;

        charDeltas->x -= 1.0f;
    }
    // Update X coordinate until deltas.x  >= 0
    while( charDeltas->x < 0.0f )
    {
        character->x -= 1;
        charDeltas->x += 1.0f;
    }
    /* Do the same for the Y coordinate */

}

We also need to implement these changes in the code for the enemy updates:

    for( unsigned int iEnemy=0; iEnemy < gameObject->Enemy.size(); iEnemy++ )
    {
        SCharacter* currentEnemy = &gameObject->Enemy[iEnemy]; // get pointer to character
        SVector2D* enemyDelta = &currentEnemy->PositionDeltas; // get pointer to deltas to be updated
        float fEnemySpeed = currentEnemy->Speed; // get enemy speed
       
        if( gameObject->Player.x < currentEnemy->x )
            enemyDelta->x -= fEnemySpeed*fLastFrameTime; // decrease speed*time
        else if( gameObject->Player.x > currentEnemy->x )
            enemyDelta->x += fEnemySpeed*fLastFrameTime; // increase speed*time
   
        if( gameObject->Player.z < currentEnemy->z )
            enemyDelta->y -= fEnemySpeed*fLastFrameTime; // decrease speed*time
        else if( gameObject->Player.z > currentEnemy->z )
            enemyDelta->y += fEnemySpeed*fLastFrameTime; // increase speed*time

        // refresh cell coordinates now that we have accumulated the distance walked
        refreshPosFromDeltas( currentEnemy );
        /* more enemy update code follows */
 


This will be enough for our game characters to move smoothly depending on the actual time elapsed between frames instead of moving a whole cell on each update, giving more realism to the movement of the characters and making the game more enjoyable.

The complete code and binaries for this tutorial can be found in the repository at google code. In the next tutorial we will be adding a persistent direction system and our vector struct in order to set a base to add bullets and a improve the character controls and enemy AI. See you there!

sábado, 21 de junio de 2014

Organizing the source code into different files.

As our program gets larger it's convenient to organize the source code into different files. In this tutorial we're going to talk about how to use the header files which are intended to share pieces of program to other files.

For creating a new header file go to menu Project->Add->New item (or right click on the project in the solution explorer and select the same option). A window will pop up, then select Header file in the window and set the name to game.h:



We're going to place in this file some code that will be used from main.cpp, so in main.cpp we need to include it:

#include "game.h" // use "" for files found in the project folder, contrary to <stdio.h> or <vector>

Now we can place some of the code into the file. We're going to move all the #define lines, the #include for <vector> and the structure declarations to game.h. We're also going to add the following lines at the end of game.h:

// -- these game functions need to be included at main.cpp in order to use them
void setupMap( SGame* gameObject );     // initialize map layers
void setupPlayer( SGame* gameObject );  // initialize player
void setupEnemies( SGame* gameObject ); // initialize enemy list

void updateMap( SGame* gameObject, float fTimeElapsed );     // update tiles
void updatePlayer( SGame* gameObject, float fTimeElapsed );  // update player
void updateEnemies( SGame* gameObject, float fTimeElapsed ); // update enemy AI and refresh enemy layer

void drawASCIIMap( const SGame* gameObject ); // take the map data and print it on the console
void drawASCIIGameInfo( const SGame* gameObject ); // print the player and misc info


We're also going to be adding files that will contain a timer object that we will be using for querying the time elapsed between game frames. We're going to need this in the next tutorials to improve animation, AI and more. These files will be called timer.h and timer.cpp and will define a structure called STimer that we will be able to use in any of our projects. It will also contain the following member variables and methods:

    // members
    float       LastTime; // the time calculated by the last Frame() call.
   
    // methods
    void        Reset(); // Reset timer
    void        Frame(); // Calculate time elapsed since the last Frame() or Reset() call.


We're going to need this file included in our main.cpp in order to use the STimer object in our main loop, in which we're going to declare an instance of the timer and query Frame() on every loop iteration so we can send the time delta to the update() function:

int main( void ) // start application from here
{
    SGame gameInstance;
    STimer timer; // declare our timer instance

    setup( &gameInstance ); // call setup() and send the address of our data as parameters

    int frameCounter=0; // declare a variable to keep track of the frame number
    while( true ) // execute block {} while what's inside () is true
    {
        timer.Frame(); // Query time elapsed since last Frame() call

        printf("Current frame number: %i\n"
            "Last frame time: %f\n", frameCounter, timer.LastTime );
        update( &gameInstance, timer.LastTime ); // send time elapsed to update() call
        draw( &gameInstance ); // render game data


We're also be modifying our functions at main.cpp to work with the declared functions at game.h and we're going to move all the remaining code to a new game.cpp file where the game functions will be defined. So our setup() function will now look somewhat like this:

// Use this function to setup our game data
void setup( SGame* gameObject ) // Accepts an address pointing to an SGame instance
{
    printf("- setup() called.\n");

    // call setup game functions
    setupMap(gameObject); // initialize map
    
    setupPlayer(gameObject); // initialize player  
    setupEnemies(gameObject); // initialize enemy list
};


We're also be updating our update() and draw() functions the same way and moving all the game code into our game.cpp functions, which will now look similar to the following:

// Use this function to update the map tiles
void updateMap( SGame* gameObject, float fLastFrameTime  )
{
}


// Use this function to update the player
void updatePlayer( SGame* gameObject, float fLastFrameTime  )
{
    if(GetAsyncKeyState(VK_UP))
        gameObject->Player.z -= 1; // decrease by 1
    if(GetAsyncKeyState(VK_DOWN))
        gameObject->Player.z += 1; // increase by 1
    if(GetAsyncKeyState(VK_RIGHT))
        gameObject->Player.x ++; // increase by 1
    if(GetAsyncKeyState(VK_LEFT))
        gameObject->Player.x --; // decrease by 1 

}

We should end up with a file structure similar to the one shown below and it should still build and run as it previously did:



The complete files for this tutorial can be found in the repository I made for the tutorials at google code. After the code is better organized we can start looking at improving our game systems and making a proper game in which you can do something more interesting than just run and get your health lowered up to negative values.

In the next tutorial we will be improving the way our characters move and enable a configurable speed for each of them. See you there!


jueves, 19 de junio de 2014

Using TortoiseSVN to keep track of your project changes.

Before moving on with the creation of the game I wanted to talk about a version control system that is often used in development projects called SVN, which is a system that controls the changes in the files and assign a version every time you upload an updated file, allowing you to compare between versions of a file or revert changes to a previous version.

One of the most used programs for this is called TortoiseSVN which can be downloaded from http://tortoisesvn.net/downloads.html

After installing the program you will be able to get a working copy of the source code of these tutorials by going to the folder where your solutions are located and right-click on the folder so the context menu appears, then select the option SVN Checkout and a window will pop up, something like this:

So the only thing that needs to be done is to place the URL of the repository (such as gpftw.googlecode.com/svn/trunk) we want to checkout and the directory where the working copy will be placed (you can change E:\Dev to the folder of your choice).

 After clicking OK, the svn program will download the files and report success or failure:



Now if we change a file of the working copy, TortoiseSVN will report changes and display them with a red icon:


Right clicking on the file will give us the SVN -> Diff option in the menu that we can use to find where the file got changed:


After we modified our file we can commit the changes to the repository by right clicking on the folder of the working copy and select SVN Commit from the menu.



Right clicking on a folder or file of the working copy will also provide the Show log option:


For creating a new repository you can use Google Code (http://code.google.com/) or another web service, but this will also make your code public it most cases. Other option available is to create the repository in your own system. For this you can right click a folder of your choice and select the "Create repository here" option:


After that you will be told the url of the repository that you have to set when you want to checkout a working copy. In this example will be file:///E:\MyRepository



This should be enough to get you started with SVN, though there's much more about it that you should research on your own. Now you will be able to download the tutorial files from the repository and use TortoiseSVN to help you managing your projects.

In the next tutorial we'll be talking about header files and how to organize your code properly into different code files, and we will also be adding a timer to our game loop so we can update our data from the actual time between frames. Let's continue making our game there!



Adding keyboard control for the player and improving map rendering.

After a comment I received I decided it was time to improve the draw() step of the application we were developing until now and make it to display faster in order to show the frame smoother. In order to achieve this we need to reduce the amount of printf() calls we are doing during our draw() step, because when printing every row of the map as independent calls, the text in the console moves up and this makes the whole display too hard to watch.

Now we will be placing our letters representing the map into an auxiliary array so we can then draw it with a single printf() call. For this we will be declaring an array and a variable for the cell position in which we're going to place our map character inside the array.

    char imageMap[4096]={}; // The assignment of empty braces initializes all elements to 0
    int nImageCursor = 0; // The position where we should place our next character


Then we will assign the characters representing the map in this array. For this we will assign the letter and increment the cursor to the next position like this: 

    imageMap[nImageCursor++] = 'P'; // assign 'P' at imageMap[nImageCursor] then increment nImageCursor

We need to set a 0 value at the end of our map text so printf() and other string operations know that the text ends there, so once we finished to place our map characters into the array, we set the value for the cursor position to 0:

    imageMap[nImageCursor] = 0; // a null char indicates the end of the character string or text

After that, we simply call printf() by sending our map image as parameter as follows:

    printf( "%s", imageMap ); // %s gets replaced by the imageMap string.

This leaves our code at the draw() function looking somewhat like this:

    // We're going to draw our map in the imageMap array.
    char imageMap[4096]={}; // the assignment of empty braces initializes all chars in the array to 0
    int nImageCursor = 0; // The position where we should position our next character
    for( int z=0; z< map.Depth; z++ ) // iterate over every row
    {
        for( int x=0; x< map.Width; x++ ) // iterate over every column for the z row
        {
            if( player.x == x && player.z == z )
                imageMap[nImageCursor++] = 'P'; // draw the player as an ascii character

            else if( enemy.x == x && enemy.z == z )
                imageMap[nImageCursor++] = 'E'; // draw the enemy

            else
            {
                // Replace the tile value with a space character if the value is 0, otherwise the null character will terminate our string and the map will display truncated. 
                imageMap[nImageCursor++] = map.FloorCells[z][x] ? map.FloorCells[z][x] : ' '; // inline conditional is (condition  ? value_if_true : value_if_false)
            }
        }
        imageMap[nImageCursor++] = '\n'; // \n is the code character for "new line" inside a text. We use it to display the next cells in the next row.
    }
    imageMap[nImageCursor] = 0; // a null char indicates the end of the character string or text.

    printf( "%s", imageMap ); // draw the map to the console



Now that we have our display improved, we can start adding some controls to our player. We are going to do this during our update() step by checking the state of the arrow keys and changing the value of the player position accordingly:

    if(GetAsyncKeyState(VK_UP))
        gameObject->Player.z = gameObject->Player.z - 1; // decrease z by 1
    if(GetAsyncKeyState(VK_DOWN))
        gameObject->Player.z += 1; // increase z by 1
    if(GetAsyncKeyState(VK_RIGHT))
        gameObject->Player.x ++; // increase x by 1
    if(GetAsyncKeyState(VK_LEFT))
        gameObject->Player.x --; // decrease x by 1


Not that difficult, was it? So now let's enable more enemies into our game so it doesn't look so empty. For this we're going to replace our single SCharacter instance we used for the enemy with a template type called vector that comes with the standard library of the compiler. We need to include a new file in order to have access to vector's definition:

#include <vector> // so we can use the std::vector<> type.

Now we can declare the player and our enemy list as part of our new SGame structure that will hold the game data:

struct SGame // holds game data
{
    SMap Map; // declare a variable of type SMap
    SCharacter Player; // Declare a variable of type SCharacter for the player
    std::vector<SCharacter> Enemy; // Here we're going to store our list of enemies
};


Also we should add a new map layer for storing our enemies as part of the map structure:

struct SMap // The struct is a block of variables to be used to store our map information
{
    int Width, Depth; // Declare Width and Depth variables which will hold the active map size
    int FloorCells[MAX_MAP_DEPTH][MAX_MAP_WIDTH]; // 2-D Array representing the tile cells
    int EnemyCells[MAX_MAP_DEPTH][MAX_MAP_WIDTH]; // Map that holds indices to the enemy list.
};


We could use a new function in order to manage the setup of enemies, like this:

void setupEnemies( SGame* gameObject )
{
#define INITIAL_ENEMY_COUNT 4
    for( int iEnemy=0; iEnemy < INITIAL_ENEMY_COUNT; iEnemy++ )
    {
        SCharacter newEnemy;
        newEnemy.MaxPoints        = { 100, 50, 1000000 }; // HP, MP and XP
        newEnemy.CurrentPoints    = { 100, 50, 0 };
        newEnemy.x = rand() % gameObject->Map.Width; // random value between 0 and width-1
        newEnemy.z = rand() % gameObject->Map.Depth;

        gameObject->Enemy.push_back( newEnemy ); // copy the new enemy as a new element at the end of our enemy list.
    }
};


At the update function, we refresh the enemy layer of the map from the enemy list. For this we first clear the enemy layer with the INVALID_ENEMY value:

    for( int z = 0; z < gameObject->Map.Depth; z++ ) // clear each row
       memset( gameObject->Map.EnemyCells[z], INVALID_ENEMY, sizeof(int)*gameObject->Map.Width );


Then we iterate through each enemy and place the index in the corresponding cell:

    for( unsigned int iEnemy=0; iEnemy < gameObject->Enemy.size(); iEnemy++ )
    {
        gameObject->Map.EnemyCells[currentEnemy->z][currentEnemy->x] = iEnemy; // assign enemy index to the cell corresponding to this enemy
    }


Now only the display of the new enemy data is missing, and we can change our previous lines at draw() with these new ones, which check for a valid enemy index in the cell being rendered:

            else if( gameObject->Map.EnemyCells[z][x] != INVALID_ENEMY )
                imageMap[nImageCursor++] = 'E'+gameObject->Map.EnemyCells[z][x]; // draw the enemy


The source code for this program can be read from http://code.google.com/p/gpftw/source/browse/trunk/Tutorial5/main.cpp

That's it for now, I hope you liked it. In the next tutorial we we'll be talking about SVN which we're going to use through the rest of the tutorials. See you there!

martes, 17 de junio de 2014

Adding player character and AI-controlled enemy to the game scene.

Hello again!

I decided it's time to add something more interesting than a simple rectangle of smiley walls without having to talk about more advanced topics such as textures and sprite collisions, so I decided to change our program into something that looks more like a game by adding a player (represented by a P) and an enemy (E):


(I know it would be too generous to call this a game, but the game isn't much further either).


I also thought that it would be nice if the enemy was kind of aggressive, so I made it run towards and into the player and hurt him by 1 each time they share the same cell:



So what we're going to do next is add some variables to hold our character positions (such as the player or an enemy character):

struct SCharacter // we're gonna use these bunch of variables for representing all our game characters
{
         int HealthPoints; // declare an integer for  the character life points
         int x, y; // coordinates in map or array
};


Now we can use the struct to define our player and character as follows:

     SCharacter playerCharacter;  // Declare a variable of type SCharacter for the player
     SCharacter enemyCharacter;   // and another SCharacter instance for the enemy


We should also update our functions to take these as parameters so we can use them at the setup() draw() and update() steps:

void setup( SMap* activeMap, SCharacter* player, SCharacter* enemy );  // Accepts addresses pointing to SMap and SCharacter data
void update( SMap* activeMap, SCharacter* player, SCharacter* enemy ); // We can now modify our player and enemy data from these functions
void draw( SMap someMap, SCharacter player, SCharacter enemy );        // Accepts a copy of the data found in the game structures.


This will require to call them by sending the new parameters added:

    setup( &gameMap, &playerCharacter, &enemyCharacter ); // call setup() and send the address of our data as parameters
   
update( &gameMap, &playerCharacter, &enemyCharacter ); // update frame, send map address to update() call
    
draw( gameMap, playerCharacter, enemyCharacter ); // render frame, send copy of the data to be displayed by draw() 

We will then set some valid data to our player and enemy. For this we can add some code to the setup() function:

    // set some initial configuration to the game characters
    player->HP = 100; enemy->HP = 100;   
    player->x = 5, player->z = 5;
    enemy->x = 25, enemy->z = 15;


And in the update() step we're going to add some code for the enemy to walk towards the player, and reset to a random position once it hit the player:


    if( player->x < enemy->x )
        enemy->x = enemy->x-1; // decrease enemy x if player in that direction
    else if( player->x > enemy->x )
        enemy->x = enemy->x+1; // increase enemy x if player in that direction

    if( player->z < enemy->z )
        enemy->z = enemy->z-1; // decrease enemy z if player in that direction
    else if( player->z > enemy->z )
        enemy->z = enemy->z+1; // increase enemy z if player in that direction

    if( player->z == enemy->z
        && player->x == enemy->x )
    {
        player->HP    -= 1; // Decrease player life if enemy position matches player position
        enemy->x = rand()%activeMap->Width, enemy->z = rand()%activeMap->Depth; // set a random position for the enemy so it has to walk again to reach the player
    }


Now the only thing that is missing is to improve our draw() step in order to draw the player and enemy. We're going to check if the coordinates match with the player position and then draw a letter representing the player ('P'), and if they don't match, we compare the coordinates with the enemy position, drawing an 'E' if match, and if none of the previous conditions is met, we're going to draw the letter for the tile as we did before.

Our map render loop will then become something like this:

    for( int z=0; z< someMap.Depth; z++ ) // iterate over every row
    {
        for( int x=0; x< someMap.Width; x++ ) // iterate over every column for the z row
        {
            if( player.x == x && player.z == z )
                printf( "%c", 'P' ); // draw the player
            else if( enemy.x == x && enemy.z == z )
                printf( "%c", 'E' ); // draw the enemy
            else
                printf( "%c", someMap.FloorCells[z][x] ); // draw the tile at (x,z) as an ascii character
        }
        printf( "\n" ); // \n is the code character for "new line" inside a text. We use it to display the next cells in the next row.
    }


These few and simple modifications added our first artificial intelligence signs to our program and now we are ready to expand all this into something more interesting, preparing it for later being represented with some fancy 2d or 3d animations and particle physics.

The complete code for the program will still be quite compact, though this is going to change so I recommend downloading TortoiseSVN which I will also be explaining how to use for a development project and for downloading the code for next tutorials.


In the incoming tutorials we are going to be adding keyboard control to our player and modifying our current program in order to support more enemies and with more interesting properties than just health points. See you there!



lunes, 16 de junio de 2014

How to set breakpoints in Visual Studio and debug the application.

Hi again!

In this tutorial I'm gonna explain how to pause your program at a selected line and see the program data during execution.

Visual Studio allows to select a position of the code in which to pause the execution by clicking over the grey line at the left of the editor area. In the image below we can see a breakpoint added (the red dot) with the program paused and the mouse over a variable showing the data contents of the variable (which hold invalid values because nothing was assigned yet):


For running the program in debug mode you can either press F5 or go to menu Tools -> Start Debugging and it will pause at the first breakpoint reached. To continue execution after the breakpoint press F5 again.

This enables us to analyze the program flow step by step and see the contents of the data in memory. In the next image we can see the contents of gameMap have changed after the setup() call:


We can see then how our data changes as the program runs each line:

If we want to move to the next line but keep with the execution paused, we can press F10 or select the "step over" option in the Debug menu. If you want to jump inside the function being called in that line, you can press F11 or select the "step into" option in the Debug menu.

Knowing how to debug your program is necessary for any program you want to make and Visual Studio makes it very easy. Now you will be able to work and analyze the program flow better when you read the next tutorial about updating the game map seen in our previous program.

Adding game data to our skeleton application.

Hi there!

Following we're going to see how to start adding game logic to our skeleton application seen in the previous tutorial.

For this we will add a variable holding a two-dimensional array of cells, which is declared as follows:

    int FloorCells[MAX_MAP_DEPTH][MAX_MAP_WIDTH]; // 2-Dimensional array of (int)eger numbers

We will also add two variables holding the actual size of the map. This can be done with:

    int Width; // Declare Width and Depth variables which will hold the active map size
    int Depth;


Where "int" tells the type of data that the variable will hold, and Width and Depth are the names of the variables. They can also be defined like:

    int Width, Depth; // Declare Width and Depth variables which will hold the active map size


And for holding all the map data under a single name which we can reference comfortably, we're going to declare a data structure holding all these variables and we're going to call it SMap (S for Structure):

struct SMap  // The struct is a block of variables to be used to store our map information
{
    int Width; // Declare Width and Depth variables which will hold the active map size
    int Depth;   
    int FloorCells[MAX_MAP_DEPTH][MAX_MAP_WIDTH]; // 2-Dimensional array of integers which can be accessed as FloorCells[y][x] and will hold values for representing the terrain
};


Then we can declare a new variable holding together all the contents of SMap:

    SMap gameMap;      // declare a variable of type SMap

Then for setting a value on a variable inside the structure we can do as follows:

    gameMap.Width = 32;      // Set a width value
    gameMap.Depth = 21;      // Same for map depth


We can also get the memory address where the data of gameMap is located:

   SMap* gameMapAddress = &gameMap;

Where * tells that the address points to a SMap structure, gameMapAddress is the name of the variable and & requests the memory address of the gameMap data.

After having our data architecture set up, we will need to store some data inside our map cells. For this we're going to iterate over each position and set the value to the corresponding position in our 2d array:

#define TILE_GRASS 0
    for( int z=0; z< gameMap.Depth; z = z+1 ) // iterate over every row
    {
        for( int x=0; x< gameMap.Width; x++ ) // iterate over every column for the z row
        {
           
gameMap.FloorCells[z][x] = TILE_GRASS; // initialize the (x,z) map cell to "grass"
        }
    }


Where  the for() statement will have a syntax such as:

for( a ; b ; c )
{
      // code block for each iteration

Where a, b and c are:
a. code to execute before start iterating.
b. condition to meet before each iteration. (otherwise end for( ) execution)
c. code to execute at the end of iteration.

We can set up a wall border in a similar way:


#define TILE_WALL 1
    // set a wall border
    for( int x=0; x<
gameMap.Width; x++ )   
       
gameMap.FloorCells[0][x] = TILE_WALL; // set all cells in the first row [0]    

After having everything set up, we will want to draw it on the screen. For this we're going to iterate over each cell and print it as follows:

    for( int z=0; z< gameMap.Depth; z++ ) // iterate over every row
    {
        for( int x=0; x<
gameMap.Width; x++ ) // iterate over every column for the z row
        {
            printf( "%c",
gameMap.FloorCells[z][x] ); // draw the contents of the cell at (x,z) as an ascii character
        }
        printf( "\n" ); // \n is the code character for "new line" inside a text. We use it to display the cells for the next z value in the next row of the display.
    }
You can find more about printf formatting codes with Google, because it's a well known standard function and it's documented everywhere.

In the following link is the complete code added to our skeleton application but I also pasted the code below because it's still small enough to fit in the page:

// from http://code.google.com/p/gpftw/source/browse/trunk/Tutorial3/main.cpp
 

#include <stdio.h>      // for printf()
#include <windows.h>    // for interacting with Windows

#define TILE_GRASS   0  // define some values to represent our terrain tiles
#define TILE_WALL    1
#define TILE_WATER   2
#define TILE_LAVA    3

#define MAX_MAP_WIDTH    256 // define a maximum size to our game map
#define MAX_MAP_DEPTH    256   

struct SMap // The struct is a block of variables to be used to store our map information
{
    int Width; // Declare Width and Depth variables which will hold the active map size
    int Depth;   
    int FloorCells[MAX_MAP_DEPTH][MAX_MAP_WIDTH]; // 2-Dimensional array of integers which can be accessed as FloorCells[y][x] and will hold values for representing the terrain
};

void setup( SMap* activeMap );  // Accepts as parameter an address pointing to an SMap
void update( SMap* activeMap ); // Accepts as parameter an address pointing to an SMap
void draw( SMap someMap );      // Accepts as parameter a copy of the contents of an SMap

int main( void ) // start application from here
{
    SMap gameMap;      // declare a variable of type SMap
    setup( &gameMap ); // call setup() and send the address of our gameMap as parameter

    int frameCounter=0; // declare a variable for keeping track of the number of frame since execution began, where "int" stands for "integer"
   
    while( true ) // execute block {} while what's inside () is true
    {
        printf("Current frame number: %i\n", frameCounter);

        update( &gameMap );     // update frame, send map address to update() call
        draw( gameMap );        // render frame, send copy of the data to be displayed by draw()
        if(GetAsyncKeyState(VK_ESCAPE)) // check for escape key pressed
        {
            break; // exit while()
        }
        Sleep(150); // wait some time to give visual stability to the frame
        

        frameCounter = frameCounter+1;
    };
  
    return 0; // return an int
}

void setup( SMap* activeMap ) // Accepts an address pointing to an SMap
{
    printf("- setup() called.\n");

    activeMap->Width = 32; // Set a proper width for our map, which has to be less than MAX_MAP_WIDTH
    activeMap->Depth = 21; // Same for map depth   

    for( int z=0; z< activeMap->Depth; z = z+1 ) // iterate over every row
    {
        for( int x=0; x< activeMap->Width; x++ ) // iterate over every column for the z row
        {
            activeMap->FloorCells[z][x] = TILE_GRASS; // initialize the (x,z) map cell to "grass"
        }
    }

    // set a wall border
    for( int x=0; x< activeMap->Width; x++ )   
        activeMap->FloorCells[0][x] = TILE_WALL; // set all cells in the first row [0]   
    for( int x=0; x< activeMap->Width; x++ )       
        activeMap->FloorCells[activeMap->Depth-1][x] = TILE_WALL; // set all cells in the last row [depth-1]   
    for( int z=0; z< activeMap->Depth; z++ )   
        activeMap->FloorCells[z][0] = TILE_WALL; // set all cells in the first column [0]   
    for( int z=0; z< activeMap->Depth; z++ )   
        activeMap->FloorCells[z][activeMap->Width-1] = TILE_WALL; // set all cells in the last column [width-1]   
};

void update( SMap* activeMap )    // Accepts an address pointing to an SMap
{
    printf("- update() called.\n");
};

void draw( SMap someMap ) // Accepts a copy of the contents of an SMap
{
    printf("- draw() called.\n");
    for( int z=0; z< someMap.Depth; z++ ) // iterate over every row
    {
        for( int x=0; x< someMap.Width; x++ ) // iterate over every column for the z row
        {
            printf( "%c", someMap.FloorCells[z][x] ); // draw the contents of the cell at (x,z) as an ascii character
        }
        printf( "\n" ); // \n is the code character for "new line" inside a text. We use it to display the next cells in the next row.
    }
};



What this program does is the following:
- Declares a variable of type SMap to hold data during the whole program execution.
- Calls setup() which writes data on our SMap instance.
- Starts calling update() and draw() on every iteration of the main loop until the escape key is pressed, then inside draw() prints all the map cells as console characters.

In the next tutorial I will explain how to debug the application with Visual Studio and pause the execution to analyze the program step by step, and after that we will be adding more logic for the update() step in order to make our program more interesting.

See you there!