Thursday, March 17, 2011

Code::Blocks(12) RPG 1 - The Hero and the Camera

The hero finally can move around...

Important Note: From <now> i have decided to change the structure of the posts, i have received comments because my posts were confusing: I was mixing mandatory statements with suggestions and various and confusing thoughts, so i have decided to split this posts in two parts, the first one is going to be the objectives for the current post and the discussion about how to do things and my thoughts (in Italic), the second one is the solution to the problem.

In this post we have two objectives:
  1. Define and draw a hero.
  2. Define a camera, draw the map using the camera.

So we need a hero, in the near future it's going to fight the foes and rescue the princess (Female heroes will rescue the prince of course),  but at the moment we just need to be able to draw the hero in the map, and to move him/her around it.

So what do we need? We just need to locate the hero in the map to draw it (X and Y location), that's all from now!, we are going to create the PcX, PcY and PcSpeed variables to store the position of the Player and the speed.

int PcX,PcY,PcSpeed;

PcX = 110;
PcY = 310;
PcSpeed = 5; 

<It's always better to encapsulate things into classes , that's why it's useful C++, but sometimes, for simple things it is much more faster to just define a few variables, so that's what I'm going to do with the Hero (and with the camera too), the hero will grow in the future and will become a class(but not now)>

Since i started drawing the map on previous posts i was drawing just a small piece of it, showing just 8x6 tiles starting from the top-left corner of the map(screen), now the objective is to have a camera that follows the PC around the map.

I have been thinking in it and realized there was at least three kind of common cameras used in is the games (2D games):

  1. A camera that constantly moves, following the hero so we have the feeling that the hero is stopped and the map is moving.
  2. A camera that show a part of the map , and only moves when the hero is about to get out the map, so it will adjust the view only to prevent us to loose the hero.
  3. A fixed camera that shows a screen, when the user gets out of the screen it loads another complete scree.
I have chosen to  use the second kind of camera because it is less confusing and allows people to move without too much screen scrolling.

To set up a camera we just need to define 4 variables: Width and height of the field of view , and the start of the camera "window", we could use a SDL_rect to define it so we will have something like:

SDL_Rect Camera;
Camera.x= 1;
Camera.y= 1; 
Camera.w= 800;
Camera.h= 600;

Camera.x and Camera.y is the top-left corner of the camera, Camera.w and Camera.h is the window size that will be displayed.

Now, into the key input we need to move the player around the map, increasing it's x and y coordinates (adding or substring the PjSpeed), we must prevent player from getting outside the map so x and y must be always between 1 and 4900 (tiles*tilesize), finally we have to make the camera follow the player if it gets out the camera bounds:


if (keys[SDLK_UP] ) PcY-=PcSpeed;
            if (PcY<1)PcY=1;
            while (PcY  <= (Camera.y )) Camera.y-=1;
if (keys[SDLK_DOWN] ) PcY+=PcSpeed;
            if (PcY>4900)PcY=4900;
        while (PcY +100 >= (Camera.y  + Camera.h ) )Camera.y+=1;
if (keys[SDLK_LEFT] ) PcX-=PcSpeed;
            if (PcX<1)PcX=1;
          while (PcX <= (Camera.x )  )Camera.x-=1;
if (keys[SDLK_RIGHT] ) PcX+=PcSpeed;
            if (PcX>4900)PcX =4900;
          while (PcX +100  >= (Camera.x  + Camera.w ))Camera.x+=1; 

The map drawing function has changed , now it travels across the map starting from the top left corner of the camera , translating the camera position (in pixels), to the properly tile in the map array, then it will blit the tile from all the three layers, notice the blitting function in the engine has changed, i will explain it later...

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,Map[x][y][0]); //Display map, layer 1
       myengine.BlitTileset(x *myengine.TileSize-Camera.x,
y *myengine.TileSize-Camera.y,Map[x][y][1]);//Display map, layer 2
        myengine.BlitTileset(x *myengine.TileSize-Camera.x,
y *myengine.TileSize-Camera.y,Map[x][y][2]);//Display map, layer 3

Now we draw the player, adjusting it to the camera too:

myengine.BlitTileset(PcX  - Camera.x   ,PcY  -Camera.y  ,16); //Display player

Finally i have added a new function to the simple engine: Text drawing, to test it i have added this code to the main program:

myengine.SetTextColor( {0, 0, 255 }); // Set the text color
myengine.TextOut(10,10,"Rpg test"); //Draw the text at 10,10..
myengine.SetTextColor( {0, 255, 0 });
myengine.TextOut(10,30,"Rpg test");
myengine.SetTextColor( {255, 0, 0 });
myengine.TextOut(10,50,"Rpg test");

Now we are going to change the simple engine.

<Basically i have just changed two functions here: the blitting function and the mouse drawing function, after thinking about it i've seen that the blitting function was too complicated so i have reduced it to the x and y (pixel) start position and the tile to draw, i have added too a pair of functions to draw text on the screen , a function we are going to use in the next tutorials, there are two new functions: SetTextColor(SDL_color) to adjust the text color and Textout(x,y, TextToDraw), and that's it>


#ifndef ENGINE_H
#define ENGINE_H

#include  //SDL library
#include  //+SDL_image library (I use it for loading .png files)
#include "SDL/SDL_ttf.h"//+SDL text library


class Engine

    SDL_Surface* screen; //The screen
    SDL_Surface* tileset; //A simple tileset for testing purposes only
    SDL_Surface*   text;//The text surface

    TTF_Font *font;//The font that's going to be used
    SDL_Color textColor;  //The color of the font
    int ScreenX;//Width of the screen
    int ScreenY;//Height of the screen
    int bpp;//Bit Per Pixel (colour depth)

    int MouseX; //Mouse x position
    int MouseY; //Mouse y position
    Uint8 MouseButtons; //Mouse button pressed

    int Frames; //Actual frame
    int FPS;      //Frames per second
    int StartTick;
    int LastTick;

    Uint8 TileSize; //size of the default tile

        Engine(); //creator
        virtual ~Engine(); //destructor
        void SetVga(Uint16 Width, Uint16 Height, Uint8 BPP,bool FullScreen); //Set the video mode
        void FillColour(Uint8 r,Uint8 g,Uint8 b); //fill screen with a colour
        void Flip(); //Flips the backbuffer to the primary buffer
        void LoadTileset(const char *File); //loads a test tileset
        void DrawMouse(); //refresh mouse position and buttons pressed
        void BlitTileset(int x,int y, int tile); // Blit a tile from our tileset to the screen
        void Wait(Uint32 ms); //waits ms miliseconds
        int TimeElapsed(); //Get the time passed since last time control
        void UpdateTimer(); //Update the start time control
        void TextOut(int x, int y,const char *TextOut);
        void SetTextColor(SDL_Color color);


#endif // ENGINE_H

<I will show here only the updated /new functions to avoid confusion, i have updated the creator and destructor, the blitting function, mouse drawing and text drawing>


Engine::Engine()//Create the class and assign default values

SDL_Init( SDL_INIT_VIDEO ); //We start SDL, just video at the moment


SDL_WM_SetCaption( "Rpg tutorial", NULL ); //Caption of the window

SDL_ShowCursor(SDL_DISABLE); // hide system mouse

//Initialize SDL_ttf
TTF_Init() ;

//Open the font
font = TTF_OpenFont( "CARBONBL.ttf", 28 );

TileSize =100; // size of the default tile 100 pixels x 100 pixels

ScreenX=800; //screen width
ScreenY=600; //screen height
bpp=32; //screen bpp

StartTick = SDL_GetTicks();//Initialize timer
Frames=0; //Initialize frames = 0

    //Free all the surfaces
    SDL_FreeSurface(screen); //Free screen surface
    SDL_FreeSurface(tileset); //Free tileset surface
    SDL_FreeSurface(text);    //Free text surface
//Close the font that was used
    TTF_CloseFont( font );

    //Quit SDL_ttf
    SDL_Quit(); //Close SDL

void Engine::DrawMouse()
BlitTileset(MouseX,MouseY,24); //Blits the mouse pointer into the screen

void Engine::BlitTileset(int x, int y, int tile) //Blits a tile from our tileset to the screen
SDL_Rect start,end;
//set the cliping for the source surface (the tileset)
start.x= (tile%8)*TileSize; //Column = tile % 8 (0-7)
start.y= (tile/8)*TileSize; //Row = tile / 8 (0-7)
//set the cliping for the destination surface (the screen)
end.x =x; // X
end.y =y; // Y
end.w = TileSize;
end.h = TileSize;
SDL_BlitSurface(tileset, &start, screen, &end); //Blits a tile from our tileset to the screen

void Engine::TextOut(int x, int y,const char *TextOut)//Renders text
text= TTF_RenderText_Solid( font, TextOut, textColor );
SDL_Rect dtext;
dtext.w = text ->w;
dtext.h = text ->h;

    //Apply the text to the screen
    SDL_BlitSurface(text, 0, screen, &dtext);

void Engine::SetTextColor(SDL_Color Color)
       textColor= Color;

<Well, that was long! but now we have loaded a full map into memory and can travel north and south, east and west with the arrow keys on the keyboard, (i think it's a good start point) , ¿What to do next? , for me the question is clear: we need the hero to be more  than a sprite moving up and down: we want it to have a name, skills, equipment.... I'm going to design a class that will be the hero with all the skills, and i want to Create the hero before actually we start "playing" the game>

Next tutorial topic is going to be: The Hero class + Creating heroes + Game Menus....

See you soon

P.D. You can find the code for the tutorial in the <Download page >

No comments: