In this post I am going to develop, a basic game called killingSoftly using SDL. Let us assume that killingSoftly game has following rules.
1. There are four Dayaans(witch) and they enter into gaming area randomly.
2. There is one Warrior at centre of gaming area, who can be controlled by arrow keys.
3. If Warrior is overlapped with Dayaan then Dayaan will die and your score will increase.
4. To start new game you have to press 'n' key.
5. Whenever you start new game you can see your previous game score.
6. Before quitting game, press 'n' to see the score then quit.
Watch Video Here: http://www.youtube.com/watch?v=TYRft9dRcqs
You can Download KillingSoftly-0.1, A game which is developed by me DOWNLOAD HERE.
The GUI of this game looks like as follows
Note: The aim this post is not to create a game (you may not like this game!) but to help other developers who are interested in learning game development programming using SDL and C++.
If you observe/ read the comments of the code which i wrote for the development of KillingSoftly you will come to know the following things.
1. How to initialize SDL components for game development.
2. How to load different objects of game.
3. How to control game objects based on events.
4. How to load TTF's and How to display text in gaming environment.
5. How to play audio files in the background etc.
// Filename: KillingSoftly.cpp
#include <string>
#include "SDL/SDL.h"
#include "SDL/SDL_ttf.h"
#include "SDL/SDL_image.h"
#include "SDL/SDL_mixer.h"
using namespace std;
// Display Properties
const int DISPLAY_HEIGHT = 480;
const int DISPLAY_WIDTH = 480;
const int DISPLAY_BPP = 32;
// The dimensions of the Dayaan/Warrior objects
const int OBJECT_HEIGHT = 30;
const int OBJECT_WIDTH = 30;
// frame rate
const int FRAMES_PER_SEC = 25;
// surfaces for various objects
SDL_Surface *Warrior = NULL;
SDL_Surface *Dayaan1 = NULL,*Dayaan2 = NULL,*Dayaan3 = NULL,*Dayaan4 = NULL;
SDL_Surface *display = NULL;
SDL_Surface *scoreDisplay = NULL;
// font
TTF_Font *font = NULL;
// event variable
SDL_Event event;
//color of the font
SDL_Color text_Color = { 0, 0, 255};
// Varibales to caluculate score
int targets =4;
int score=0;
//sound effects used
Mix_Chunk *KillSound = NULL;
// Objects that will move on the display
class Object
{
public:
// x, y offsets of the objects
int x, y, x1, y1, x2, y2, x3, y3, x4, y4;
// Speed of the warrior object
int x_speed, y_speed;
public:
// Initializes the variables
Object();
// Takes key presses and adjusts warrior speed
void handle_warrior();
// Moves the warrior
void move_warrior();
// Moves Dayaans and updates scoring
void move_Dayaans();
// Shows All the objects on the display
void show_All_objects();
};
// Clock class
class Clock
{
private:
// The clock time when clock started
int ticking_start;
// The ticks stored when the clock paused
int pausedTicks;
int ticks_paused;
// Clock status
bool ispaused;
bool isstarted;
public:
// Initializes variables
Clock();
// The various clock actions
void start();
void stop();
void pause();
void unpause();
// Checking status of clock
bool is_started();
bool is_paused();
// Gets the Clock's time
int get_No_of_ticks();
};
// fucntion to load bliting surface
void loading_surface( int x, int y, SDL_Surface* source, SDL_Surface* dest, SDL_Rect* area = NULL )
{
// Holding offsets
SDL_Rect offset;
// Geting offsets
offset.x = x;
offset.y = y;
// Bliting using SDL_BlitSurface
SDL_BlitSurface( source, area, dest, &offset );
}
// Function to load optimised images/objects
SDL_Surface *load_optimized_image( std::string filename )
{
// optimised surface
SDL_Surface* optiImage = NULL;
// loaded image
SDL_Surface* loadImage = NULL;
// loading image
loadImage = IMG_Load( filename.c_str() );
if( loadImage != NULL )
{
// creating optimised surface
optiImage = SDL_DisplayFormat( loadImage );
// Free old surface
SDL_FreeSurface( loadImage );
// Checking optimised surface
if( optiImage != NULL )
{
//Colouring key surface area
SDL_SetColorKey( optiImage, SDL_SRCCOLORKEY, SDL_MapRGB( optiImage->format, 0, 0xFF, 0xFF ) );
}
}
// Returning optimized surface
return optiImage;
}
// Function to load required files
bool loading()
{
// Loading the Warrior/Dayaans images
Warrior = load_optimized_image( "Warrior.bmp" );
Dayaan1 = load_optimized_image( "Dayaan.bmp" );
Dayaan2 = load_optimized_image( "Dayaan.bmp" );
Dayaan3 = load_optimized_image( "Dayaan.bmp" );
Dayaan4 = load_optimized_image( "Dayaan.bmp" );
// Checking for problems in loading Warrior/Dayaans images
if( Warrior == NULL || Dayaan1 == NULL || Dayaan2 == NULL || Dayaan3 == NULL || Dayaan4 == NULL)
return false;
// Loading killing sound effect
KillSound = Mix_LoadWAV( "Kill.wav" );
// Checking for problem in loading the sound effect
if( KillSound == NULL )
return false;
// Opening font called akshar
font = TTF_OpenFont( "akshar.ttf", 20 );
// Checking for error in loading the font
if( font == NULL)
return false;
// If everything is fine returing true
return true;
}
// Function to initialize SDL sub functions
bool initialize()
{
// Initialising SDL
if( SDL_Init( SDL_INIT_EVERYTHING ) == -1 )
return false;
// Setting up display
display = SDL_SetVideoMode( DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_BPP, SDL_SWSURFACE );
// Checking for problems in setting up display
if( display == NULL )
return false;
// Initializing SDL_mixer
if( Mix_OpenAudio( 22050, MIX_DEFAULT_FORMAT, 2, 4096 ) == -1 )
return false;
// Intializing TTF and checking for problems
if( TTF_Init() == -1 )
return false;
// Setting up window Name
SDL_WM_SetCaption( "Killing Softly", NULL );
// If everything is fine returning true
return true;
}
// Function to clean everything
void Do_cleaning()
{
// Removing all surfaces
SDL_FreeSurface( Warrior );
SDL_FreeSurface( Dayaan1 );
SDL_FreeSurface( Dayaan2 );
SDL_FreeSurface( Dayaan3 );
SDL_FreeSurface( Dayaan4 );
// Removing font
TTF_CloseFont( font );
// Removing sound effect
Mix_FreeChunk( KillSound );
// Exiting from SDL_mixer
Mix_CloseAudio();
//Exiting from SDL
SDL_Quit();
}
// Initialising object variables
Object::Object()
{
// Setting up object positions
x = DISPLAY_WIDTH/2;
y = DISPLAY_HEIGHT/2;
x1 = 0;
y1 = 0;
x2 = 0;
y2 = DISPLAY_HEIGHT-OBJECT_HEIGHT;
x3 = DISPLAY_WIDTH-OBJECT_WIDTH;
y3 = 0;
x4 = DISPLAY_WIDTH-OBJECT_WIDTH;
y4 = DISPLAY_HEIGHT-OBJECT_HEIGHT;
// Setting speed of warrior
x_speed = 0;
y_speed = 0;
}
// Speeding up warrior with Keys
void Object::handle_warrior()
{
// If Key pressed
if( event.type == SDL_KEYDOWN )
{
// Changing speed
switch( event.key.keysym.sym )
{
case SDLK_UP: y_speed -= OBJECT_HEIGHT / 5;
break;
case SDLK_DOWN: y_speed += OBJECT_HEIGHT / 5;
break;
case SDLK_LEFT: x_speed -= OBJECT_WIDTH / 5;
break;
case SDLK_RIGHT: x_speed += OBJECT_WIDTH / 5;
break;
}
}
// If Key released
else if( event.type == SDL_KEYUP )
{
// Changing speed
switch( event.key.keysym.sym )
{
case SDLK_UP: y_speed += OBJECT_HEIGHT / 5;
break;
case SDLK_DOWN: y_speed -= OBJECT_HEIGHT / 5;
break;
case SDLK_LEFT: x_speed += OBJECT_WIDTH / 5;
break;
case SDLK_RIGHT: x_speed -= OBJECT_WIDTH / 5;
break;
}
}
}
// Moving warrior
void Object::move_warrior()
{
// Moving warrior left or right
x += x_speed;
// checking Boundaries
if( ( x < 0 ) || ( x + OBJECT_WIDTH > DISPLAY_WIDTH ) )
{
// Moving warrior back
x -= x_speed;
}
// Moving warrior up or down
y += y_speed;
// Checking Boundaries
if( ( y < 0 ) || ( y + OBJECT_HEIGHT > DISPLAY_HEIGHT ) )
{
// Moving warrior back
y -= y_speed;
}
}
// Moving Dayaans and updating scoring
void Object::move_Dayaans()
{
// Moving Dayaan1
x1 = x1+3;
if((x1 + OBJECT_WIDTH) > DISPLAY_WIDTH)
{
x1 = 0;
targets++;
}
// Dayaan1 killed Score updated
if(x1==x && y1==y)
{
// Playing killing Music
Mix_PlayChannel( -1, KillSound, 0 );
x1=0;
score++;
}
// Moving Dayaan2
y2 = y2-3;
if((y2 + OBJECT_WIDTH) < 0)
{
y2 = DISPLAY_HEIGHT-OBJECT_HEIGHT;
targets++;
}
// Dayaan2 killed Score updated
if(x2==x && y2==y)
{
// Playing killing Music
Mix_PlayChannel( -1, KillSound, 0 );
y2 = DISPLAY_HEIGHT-OBJECT_HEIGHT;
score++;
}
// Moving Dayaan3
y3 = y3+3;
if((y3 + OBJECT_HEIGHT) > DISPLAY_HEIGHT)
{
y3 = 0;
targets++;
}
// Dayaan3 killed Score updated
if(x3==x && y3==y)
{
// Playing killing Music
Mix_PlayChannel( -1, KillSound, 0 );
y3=0;
score++;
}
// Moving Dayaan4
x4 = x4-3;
if((x4 + OBJECT_WIDTH) < 0)
{
x4 = DISPLAY_WIDTH - OBJECT_WIDTH;
targets++;
}
// Dayaan4 killed Score updated
if(x4==x && y4==y)
{
// Playing killing Music
Mix_PlayChannel( -1, KillSound, 0 );
x4 = DISPLAY_WIDTH - OBJECT_WIDTH;
score++;
}
}
// Showing Dayaans/warrior on display
void Object::show_All_objects()
{
// Show warrior/dayaans
loading_surface( x, y, Warrior, display );
loading_surface( x1, y1, Dayaan1, display );
loading_surface( x2, y2, Dayaan2, display );
loading_surface( x3, y3, Dayaan3, display );
loading_surface( x4, y4, Dayaan4, display );
}
// Intializing clock
Clock::Clock()
{
// Initialize the variables
ticking_start = 0;
ticks_paused = 0;
ispaused = false;
isstarted = false;
}
// Starting Clock
void Clock::start()
{
// Start clock
isstarted = true;
// Unpause clock
ispaused = false;
// Get the current clock time
ticking_start = SDL_GetTicks();
}
// Stopping Clock
void Clock::stop()
{
// Stop clock
isstarted = false;
// Unpause clock
ispaused = false;
}
// Pausing Clock
void Clock::pause()
{
// If the clock running and not already paused
if( ( isstarted == true ) && ( ispaused == false ) )
{
// Pause clock
ispaused = true;
// counting paused ticks
ticks_paused = SDL_GetTicks() - ticking_start;
}
}
// unPausing Clock
void Clock::unpause()
{
// If clock is paused
if( ispaused == true )
{
// Unpause clock
ispaused = false;
//Reset clock
ticking_start = SDL_GetTicks() - ticks_paused;
// Reset clock
ticks_paused = 0;
}
}
// Getting Number of ticks
int Clock::get_No_of_ticks()
{
// If clock is running
if( isstarted == true )
{
// If clock is paused
if( ispaused == true )
return ticks_paused;
else
return SDL_GetTicks() - ticking_start;
}
// If clock not running
return 0;
}
// Checking clock started or not
bool Clock::is_started()
{
return isstarted;
}
// Checking clock paused or not
bool Clock::is_paused()
{
return ispaused;
}
// Main Function
int main( int argc, char* args[] )
{
// quit flag to check quitting
bool quit = false;
// Object to be used
Object obj;
// The frame rate checker
Clock clk;
// checking for Initialisation
if( initialize() == false )
return 1;
//Loading necessary files
if( loading() == false )
return 1;
// When user Not quit
while( quit == false )
{
//Start frame clock
clk.start();
// Events to handle
while( SDL_PollEvent( &event ) )
{
// Handle events for Display
obj.handle_warrior();
if( event.key.keysym.sym == SDLK_n )
{
// Diplaying score and starting new game on pressing 'n' key
char line[50];
sprintf(line,"You Killed: %d/%d Dayaans::Starting New game ;-)",score, targets);
scoreDisplay = TTF_RenderText_Solid( font, line, text_Color );
// Sendign score to Display
SDL_BlitSurface(scoreDisplay, NULL, display, NULL);
// Setting deplay to see score
SDL_Delay(5000);
// Free the score
SDL_FreeSurface( scoreDisplay );
SDL_Flip( display );
// Setting object positions to orizinal
obj.x = DISPLAY_WIDTH/2;
obj.y = DISPLAY_HEIGHT/2;
obj.x1 = 0;
obj.y1 = 0;
obj.x2 = 0;
obj.y2 = DISPLAY_HEIGHT-OBJECT_HEIGHT;
obj.x3 = DISPLAY_WIDTH-OBJECT_WIDTH;
obj.y3 = 0;
obj.x4 = DISPLAY_WIDTH-OBJECT_WIDTH;
obj.y4 = DISPLAY_HEIGHT-OBJECT_HEIGHT;
targets =0;
score =0;
}
// If the user close window
if( event.type == SDL_QUIT )
quit = true;
}
// Moving warrior
obj.move_warrior();
// Moving Dayaans
obj.move_Dayaans();
// Filling display with white
SDL_FillRect( display, &display->clip_rect, SDL_MapRGB( display->format, 0x00, 0x00, 0x00 ) );
// Showing all objects on display
obj.show_All_objects();
// Updating the Display
if( SDL_Flip( display ) == -1 )
return 1;
// Checking frame rate
if( clk.get_No_of_ticks() < 1000 / FRAMES_PER_SEC )
{
SDL_Delay( ( 1000 / FRAMES_PER_SEC ) - clk.get_No_of_ticks() );
}
}
// Doing cleaning
Do_cleaning();
return 0;
}
To Run KillingSoftly Game:
step-1. Install required libraries using following command
>sudo apt-get install libsdl1.2-dev libsdl-image1.2-dev libsdl-mixer1.2-dev libsdl-ttf2.0-dev g++
step-2. Download KillingSoftly-0.1.
step-3. Extract using following command
>tar -xzvf KillingSoftly-0.1.tar.gz
step-4. Move to extracted killingSoftly folder
>cd KillingSoftly
step-5. To compile execute following command
>g++ -lSDL -lSDL_image -lSDL_mixer -lSDL_ttf KillingSoftly.cpp
step-6. To run execute following command
>./a.out
Hey buddy, that's a nice post you got up there. I haven't really tried it but I like the reason you posted it. I'm a C/C++ Programmer but don't often practice it like most people do. Thanx for the heads up.
ReplyDeleteYou are Welcome buddy. Sorry for late reply
Delete