Tuesday, April 5, 2011

Code::Blocks(13) RPG 1 - The Hero has a Name!

I'm back again!

< In the previous tutorial i didn't made use of part of the features that come with C++ : classes, so it's time to use them to define and store data about our hero and the map, into the player class i want to define the player creation process, finally i want to create a Main Menu for the game...>

I think I'm very ambitious with that post, but the objectives are:
  1. Create at least two classes: cplayer and cmap
  2. Define a Player creation process
  3. Create a Main Menu..
<Step one: the classes>

First let's start creating two classes: cplayer and cmap, to do that code::blocks has an assistant to design classes, so resuming from the code in the previous post we should select "File" > "New" > "Class":

In the class name , write "cplayer", and then mark "Header and implementation shall be in the same folder", then press "Create":

Select "Yes" to add the files to the project and...

Press ok, so we will use files both to debug and compile the release project, we need to create a second class "cmap", so just repeat the steps,and save the project.

Now let's start modifying the cmap class, we have two files cmap.h (header) and cmap.cpp (implementation), in the header we are going to define the variables and functions , so we are going to move the Uint8 Map[x][y][layer] to the class, we need too a pair of functions to get and set the values in the map array, so our cmap header will finally be:

#ifndef CMAP_H
#define CMAP_H

#include  //SDL library

class cmap
        virtual ~cmap();
        Uint8 GetTile(int x, int y, int layer); //Get tile value
        void SetTile(int x, int y, int layer, Uint8 Tile);//Set tile Value
    Uint8 Map[50][50][3]; //The map, now it has three layers
    int x,y;

#endif // CMAP_H

Now we have to implement the functions in cmap.cpp:

    for (x=0;x<50;x++){ // Map size 50x50 tiles: 5000 x 5000 pixels
    for (y=0;y<50;y++){
        Map[x][y][0]=0; //clean the map layer 1 so everything is "0" >> "sand"
        Map[x][y][1]=63; //clean the map layer 2 so everything is "63" >> nothing
        Map[x][y][2]=63; //clean the map layer 3 so everything is "63" >> nothing
    };//end y for
 };//end x for

for (x=2;x<5;x++){
    for (y=2;y<7;y++){
                Map[x][y][0]=2; //A lake
                Map[x+4][y][0]=1; //Grass..
    };//end y for
 };//end x for

for (x=1;x<5;x++){
    Map[x][2][1]=8; //Trees
    Map[x][3][1]=10; //A road
}; // end for x

Map[5][3][1]=12; //A road
Map[5][2][1]=14; //A road
Map[6][2][1]=10; //A road

Map[6][3][1]=9; //A volcano

Map[3][3][2]=25; //Coins
Map[4][3][2]=25; //More coins

for (x=0;x<50;x++){

void cmap::SetTile(int x, int y, int layer, Uint8 Tile) //Set tile value

Uint8 cmap::GetTile(int x, int y, int layer) //Get tile value
        return Map[x][y][layer];

That's it! Now we have a simple class that will store the map data, notice that i've inserted into the creator the code to define a map.. it's not the better way to do that , but i wanted to show how we can separate code into classes , now we need to change a few things in the main program to use the new class, first we have to insert into rpg.cpp:

#include "cmap.h" // The Map class

So we actually use the class, now let's create a variable to use the map:

cmap mymap; // Our map class

Then i will erase the old Uint8 Map definition and the old map initialization, now everything is inside mymap, finally we have to update the map blitting functions to draw the map:

myengine.BlitTileset(x *myengine.TileSize-Camera.x,y *myengine.TileSize-Camera.y,
mymap.GetTile(x,y,0)); //Display map, layer 1

myengine.BlitTileset(x *myengine.TileSize-Camera.x,y *myengine.TileSize-Camera.y,
mymap.GetTile(x,y,1));//Display map, layer 2

myengine.BlitTileset(x *myengine.TileSize-Camera.x,y *myengine.TileSize-Camera.y,
mymap.GetTile(x,y,2));//Display map, layer 3

Notice before i used Map[x][y][layer], now mymap.GetTile(x,y,layer), time to "Build and Run" the project to see what happens, if you have followed all the steps, you should get the same results than in the previous post, it's ok because we have just changed a variable with a class.

We have to create the player class too, so let's move the PcX,PcY and PcSpeed to the cplayer class:


#ifndef CPLAYER_H
#define CPLAYER_H

class cplayer
        cplayer(); //Creator
        virtual ~cplayer(); //Destructor
        void SetX(int x);// Set player x position
        void SetY(int y);//Set player y position
        void SetSpeed(int speed);//Set Player speed
        void SetName(const char* name);//Set Player's name
        int GetX(); // Get player x position
        int GetY(); // Get player y position
        int GetSpeed();// Get player speed
        const char* GetName(); //Get player name

    int X,Y,Speed; //X and y position, player speed
    const char* Name; // Player's name

#endif // CPLAYER_H

I've added too the player's name variable, and the functions to get/set the values:


    Name="No Name";


       void cplayer::SetX(int x)

        void cplayer::SetY(int y)

        void cplayer::SetSpeed(int speed)

        int cplayer::GetX()
            return X;

        int cplayer::GetY()
            return Y;

        int cplayer::GetSpeed()
            return Speed;

         void cplayer::SetName(const char* name)
            Name = name;

         const char* cplayer::GetName()
             return Name;

Now we return again to rpg.cpp, we have to add:

#include "cplayer.h" // The player class

So we actually use the class, now let's create a variable to use the player:

cplayer Hero; // Our player class

And initialize the values:

Hero.SetX(110); //starting player's x position
Hero.SetY(310); //starting player's y position
Hero.SetSpeed(5); //starting player's speed
Hero.SetName("The Karbarian"); //Player's name

Keyboard input changes, to use the player's class:

if (keys[SDLK_UP])Hero.SetY(Hero.GetY() - Hero.GetSpeed());
            if (Hero.GetY()<1)Hero.SetY(1);
            while (Hero.GetY()  <= (Camera.y )) Camera.y-=1;

if (keys[SDLK_DOWN] ) Hero.SetY(Hero.GetY() + Hero.GetSpeed());
            if (Hero.GetY()>4900)Hero.SetY(4900);
        while (Hero.GetY() +100 >= (Camera.y  + Camera.h ) )Camera.y+=1;

if (keys[SDLK_LEFT] ) Hero.SetX(Hero.GetX() - Hero.GetSpeed());
            if (Hero.GetX()<1)Hero.SetX(1);
            while (Hero.GetX()  <= (Camera.x )) Camera.x-=1;

if (keys[SDLK_RIGHT] ) Hero.SetX(Hero.GetX() + Hero.GetSpeed());
            if (Hero.GetX()>4900)Hero.SetX(4900);
        while (Hero.GetX() +100 >= (Camera.x  + Camera.w ) )Camera.x+=1;

And finally the blitting functions need to be adapted too so they use the player class:

for (x=Camera.x/100 ;x<=Camera.x/100 + (Camera.w/100);x++){//Process map
    for (y=Camera.y/100  ;y<=Camera.y/100 + (Camera.h/100);y++){

       myengine.BlitTileset(x *myengine.TileSize-Camera.x,y *myengine.TileSize-Camera.y,
mymap.GetTile(x,y,0)); //Display map, layer 1
       myengine.BlitTileset(x *myengine.TileSize-Camera.x,y *myengine.TileSize-Camera.y,
mymap.GetTile(x,y,1));//Display map, layer 2
       myengine.BlitTileset(x *myengine.TileSize-Camera.x,y *myengine.TileSize-Camera.y,
mymap.GetTile(x,y,2));//Display map, layer 3

myengine.BlitTileset(Hero.GetX()  - Camera.x   ,Hero.GetY()  -Camera.y  ,16); //Display player

 Well, our player has a name and we want to show it, so that's what i'm going to make here:

mycolor = {0, 0, 0 };
myengine.SetTextColor( mycolor);
myengine.TextOut(Hero.GetX()  - Camera.x  +50  ,Hero.GetY()  -Camera.y  ,Hero.GetName());

We display player's name at his right side, it can be easily modified so the name moves to another side...


Post is getting way too big, I'm going to split it so it's going to be easier to follow..

See you