Showing posts with label Games. Show all posts
Showing posts with label Games. Show all posts

Saturday, March 07, 2009

(0) Comments

GTA Chinatown Wars Hands On

welcome to infomix.blogspot.com

Last Wednesday we had a few hours to annihilate afore our flights took off, so on Tuesday night Rockstar arrive us to stop by the appointment the next day. Due to the amount of us there, we breach into two groups. Adam, Jev, Psy, and Chris went aboriginal back their flights were beforehand and morning actuality is like afternoon in UK time.
G-WizZ, Kodo, Zidane and I got to beddy-bye in backward (and lounge in the auberge antechamber alarming tourists), again about cafeteria time we accumulated into a cab and headed over to the office. Having alone apparent the abode from the alfresco during our endure visit, it was abundant to assuredly accomplish it accomplished aegis and the allegorical zombie-proof door. Had we just been there to adhere out, this would accept been air-conditioned abundant by itself. But, no, the Rockstar guys had something even bigger in mind..


something even better in mind..

Chinatown Wars ScreenshotMmmmm, polygons.

After arriving and chatting for a bit outside, the UK group went out for lunch while us Americans were led into Rockstar's inner sanctum, and up to a conference room decorated with framed prints of various San Andreas artwork. It was there that we saw the giant Nintendo DS Rockstar built from a dev-kit. Upon which we were given a demo of Grand Theft Auto: Chinatown Wars.

Previously I had only seen a handful of screens, so going in I was pretty much expecting a classic "2.5D" game with a blocky map and a lot of fancy sprite work to simulate 3D. Being a big fan of the old school GTAs, this would have been more than good enough for me. Once again, however, Rockstar had something better in mind; namely wowing us with a fully 3D game. Obviously the tiny DS can't render every little pothole/doorknob/finger/etc.. in 3D like GTA IV, but all the buildings, cars, peds, on down to street rubbish and signs, are made of polygons.

Naturally, the peds and cars are somewhat simple since they're so small, but on a triangle for triangle basis, the level of detail in the map is roughly comparable to GTA III. Given the size of the screen and the limitations of the hardware, this is truly impressive. On top of just being detailed, the map is huge. While they've tweaked and moved things around a bit so the scale would better match the action, the map is essentially the same Liberty City (minus Alderney) found in GTA IV. As such, the world should feel instantly familiar to anyone who has played IV, yet totally fresh at the same time, thanks in part to the slick cartoon shader. Oh, yea, and everything casts real-time shadows which move as the time of day changes.

Chinatown Wars ScreenshotClassic chaos.

But enough about the technical artsy stuff, how does it play? Well, after picking up our jaws off the floor following the demo on the giant DS, we all gathered around the table in the conference room to try it out for ourselves.

More : gtanet.com


ReadMore...

Saturday, March 07, 2009

(0) Comments

The New Internet Games in Windows 7

welcome to infomix.blogspot.com

If you’re active the Windows 7 Beta and hadn’t already noticed, we are introducing 3 new Windows Amateur for Windows 7.
Tired of Minesweeper? Definitely accord these amateur a try and accompany the bags of added players online who are arena too. I’m in fact absolutely absorbed to Internet Checkers.

Internet Backgammon:



internet_backgammon1

Internet Checkers:
internet_checkers1

Internet Spades:
internet_spades1



These amateur were able-bodied admired in Windows XP. Due to the bulk of requests we received, we are bringing them back!

The new versions of these amateur accept been absolutely redesigned and bigger over their old-school predecessors. With anniversary game, you can:

*

Play online adjoin animal opponents (up to 4 players at the aforementioned time with Internet Spades).

*

Select from 3 accomplishment levels to be akin with analogously accomplished players from all about the world.

*

If your adversary drops out, a computer adversary takes over to accomplishment the game.

*

Choose from air-conditioned altered backgrounds and piece/card designs.

And as I mentioned in my column about Amateur Explorer, you can clue statistics for amateur played as well.

Source : windowsteamblog.com
ReadMore...

Saturday, March 07, 2009

(0) Comments

Need for Speed Shift Screenshots

welcome to infomix.blogspot.com

Eight Need For Speed: Shift Screenshots Know abundant about Need for Speed: Shift? Neither do we! EA's appear eight new screenshots from the bold that we can boring longingly at, though.

Unlike abounding antecedent NFS amateur that emphasized artery antagonism and arcade-style driving, Need For Speed: Shift is a realistic, simulation racer. It's getting developed by GT Legends creators Slightly Mad Studios. It's set for a Fall 2009 absolution for PS3, Xbox 360, PC, and PSP.

Shift is one of several altered NFS titles currently in development. Also in the works is the arcade-style Need for Speed: Nitro, free-to-play Need for Speed: World Online, and an unannounced project.

Click to enlarge this screenshot


Click to enlarge this screenshot


Click to enlarge this screenshot

Click to enlarge this screenshot


Click to enlarge this screenshot

Added Wed 4th Mar 2009

Click to enlarge this screenshot


Click to enlarge this screenshot


Click to enlarge this screenshot

Added Mon 2nd Feb 2009

Click to enlarge this screenshot





ReadMore...

Friday, February 27, 2009

(0) Comments

Mario Forever

welcome to infomix.blogspot.com



Super Mario Forever is really a classic Mario remake. Once again you strap on your wrench and hardhat and guide the chubby plumber through many skillfully made levels. The diversity of the levels is very impressive. You will have to get through levels ranging from underwater caverns to levels filled with hot lava.

Download:
http://depositfiles.com/files/2354143
or
http://w13.easy-share.com/10075381.html





ReadMore...

Friday, February 27, 2009

(0) Comments

welcome to infomix.blogspot.com


Playability is central to the ethos of Premier Manager and 08 offers all football fans, young and old, the most accessible entry point to the management genre. Whether you want to start at the top or work your way up through the leagues, Premier Manager is the best choice for those looking to get their foot on the ladder.

Fresh, vibrant player interface increases the user friendly feel of Premier Manager. You are never more than a couple of clicks away from where you want to be in the game which gives you total control at all times. All new character bar allows easy access to key members of staff and replaces the need to process every news item before moving to the next day. This speeds up the flow and feel of the playing experience.

The home screen shows a dynamic snapshot of everything that is happening at your club, including stats, team and staff, news and finance. Premier Manager's updated database (up to summer 2007) will feature over 400 clubs and over 12,000 players. Improved match engine illustrates the effect of tactical and team changes in real-time.

Download:
http://rapidshare.com/files/50705067/premier_manager_08_rip_vace.rar

Pass:
banon
ReadMore...

Saturday, December 27, 2008

(0) Comments

Block Breaker (Breakout)

welcome to infomix.blogspot.com

What this tutorial covers

* The creation of a Breakout clone
* Loading level information from a file
* Building off of previously written code

Introduction

This tutorial will move faster than previous tutorials because you should already understand most of what's here from the Pong tutorial. Breakout really is just an extension of Pong. All we need to do is cut out the computer opponent and replace it with some blocks that will "break" when the ball hits them.

To make things a little more interesting, we'll be loading the locations of the blocks from .txt files. It's important to get used to loading data from external files because you'll be doing it like crazy for larger projects. Level data, model information, scripts, and generally all of the actual content in a game is loaded from external files. Not only does this allow you to load information from data files that were created by other programs (thus saving you from having to enter that information in by hand), this also saves you from having to recompile every time you need to change a value. If your program loads the data in at run-time, you just have to make the necessary changes to your files and re-run the program. No recompiling required.

Although we'll be building off of the code from the Pong tutorial, I think that it would get confusing if I just told you what changes to make. Instead, I'll be going through all of the code for the project and will let you know when we've already covered something. With that in mind, it's time to start a new project!
Getting started

Click Here if you need the files from the Introduction tutorial.

The first thing we need to do is start a new project called "Block Breaker" and copy in "Main.cpp" and "Defines.h" from the Introduction tutorial. Also remember to copy "SDL.dll", "SDL_ttf.dll", and "ARIAL.TTF" into you project directory and set Project->Block Breaker Properties->C/C++->Code Generation->Runtime Library to Multi-threaded DLL (/MD). You'll also need the bitmap for this tutorial, which can be found in the downloadable source code.
The Code
Defines.h

We'll be storing our blocks in an array, so we need to specify how many blocks we want when we intialize the array. We might want to change this value later, so we'll define it here.

Since the only real strategy in Breakout is to get the ball above the blocks (so it'll keep rebounding off of the "roof"), it's a good idea to always have space between the sides of the screen and the blocks. It's tedious to have to break through a bunch of blocks before you can get to the roof. We'll define the amount of space here.

The player will be given a certain amount of lives and will have to pass a certain amount of levels. We'll define that information here too.

The rest should be self-explanatory, so here's what you should add after #define FRAME_RATE 1000/FRAMES_PER_SECOND:

// Location of images within bitmap
#define PADDLE_BITMAP_X 0
#define PADDLE_BITMAP_Y 0
#define BALL_BITMAP_X 100
#define BALL_BITMAP_Y 0
#define YELLOW_X 0
#define YELLOW_Y 20
#define RED_X 0
#define RED_Y 40
#define BLUE_X 80
#define BLUE_Y 20
#define GREEN_X 80
#define GREEN_Y 40

// Minimum distance from the side of the screen to a block
#define BLOCK_SCREEN_BUFFER 40

// Maximum number of blocks allowed
#define MAX_BLOCKS 80

// Number of rows and columns of blocks
#define NUM_ROWS 6
#define NUMCOLS 9

// Location of the player's paddle in the game
#define PLAYER_Y 550

// Dimensions of a paddle
#define PADDLE_WIDTH 100
#define PADDLE_HEIGHT 20

// Dimensions of a block
#define BLOCK_WIDTH 80
#define BLOCK_HEIGHT 20

// Diameter of the ball
#define BALL_DIAMETER 20

// Paddle speed
#define PLAYER_SPEED 10

// Ball speeds
#define BALL_SPEED_MODIFIER 5 // divide location on paddle by this
#define BALL_SPEED_Y 10 // max speed of ball along y axis

// Maximum number of times the player can miss the ball
#define NUM_LIVES 5

// Number of levels, increase this value to add new levels
#define NUM_LEVELS 3

// Locations of output text
#define LIVES_X 5
#define LIVES_Y 5
#define LEVEL_X 75
#define LEVEL_Y 5
Includes

We'll use the std::string for building output strings as well as for building file names (covered later). For file I/O, we'll use fstream. I'll show you how to perform file I/O when we get to initializing our blocks. For now, add this to "Main.cpp":

#include // We'll use the STL string for text output and for our file names
#include // We need to read in our levels from files
Global Data

Although the Entity structure we used in the Pong tutorial would work here for the paddle and the ball, I decided to split them into two different structures. The structure we'll be using for our blocks will be slighty different and I didn't want to have one general struct (Entity) and one specific one (Block). Thought it might get confusing...

You should have no problem with the Ball and Paddle structures, so I'll just post them. The only change is that the Paddle struct no longer has a y_speed variable. The Ball struct is just the Entity struct with a new name. Add the following to "Main.cpp":

// The paddle only moves horizontally so there's no need for a y_speed variable
struct Paddle
{
SDL_Rect screen_location; // location on screen
SDL_Rect bitmap_location; // location of image in bitmap

int x_speed;
};

// The ball moves in any direction so we need to have two speed variables
struct Ball
{
SDL_Rect screen_location; // location on screen
SDL_Rect bitmap_location; // location of image in bitmap

int x_speed;
int y_speed;
};

The Block structure doesn't need speed variables but it does need to keep track of the number of times the block has been hit. The blocks will change color when they get hit until their hit count reaches zero. Here's the Block struct:

// The block just stores it's location and the amount of times it has been hit
struct Block
{
SDL_Rect screen_location; // location on screen
SDL_Rect bitmap_location; // location of image in bitmap

int num_hits; // "health"
};

For global data we need the player's paddle, the ball, the player's lives, the current level, the number of blocks, and an array to hold the blocks.

Paddle g_Player; // The player's paddle
Ball g_Ball; // The game ball
int g_Lives; // Player's lives
int g_Level = 1; // Current level
int g_NumBlocks = 0; // Keep track of number of blocks
Block g_Blocks[MAX_BLOCKS]; // The blocks we're breaking

Notice that we keep track of the number of blocks. Since the number of blocks in our game will vary, the block array will almost never be full. When we loop through the array to draw the blocks or detect collisions, we'll need to know how far into the array to look. We don't want to loop into a part of the array that's empty and try to work with something that isn't there.
Function Prototypes

These functions should look pretty familar by now, add them right after the prototype for Exit():

void GameWon();
void GameLost();

// Helper functions for the main game state functions
...
void HandleWinLoseInput();

You can also delete the prototype and definition for DrawBackground(), unless you want a background for your game.

For collision detection, we need a function for checking collisions between the ball and the player's paddle, and one for checking for collisions between the ball and the blocks. We'll also have a function that handles the event of a block being hit by the ball. This function will take the index of the block that's been hit as a parameter.

You'll notice that we no longer pass parameters to CheckBallCollisions(). This is because there's only one paddle now. You'll also notice the I've included a function that checks to see if a point is inside a rectangle. Since our collision detection is based on checking for bounding rectangles, this function should save us a lot of time.

bool CheckBallCollisions();
void CheckBlockCollisions();
void HandleBlockCollision(int index);
bool CheckPointInRect(int x, int y, SDL_Rect rect);

The following prototypes are straight out of the Pong tutorial with the exception of ChangeLevel() which...changes the level.

void HandleBall();
void MoveBall();
void HandleLoss();
void HandleWin();
void ChangeLevel();

The last function to add is InitBlocks(). This function will load the locations and hit counts of the blocks according to what level we're on. We'll call it at the beginning of the game, as well as when we change levels or restart the game.

void InitBlocks();
Init()

Everything in Init() has already been covered, so I'll just give you the code. The only real change here is that we call InitBlocks().

void Init()
{
// Initiliaze SDL video and our timer.
SDL_Init( SDL_INIT_VIDEO | SDL_INIT_TIMER);
// Setup our window's dimensions, bits-per-pixel (0 tells SDL to choose for us),
// and video format (SDL_ANYFORMAT leaves the decision to SDL). This function
// returns a pointer to our window which we assign to g_Window.
g_Window = SDL_SetVideoMode(WINDOW_WIDTH, WINDOW_HEIGHT, 0, SDL_ANYFORMAT);
// Set the title of our window.
SDL_WM_SetCaption(WINDOW_CAPTION, 0);
// Get the number of ticks since SDL was initialized.
g_Timer = SDL_GetTicks();

// Initialize the player's data

// screen locations
g_Player.screen_location.x = (WINDOW_WIDTH / 2) - (PADDLE_WIDTH / 2); // center screen
g_Player.screen_location.y = PLAYER_Y;
g_Player.screen_location.w = PADDLE_WIDTH;
g_Player.screen_location.h = PADDLE_HEIGHT;
// image location
g_Player.bitmap_location.x = PADDLE_BITMAP_X;
g_Player.bitmap_location.y = PADDLE_BITMAP_Y;
g_Player.bitmap_location.w = PADDLE_WIDTH;
g_Player.bitmap_location.h = PADDLE_HEIGHT;
// player speed
g_Player.x_speed = PLAYER_SPEED;
// lives
g_Lives = NUM_LIVES;

// Initialize the ball's data //

// screen location
g_Ball.screen_location.x = (WINDOW_WIDTH / 2) - (BALL_DIAMETER / 2); // center screen
g_Ball.screen_location.y = (WINDOW_HEIGHT / 2) - (BALL_DIAMETER / 2); // center screen
g_Ball.screen_location.w = BALL_DIAMETER;
g_Ball.screen_location.h = BALL_DIAMETER;
// image location
g_Ball.bitmap_location.x = BALL_BITMAP_X;
g_Ball.bitmap_location.y = BALL_BITMAP_Y;
g_Ball.bitmap_location.w = BALL_DIAMETER;
g_Ball.bitmap_location.h = BALL_DIAMETER;
// speeds
g_Ball.x_speed = 0;
g_Ball.y_speed = 0;

// We'll need to initialize our blocks for each level, so we have a
// separate function handle it
InitBlocks();

// Fill our bitmap structure with information.
g_Bitmap = SDL_LoadBMP("data/BlockBreaker.bmp");

// Set our transparent color (magenta)
SDL_SetColorKey( g_Bitmap, SDL_SRCCOLORKEY, SDL_MapRGB(g_Bitmap->format, 255, 0, 255) );

// We start by adding a pointer to our exit state, this way
// it will be the last thing the player sees of the game.
StateStruct state;
state.StatePointer = Exit;
g_StateStack.push(state);

// Then we add a pointer to our menu state, this will
// be the first thing the player sees of our game.
state.StatePointer = Menu;
g_StateStack.push(state);

// Initialize the true type font library.
TTF_Init();
}
InitBlocks()

Loading information from external files can be frustrating at times. If you try to perform the file I/O before you even know that your program works properly, it will be very hard to tell whether the bug is in your game code, your I/O code, or if there's something wrong with the file itself. When I first wrote the code for this tutorial, I used a loop to intialize the blocks to dummy values and made sure that the code worked. I then went ahead and started loading level information from a file.

So what information are we going to load from a file? For this project, we'll only be loading the number of hits our blocks can take (i.e. their health). We'll store this information in .txt files. Here's what "level1.txt", the file for the first level, looks like:

0 3 3 3 3 3 3 3 0
2 0 3 3 3 3 3 0 2

2 2 0 3 3 3 0 2 2
2 2 2 0 3 0 2 2 2
2 2 2 2 0 2 2 2 2
2 2 2 0 4 0 2 2 2

Our game area will consist of 6 rows and 9 columns of blocks. Each block will be a certain color that depends on how many hits it has left. When we read in these values, we'll skip any blocks that have been given a zero for their hit count.

You'll notice that there are spaces between each number in "level1.txt". When we read in a value, we'll use the overloaded >> operator, which will read a string of characters from our file until it reaches a space or new line character. We only want it to read one character at a time, so we put spaces between each number.

The first thing we need to do in InitBlocks() is declare a file stream object. This is the object that contains the functions we need to carry out our I/O. We then construct a string according to what level the player is on. This code looks a lot like what we did for outputting the player and computer scores in the Pong tutorial, only now we're constructing a string for the name of a file. After that, a call to fstream's open() function will allow us to begin reading information from our file. Add the following code to "Main.cpp":

void InitBlocks()
{
fstream inFile;

// The following code creates a string storing the proper file name. If
// g_Level = 1, we get: "data\\level" + "1" + ".txt" = "data\\level1.txt"
char level_num[256]; // for itoa
string file_name = "data\\level"; // the file will always start with "level"
itoa(g_Level, level_num, 10); // convert g_Level to a string
file_name.append(level_num); // append the level number
file_name.append(".txt"); // we'll just use txt's for our levels

// Open the file for input. Note that this function takes a
// char* so we need to use the std::string's c_str() function.
// ios::in specifies that we want to read from this file.
inFile.open(file_name.c_str(), ios::in);

We now loop through each block in our game and read in its hit count from the file. We'll be using nested for loops for this. The outside loop will be for the rows of blocks, the inside loop will be for the columns. We'll set the location of the current block being intialized according to where we are in in the loop. Note that we'll also need a variable to keep track of what block we're at in the array. We'll call this variable index. Here's the code for the start of the loop:

int index = 0; // used to index blocks in g_Blocks array

// Temporary variable to hold the number we read in from our file
int temp_hits;

// Iterate through each row and column of our blocks
for (int row=1; row<=NUM_ROWS; row++)
{
for (int col=1; col<=NUMCOLS; col++)
{

We now read in the next value from our file. We'll store this value in temp_hits so we can make sure it's not zero. If it is, we'll just skip the current block. There's no need to store blocks with zero hits left. If the hit count isn't zero, we intialize the block's num_hits variable as well as it's location. Notice that we apply BLOCK_SCREEN_BUFFER to make sure our blocks are always a specific distance from the sides of the screen.

// Read the next value into temp_hits
inFile >> temp_hits;

// If temp_hits is zero, we go on to the next block
if (temp_hits != 0)
{
g_Blocks[index].num_hits = temp_hits;

// We set the location of the block according to what row and column
// we're on in our loop. Notice that we use BLOCK_SCREEN_BUFFER to set
// the blocks away from the sides of the screen.
g_Blocks[index].screen_location.x = col*BLOCK_WIDTH - BLOCK_SCREEN_BUFFER;
g_Blocks[index].screen_location.y = row*BLOCK_HEIGHT + BLOCK_SCREEN_BUFFER;
g_Blocks[index].screen_location.w = BLOCK_WIDTH;
g_Blocks[index].screen_location.h = BLOCK_HEIGHT;
g_Blocks[index].bitmap_location.w = BLOCK_WIDTH;
g_Blocks[index].bitmap_location.h = BLOCK_HEIGHT;

Now we set the color of the block according to its hit count. A switch statement handles this process nicely. With that done, we increment the index variable as well as g_NumBlocks. Remember that every time we add a block we have to increment g_Numblocks and we have to decrement it every time we remove a block.

// Now we set the bitmap location rect according to num_hits
switch (g_Blocks[index].num_hits)
{
case 1:
{
g_Blocks[index].bitmap_location.x = YELLOW_X;
g_Blocks[index].bitmap_location.y = YELLOW_Y;
} break;
case 2:
{
g_Blocks[index].bitmap_location.x = RED_X;
g_Blocks[index].bitmap_location.y = RED_Y;
} break;
case 3:
{
g_Blocks[index].bitmap_location.x = GREEN_X;
g_Blocks[index].bitmap_location.y = GREEN_Y;
} break;
case 4:
{
g_Blocks[index].bitmap_location.x = BLUE_X;
g_Blocks[index].bitmap_location.y = BLUE_Y;
} break;
}

// For future use, keep track of how many blocks we have.
g_NumBlocks++;
index++; // move to next block
}
}
}

With our loop completed, we just have to call close() to tell fstream that we are done reading from the file.

inFile.close();
}
Game()

We actually don't need to make that many changes to Game(). As with the Pong tutorial, we need to make a call to HandleBall(). Add the line

HandleBall();

just below the call to HandleGameInput().

Now we need to draw the ball, paddle, and blocks. For the blocks, we iterate through g_Blocks, drawing each block. Add the following after the call to ClearScreen():

// Draw the paddle and the ball
SDL_BlitSurface(g_Bitmap, &g_Player.bitmap_location, g_Window,
&g_Player.screen_location);
SDL_BlitSurface(g_Bitmap, &g_Ball.bitmap_location, g_Window,
&g_Ball.screen_location);

// Iterate through the blocks array, drawing each block
for (int i=0; i{
SDL_BlitSurface(g_Bitmap, &g_Blocks[i].bitmap_location, g_Window,
&g_Blocks[i].screen_location);
}

All that's left is to display the current level and the number of lives the player has left. This code should look very familar to you:

// Output the number of lives the player has left and the current level
char buffer[256];

string lives = "Lives: ";
itoa(g_Lives, buffer, 10);
lives.append(buffer);

string level = "Level: ";
itoa(g_Level, buffer, 10);
level.append(buffer);

DisplayText(lives, LIVES_X, LIVES_Y, 12, 66, 239, 16, 0, 0, 0);
DisplayText(level, LEVEL_X, LEVEL_Y, 12, 66, 239, 16, 0, 0, 0);
GameWon() and GameLost()

Since we're using the code from the Introduction tutorial, we need to add GameWon(), GameLost(), and HandleWinLoseInput(). Instead of making you copy them out of the Pong tutorial, I'll just give you the code here:

// Display a victory message.
void GameWon()
{
if ( (SDL_GetTicks() - g_Timer) >= FRAME_RATE )
{
HandleWinLoseInput();

ClearScreen();

DisplayText("You Win!!!", 350, 250, 12, 255, 255, 255, 0, 0, 0);
DisplayText("Quit Game (Y or N)?", 350, 270, 12, 255, 255, 255, 0, 0, 0);

SDL_UpdateRect(g_Window, 0, 0, 0, 0);

g_Timer = SDL_GetTicks();
}
}

// Display a game over message.
void GameLost()
{
if ( (SDL_GetTicks() - g_Timer) >= FRAME_RATE )
{
HandleWinLoseInput();

ClearScreen();

DisplayText("You Lose.", 350, 250, 12, 255, 255, 255, 0, 0, 0);
DisplayText("Quit Game (Y or N)?", 350, 270, 12, 255, 255, 255, 0, 0, 0);

SDL_UpdateRect(g_Window, 0, 0, 0, 0);

g_Timer = SDL_GetTicks();
}
}

// Input handling for win/lose screens.
void HandleWinLoseInput()
{
if ( SDL_PollEvent(&g_Event) )
{
// Handle user manually closing game window
if (g_Event.type == SDL_QUIT)
{
// While state stack isn't empty, pop
while (!g_StateStack.empty())
{
g_StateStack.pop();
}

return;
}

// Handle keyboard input here
if (g_Event.type == SDL_KEYDOWN)
{
if (g_Event.key.keysym.sym == SDLK_ESCAPE)
{
g_StateStack.pop();

return;
}
if (g_Event.key.keysym.sym == SDLK_y)
{
g_StateStack.pop();
return;
}
// If player chooses to continue playing, we pop off
// current state and push exit and menu states back on.
if (g_Event.key.keysym.sym == SDLK_n)
{
g_StateStack.pop();

StateStruct temp;
temp.StatePointer = Exit;
g_StateStack.push(temp);

temp.StatePointer = Menu;
g_StateStack.push(temp);
return;
}
}
}
}
HandleGameInput()

HandleGameInput() is going to look just like the one from the Pong tutorial, only we now want to check for collisions with the sides of the walls here. Remember that we got rid of HandleWallCollisions() because it took an Entity as a parameter but we split the ball and paddle into different structures. We could write two functions, one for the ball and one for the paddle, but it really wouldn't save us any coding. There's nothing new here, so I have to dump some more code on you. Don't worry, we're almost at the new stuff.

void HandleGameInput()
{
static bool left_pressed = false;
static bool right_pressed = false;

// Fill our event structure with event information.
if ( SDL_PollEvent(&g_Event) )
{
// Handle user manually closing game window
if (g_Event.type == SDL_QUIT)
{
// While state stack isn't empty, pop
while (!g_StateStack.empty())
{
g_StateStack.pop();
}

return; // game is over, exit the function
}

// Handle keyboard input here
if (g_Event.type == SDL_KEYDOWN)
{
if (g_Event.key.keysym.sym == SDLK_ESCAPE)
{
g_StateStack.pop();

return; // this state is done, exit the function
}
if (g_Event.key.keysym.sym == SDLK_SPACE)
{
// Player can hit 'space' to make the ball move at start
if (g_Ball.y_speed == 0)
g_Ball.y_speed = BALL_SPEED_Y;
}
if (g_Event.key.keysym.sym == SDLK_LEFT)
{
left_pressed = true;
}
if (g_Event.key.keysym.sym == SDLK_RIGHT)
{
right_pressed = true;
}
}
if (g_Event.type == SDL_KEYUP)
{
if (g_Event.key.keysym.sym == SDLK_LEFT)
{
left_pressed = false;
}
if (g_Event.key.keysym.sym == SDLK_RIGHT)
{
right_pressed = false;
}
}
}

// This is where we actually move the paddle
if (left_pressed)
{
// Notice that we do this here now instead of in a separate function
if ( (g_Player.screen_location.x - PLAYER_SPEED) >= 0 )
{
g_Player.screen_location.x -= PLAYER_SPEED;
}
}
if (right_pressed)
{
if ( (g_Player.screen_location.x + PLAYER_SPEED) <= WINDOW_WIDTH )
{
g_Player.screen_location.x += PLAYER_SPEED;
}
}
}
CheckBallCollisions(), HandleBall(), and MoveBall()

You're probably sick of copy-pasting by now, so let's get the rest over with. CheckBallCollisions() just handles collisions with the player's paddle now. There's no need to pass it any parameters because there's only one paddle. HandleBall() is the exact same except for a call to HandleBlockCollisions() at the end.

MoveBall() has changed a bit more. Since we got rid of HandleWallCollissions(), we check for wall collisions here. We also now check for collisions with the top of the screen so the ball doesn't disappear into the abyss. If the ball passes the player, we reset it as before and then decrement the player's lives. If the player has zero lives left, we call HandleLoss().

// Check to see if the ball is going to hit the paddle
bool CheckBallCollisions()
{
// Temporary values to keep things tidy
int ball_x = g_Ball.screen_location.x;
int ball_y = g_Ball.screen_location.y;
int ball_width = g_Ball.screen_location.w;
int ball_height = g_Ball.screen_location.h;
int ball_speed = g_Ball.y_speed;

int paddle_x = g_Player.screen_location.x;
int paddle_y = g_Player.screen_location.y;
int paddle_width = g_Player.screen_location.w;
int paddle_height = g_Player.screen_location.h;

// Check to see if ball is in Y range of the player's paddle.
// We check its speed to see if it's even moving towards the player's paddle.
if ( (ball_speed > 0) && (ball_y + ball_height >= paddle_y) &&
(ball_y + ball_height <= paddle_y + paddle_height) ) // side hit
{
// If ball is in the X range of the paddle, return true.
if ( (ball_x <= paddle_x + paddle_width) && (ball_x + ball_width >= paddle_x) )
{
return true;
}
}

return false;
}

void HandleBall()
{
// Start by moving the ball
MoveBall();

if ( CheckBallCollisions() )
{
// Get center location of paddle
int paddle_center = g_Player.screen_location.x + g_Player.screen_location.w / 2;
int ball_center = g_Ball.screen_location.x + g_Ball.screen_location.w / 2;

// Find the location on the paddle that the ball hit
int paddle_location = ball_center - paddle_center;

// Increase X speed according to distance from center of paddle.
g_Ball.x_speed = paddle_location / BALL_SPEED_MODIFIER;
g_Ball.y_speed = -g_Ball.y_speed;
}

// Check for collisions with blocks
CheckBlockCollisions();
}

void MoveBall()
{
g_Ball.screen_location.x += g_Ball.x_speed;
g_Ball.screen_location.y += g_Ball.y_speed;

// If the ball is moving left, we see if it hits the wall. If does,
// we change its direction. We do the same thing if it's moving right.
if ( ( (g_Ball.x_speed < 0) && (g_Ball.screen_location.x <= 0) ) ||
( (g_Ball.x_speed > 0) && (g_Ball.screen_location.x >= WINDOW_WIDTH) ) )
{
g_Ball.x_speed = -g_Ball.x_speed;
}

// If the ball is moving up, we should check to see if it hits the 'roof'
if ( (g_Ball.y_speed < 0) && (g_Ball.screen_location.y <= 0) )
{
g_Ball.y_speed = -g_Ball.y_speed;
}

// Check to see if ball has passed the player
if ( g_Ball.screen_location.y >= WINDOW_HEIGHT )
{
g_Lives--;

g_Ball.x_speed = 0;
g_Ball.y_speed = 0;

g_Ball.screen_location.x = WINDOW_WIDTH/2 - g_Ball.screen_location.w/2;
g_Ball.screen_location.y = WINDOW_HEIGHT/2 - g_Ball.screen_location.h/2;

if (g_Lives == 0)
{
HandleLoss();
}
}
}
CheckBlockCollisions()

When we checked for collisions between the ball and the player's paddle, we checked to see if any of the four corners of the ball had made contact with the paddle. This worked fine since we knew that the ball would always hit the top of the paddle.

Things are slightly different with the blocks though. We want the ball to hit the top and bottom of the blocks, and we also want it to hit the sides. More importantly, we need to be able to tell whether the ball has hit a side or the top/bottom of a block. Obviously if the ball hits the top/bottom of a block we want to change its vertical direction, and if it hits a side of a block we want to change its horizontal direction.

If we handle collisions by checking the corners of the ball, we'll have no idea which direction we need to deflect the ball. Think about the top-right corner of the ball hitting a block. How can we tell if it hit the block from the side, the top, or both?

The simplest solution to this problem is to check the middle points of each side of the ball. If the middle point of the left side of the ball hits something, we can be pretty confident that we need to deflect the ball to the right. To save on typing, we'll store these values in temporary variables. Here's what we have so far:

void CheckBlockCollisions()
{
// Temporary values to save on typing
int left_x = g_Ball.screen_location.x;
int left_y = g_Ball.screen_location.y + g_Ball.screen_location.h/2;
int right_x = g_Ball.screen_location.x + g_Ball.screen_location.w;
int right_y = g_Ball.screen_location.y + g_Ball.screen_location.h/2;
int top_x = g_Ball.screen_location.x + g_Ball.screen_location.w/2;
int top_y = g_Ball.screen_location.y;
int bottom_x = g_Ball.screen_location.x + g_Ball.screen_location.w/2;
int bottom_y = g_Ball.screen_location.y + g_Ball.screen_location.h;

Now we need to loop through the blocks and check for collisions. One thing to note is that the ball might hit more than one block. If the top/bottom of the ball hits a block and the left/right side of the ball hits another block, we'll need to deflect it vertically and horizontally. Because of this, we'll loop through the entire array and keep track of any sides of the ball that have hit something. When we've checked every block, we'll make the appropriate changes to the ball's direction. Here's our ball-to-block collision detection loop:

for (int block=0; block {
// top
if ( CheckPointInRect(top_x, top_y, g_Blocks[block].screen_location) )
{
top = true;
HandleBlockCollision(block);
}
// bottom
if ( CheckPointInRect(bottom_x, bottom_y, g_Blocks[block].screen_location) )
{
bottom = true;
HandleBlockCollision(block);
}
// left
if ( CheckPointInRect(left_x, left_y, g_Blocks[block].screen_location) )
{
left = true;
HandleBlockCollision(block);
}
// right
if ( CheckPointInRect(right_x, right_y, g_Blocks[block].screen_location) )
{
right = true;
HandleBlockCollision(block);
}
}

Note the use of CheckPointInRect(). We'll get to its implementation soon. All it does is return true if the given point is inside the given rect. HandleBlockCollision() takes care of removing the block. We'll go through its implementation next.

Now it's time to handle deflecting the ball. This should be pretty self-explanatory. You'll notice that we also move the ball by BALL_SPEED in the direction that we're deflecting it. This is to make sure that the ball isn't inside the block after the function finishes.

if (top)
{
g_Ball.y_speed = -g_Ball.y_speed;
g_Ball.screen_location.y += BALL_DIAMETER;
}
if (bottom)
{
g_Ball.y_speed = -g_Ball.y_speed;
g_Ball.screen_location.y -= BALL_DIAMETER;
}
if (left)
{
g_Ball.x_speed = -g_Ball.x_speed;
g_Ball.screen_location.x += BALL_DIAMETER;
}
if (right)
{
g_Ball.x_speed = -g_Ball.x_speed;
g_Ball.screen_location.x -= BALL_DIAMETER;
}
}
HandleBlockCollision()

HandleBlockCollision() takes the index to the block that has been hit and handles changing the block's hit count. If the block's hit count reaches zero, we remove that block from the array.

Since we don't need to worry about the order of blocks in the array, we can just copy the last valid block in the array (located at g_Blocks[g_NumBlocks-1]) over top of the block at index. This effectively removes the block from the array while saving us from having to shift everything around. Note that we have to decrement g_NumBlocks so we don't access any blocks twice.

void HandleBlockCollision(int index)
{
g_Blocks[index].num_hits--;

// If num_hits is 0, the block needs to be erased
if (g_Blocks[index].num_hits == 0)
{
g_Blocks[index] = g_Blocks[g_NumBlocks-1];
g_NumBlocks--;

Everytime we remove a block, we need to see if it was the last block in the level. If it is, we change levels with a call to ChangeLevel().

// Check to see if it's time to change the level
if (g_NumBlocks == 0)
{
ChangeLevel();
}
}

If the block's hit count hasn't reached zero, we just have to change its color. This is done with almost the same switch statement as in InitBlocks(). The only difference is that we don't have to handle the block having 4 hits left.

// If the hit count hasn't reached zero, we need to change the block's color
else
{
switch (g_Blocks[index].num_hits)
{
case 1:
{
g_Blocks[index].bitmap_location.x = YELLOW_X;
g_Blocks[index].bitmap_location.y = YELLOW_Y;
} break;
case 2:
{
g_Blocks[index].bitmap_location.x = RED_X;
g_Blocks[index].bitmap_location.y = RED_Y;
} break;
case 3:
{
g_Blocks[index].bitmap_location.x = GREEN_X;
g_Blocks[index].bitmap_location.y = GREEN_Y;
} break;
}
}
}
CheckPointInRect()

This function returns true if the given point is within the given rect. The algorithm is the same as we've been using in all of the tutorials. We're just putting it into its own function so we don't have to type long if statements anymore.

bool CheckPointInRect(int x, int y, SDL_Rect rect)
{
if ( (x >= rect.x) && (x <= rect.x + rect.w) &&
(y >= rect.y) && (y <= rect.y + rect.h) )
{
return true;
}

return false;
}
ChangeLevel()

To change the level, we first need to increment g_Level. We then check to see if the player just finished the last level, in which case we call HandleWin(). Otherwise, we reset the ball, set g_NumBlocks to zero, and call InitBlocks(). InitBlocks() will load the necessary data from the next level file.

void ChangeLevel()
{
g_Level++;

// Check to see if the player has won
if (g_Level > NUM_LEVELS)
{
HandleWin();
return;
}

// Reset the ball
g_Ball.x_speed = 0;
g_Ball.y_speed = 0;

g_Ball.screen_location.x = WINDOW_WIDTH/2 - g_Ball.screen_location.w/2;
g_Ball.screen_location.y = WINDOW_HEIGHT/2 - g_Ball.screen_location.h/2;

g_NumBlocks = 0; // Set this to zero before calling InitBlocks()
InitBlocks(); // InitBlocks() will load the proper level
}
HandleLoss() and HandleWin()

These two functions are almost identical. They first pop all of the states off of the state stack. They then reset the ball, player's lives, level, block count, and blocks in case the player chooses to restart the game. Finally, they push the appropriate state onto the stack.

void HandleLoss()
{
while ( !g_StateStack.empty() )
{
g_StateStack.pop();
}

g_Ball.x_speed = 0;
g_Ball.y_speed = 0;

g_Ball.screen_location.x = WINDOW_WIDTH/2 - g_Ball.screen_location.w/2;
g_Ball.screen_location.y = WINDOW_HEIGHT/2 - g_Ball.screen_location.h/2;

g_Lives = NUM_LIVES;
g_NumBlocks = 0;
g_Level = 1;
InitBlocks();

StateStruct temp;
temp.StatePointer = GameLost;
g_StateStack.push(temp);

}

void HandleWin()
{
while ( !g_StateStack.empty() )
{
g_StateStack.pop();
}

g_Ball.x_speed = 0;
g_Ball.y_speed = 0;

g_Ball.screen_location.x = WINDOW_WIDTH/2 - g_Ball.screen_location.w/2;
g_Ball.screen_location.y = WINDOW_HEIGHT/2 - g_Ball.screen_location.h/2;

g_Lives = NUM_LIVES;
g_NumBlocks = 0;
g_Level = 1;
InitBlocks();

StateStruct temp;
temp.StatePointer = GameWon;
g_StateStack.push(temp);
}
Conclusion

That's all for this tutorial. Breakout and Pong are very similar, so there really wasn't a lot of new stuff to cover. The most important thing we did here was set up our game to read level information from external files. If you want to change the levels, you can just load the .txt files in notepad and change them around. If you want to add new levels, just create more .txt files and change NUM_LEVELS.
ReadMore...

Saturday, December 27, 2008

(0) Comments

Paddle Battle

welcome to infomix.blogspot.com

What this tutorial covers

* The creation of a Pong clone
* Simulating intelligence with random logic
* Bouncing a circle back and forth between two digital sticks

Introduction

Before we get to the code for this project I'd like to discuss what's going on in the game. Right now would be a good time to get the .zip file for this tutorial and try the game out. You'll notice that when you run the program you get a whole lot of Pong. Your paddle is on the bottom and the AI's paddle is at the top. If you hit the space bar, the ball will start moving and you and the computer will be given the opportunity to display your wit, reflexes, and ability with the arrow keys.

The scores are shown in the top right of the screen and are colored green just like everything else (thought we could simulate the good old days). When the ball makes contact with a paddle, it is reflected at an angle according to the part of the paddle that it hit. Some Pong clones have a bit more complicated system but I like this one, it gives you total control over where the ball will go.

One last thing to note is that the paddles can't go beyond the sides of the screen and obviously can't move up or down.
Getting started

If you're coming from the old introduction and Tetris tutorials, you should download the new introduction tutorial code, Click Here. The only big difference is the ClearScreen() function. All it does is clears the screen by drawing a black rectangle over everything using the SDL_FillRect() function.

The first thing we need to do is start a new project called "Paddle Battle" and copy in "Main.cpp" and "Defines.h" from the Introduction tutorial. Also remember to copy "SDL.dll", "SDL_ttf.dll", and "ARIAL.TTF" into you project directory and set Project->Paddle Battle Properties->C/C++->Code Generation->Runtime Library to Multi-threaded DLL (/MD). You'll also need the bitmap for this tutorial, which can be found in the downloadable source code.

Since you can win and lose in Pong, just like in Tetris, we'll just copy the GameWon() and GameLost() functions from the previous tutorial. We'll also need to copy in HandleWinLoseInput(). Do that now, and don't forget to get the prototypes as well as the implementations.
The Code
Defines.h

As usual, we'll start our project by setting up our external files. Our game window will now be 800x600 and our caption definately needs to change so replace the previous code with the following:

#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600
#define WINDOW_CAPTION "Paddle Battle"

Now let's define the locations of our game objects within the bitmap file. Add the following to "Defines.h":

// Location of images within bitmap
#define PADDLE_BITMAP_X 0
#define PADDLE_BITMAP_Y 0
#define BALL_BITMAP_X 100
#define BALL_BITMAP_Y 0

Although the paddles move left and right, they'll never move up and down. For this reason, we can safely consider their distances from the top of the screen to be constant. It will be helpful to have these values for detecting collisions with the ball so we'll define them here. We should also define the dimensions of the ball and paddles. Add the following to "Defines.h":

// Locations of paddles in the game
#define COMPUTER_Y 30
#define PLAYER_Y 550

// Dimensions of a paddle
#define PADDLE_WIDTH 100
#define PADDLE_HEIGHT 20

// Diameter of the ball
#define BALL_DIAMETER 20

The two paddles in the game will move at a constant speed. Although we'll have them both move at the same speed (making the computer move faster is cheap!), we'll have two values in case we want to change one of them later.

The speed at which the ball moves vertically will be constant. We'll determine its horizontal speed according to where it hits a paddle. If it hits the edge of a paddle (located 50 pixels from the paddle's center) we'll set its horizontal speed to 10. If it hits the center of the paddle, we'll set its horizontal speed to 0. Note that we can divide the distance from the paddle's center by 5 to get the speed we want. We'll define this value here in case we decide to change it later. Add the following to "Defines.h":

// Paddle speeds
#define PLAYER_SPEED 10
#define COMPUTER_SPEED 10

// Ball speeds
#define BALL_SPEED_MODIFIER 5 // divide location on paddle by this
#define BALL_SPEED_Y 10 // max speed of ball along y axis

The last thing we need to specify here is the locations on the screen where we will display the scores. Add the following to "Defines.h":

// Location of output text
#define COMPUTER_SCORE_X 10
#define COMPUTER_SCORE_Y 10
#define PLAYER_SCORE_X 150
#define PLAYER_SCORE_Y 10
Enums.h

Our enumerations file will be very small for this project. It just has the Direction enumeration from the Falling Blocks tutorial, with UP added to it. Add a file to your project named "Enums.h" and add the following lines of code to it:

#pragma once

enum Direction
{
LEFT,
RIGHT,
UP,
DOWN
};
Includes

The includes for our project will be the same as in the Falling Blocks tutorial only without #include and #include "cBlock.h". Your includes list should look like the following:

#include // We'll use the STL stack to store our function pointers
#include "SDL.h" // Main SDL header
#include "SDL_TTF.h" // True Type Font header
#include "math.h" // We'll be using abs()
#include "time.h" // For seeding the random generator
#include "Defines.h" // Our defines header
#include "Enums.h" // Our enums header

Although we could create some classes to represent our game objects, we can easily get away with just using a single struct. There is only one ball and two paddles, and they each only need to store their speeds and locations. We really don't need to treat them as separate entities. We can just have one struct, which we'll name Entity, that stores the locations and speeds of our game objects.

There are two different locations that we need to store however. First, we need to store the location of the object within the game area. Second, we need to store the location of the object's image within our bitmap. Notice that both of these locations happen to be values that we pass as SDL_Rect structures to SDL functions (SDL_BlitSurface() and SDL_LoadBMP()). Instead of storing the x, y, width and height values separately and creating SDL_Rect structures with these values every time we want to display them, it would be very convenient to always store them as SDL_Rect's. For this reason, our struct will contain two SDL_Rect variables.

Add the following code to "Main.cpp":

// Struct that represents an entity in our game
struct Entity
{
SDL_Rect screen_location; // location on screen
SDL_Rect bitmap_location; // location of image in bitmap

int x_speed;
int y_speed;
};
Global Variables

For global data, all we need is three Entity variables and two integers to keep track of the scores. Add the following to the global data section in "Main.cpp":

Entity g_Computer; // The computer's paddle
Entity g_Player; // The player's paddle
Entity g_Ball; // The game ball
int g_ComputerScore; // AI's score
int g_PlayerScore; // Player's score
Function Prototypes

There are two types of collisions that can happen in our game. The ball or one of the paddles can hit the side of the screen, or the ball can hit one of the paddles. Because our ball and paddles are of the same type, we don't need to do any function overloading here. Our CheckWallCollisions() function will take the entity to check and its direction, and our CheckBallCollisions() function will take a reference to one of our paddles (whichever one the ball is moving towards).

Note that we'll pass our game objects to our functions by reference. If you don't understand this concept, see my Pointers Tutorial.

Add the following to "Main.cpp":

bool CheckWallCollisions(Entity& entity, Direction dir);
bool CheckBallCollisions(Entity& paddle);

The ball and the computer's paddle are never controlled by the player. This means we need to program their behavior, so we should prototype some functions to handle this. For the ball, we'll have a function that determines if the ball has hit one of the paddles or if it has passed one of the paddles. We'll also have a function that handles actually moving the ball. We'll just have one function for the computer's paddle. Add the following to "Main.cpp":

void HandleBall();
void MoveBall();
void HandleAI();

When the ball passes one of the paddles, we need to increase the score of whoever has scored and see if they have scored enough to win. We also need to reset the ball to the center of the screen. We'll have two functions that handle this, one for each paddle. Add the following to "Main.cpp":

void HandlePlayerScore();
void HandleComputerScore();

In the last two tutorials we had a function called DrawBackground(). There is no background in Pong so we can erase that function from our project.
Init()

The first thing we need to add to Init() is a call to srand(). If you remember from the last tutorial, this function gives our random number generator a different value to work with. This prevents rand() from always giving us the same pattern of numbers. Add the following line to Init():

srand( time(0) );

Notice that we seed our generator with the current time.

Now we need to initialize the data in our entities. This is really straight forward but kinda long so feel free to just copy-paste this code. Add the following to Init():

// Initialize the screen locations of two paddles and the ball
g_Computer.screen_location.x = (WINDOW_WIDTH / 2) - (PADDLE_WIDTH / 2); // center screen
g_Computer.screen_location.y = COMPUTER_Y;
g_Computer.screen_location.w = PADDLE_WIDTH;
g_Computer.screen_location.h = PADDLE_HEIGHT;

g_Player.screen_location.x = (WINDOW_WIDTH / 2) - (PADDLE_WIDTH / 2); // center screen
g_Player.screen_location.y = PLAYER_Y;
g_Player.screen_location.w = PADDLE_WIDTH;
g_Player.screen_location.h = PADDLE_HEIGHT;

g_Ball.screen_location.x = (WINDOW_WIDTH / 2) - (BALL_DIAMETER / 2); // center screen
g_Ball.screen_location.y = (WINDOW_HEIGHT / 2) - (BALL_DIAMETER / 2); // center screen
g_Ball.screen_location.w = BALL_DIAMETER;
g_Ball.screen_location.h = BALL_DIAMETER;

// Initialize the image location rects
g_Computer.bitmap_location.x = PADDLE_BITMAP_X;
g_Computer.bitmap_location.y = PADDLE_BITMAP_Y;
g_Computer.bitmap_location.w = PADDLE_WIDTH;
g_Computer.bitmap_location.h = PADDLE_HEIGHT;

g_Player.bitmap_location.x = PADDLE_BITMAP_X;
g_Player.bitmap_location.y = PADDLE_BITMAP_Y;
g_Player.bitmap_location.w = PADDLE_WIDTH;
g_Player.bitmap_location.h = PADDLE_HEIGHT;

g_Ball.bitmap_location.x = BALL_BITMAP_X;
g_Ball.bitmap_location.y = BALL_BITMAP_Y;
g_Ball.bitmap_location.w = BALL_DIAMETER;
g_Ball.bitmap_location.h = BALL_DIAMETER;

// Initialize speeds
g_Computer.x_speed = COMPUTER_SPEED;
g_Player.x_speed = PLAYER_SPEED;
g_Ball.x_speed = 0;
g_Ball.y_speed = 0;

// Set scores to zero
g_ComputerScore = 0;
g_PlayerScore = 0;

Now we need to load the proper bitmap. We just need to use SDL_LoadBMP() for this as usual but there is one new thing we'll be doing here. Because our game objects aren't perfect squares, we're going to end up with parts of our surfaces that we want to be transparent. To do this, we'll choose a color that we want to be transparent and tell SDL to never draw that color when we draw something from our bitmap. By telling SDL to never draw a certain color, we effectively make it an invisible color.

We accomplish this by calling SDL_SetColorKey(), which takes the surface that we're setting a transparent color for and the color that we want to be transparent. We need to pass this color in as a color structure, which we'll get with a call to SDL_MapRGB(). This function takes the color format (bits per color) and the numerical values of the red, green, and blue components of the color. The SDL_Surface structure stores the color format of the bitmap, so we'll use the format of our bitmap.

Add the following to Init():

// Set our transparent color (magenta)
SDL_SetColorKey( g_Bitmap, SDL_SRCCOLORKEY,
SDL_MapRGB(g_Bitmap->format, 255, 0, 255) );

Note that (255, 0, 255) specifies magenta. I've never seen this color used in a game, so it's always the color I choose to be transparent. Also notice the SDL_SRCCOLORKEY parameter. API functions always take parameters like this and a lot of the times you really don't need to worry about what they're for. This is one of those parameters. When you get to bigger APIs like OpenGL or DirectX, you'll waste a lot of time if you hopelessly try to memorize what all of these parameters are for.
Menu(), Exit(), GameWon(), and GameLost()

Aside from Game(), our state functions don't really change (we'll get to Game() in a sec). The only thing we need to do is change the location of the text we display because we changed the size of our window. Replace the existing lines of text output code in the four state functions with the following:

void Menu()
{
...

DisplayText("Start (G)ame", 350, 250, 12, 255, 255, 255, 0, 0, 0);
DisplayText("(Q)uit Game", 350, 270, 12, 255, 255, 255, 0, 0, 0);

....

}

void Exit()
{
...

DisplayText("Quit Game (Y or N)?", 350, 260, 12, 255, 255, 255, 0, 0, 0);

....

}

void GameWon()
{
...

DisplayText("You Win!!!", 350, 250, 12, 255, 255, 255, 0, 0, 0);
DisplayText("Quit Game (Y or N)?", 350, 270, 12, 255, 255, 255, 0, 0, 0);

....

}

void GameLost()
{
...

DisplayText("You Lose.", 350, 250, 12, 255, 255, 255, 0, 0, 0);
DisplayText("Quit Game (Y or N)?", 350, 270, 12, 255, 255, 255, 0, 0, 0);

....

}
Game()

At the beginning of each frame of our game, we need to call HandleBall() and HandleAI(). This will take care of most of our game's functionality. Add the following to Game(), just below the call to HandleGameInput():

HandleBall();
HandleAI();

Since the previous two functions, along with HandleGameInput(), handle most of our game, all we need to do now is draw everything. Because we stored the bitmap and screen locations of our objects in the Entity structure, we no longer have to build SDL_Rect structures to display our game objects. Aside from that, everything here should be fairly familiar to you by now. Remove the call to DrawBackground() and add the following to Game(), just below the call to ClearScreen():

// Draw the two paddles and the ball
SDL_BlitSurface(g_Bitmap, &g_Computer.bitmap_location, g_Window,
&g_Computer.screen_location);
SDL_BlitSurface(g_Bitmap, &g_Player.bitmap_location, g_Window,
&g_Player.screen_location);
SDL_BlitSurface(g_Bitmap, &g_Ball.bitmap_location, g_Window,
&g_Ball.screen_location);

// Output the computer and player scores
char buffer[256];

string c_score = "Computer Score: ";
itoa(g_ComputerScore, buffer, 10);
c_score.append(buffer);

string p_score = "Player Score: ";
itoa(g_PlayerScore, buffer, 10);
p_score.append(buffer);

DisplayText(c_score, COMPUTER_SCORE_X, COMPUTER_SCORE_Y, 12, 66, 239, 16, 0, 0, 0);
DisplayText(p_score, PLAYER_SCORE_X, PLAYER_SCORE_Y, 12, 66, 239, 16, 0, 0, 0);
Collision Detection

Let's start with CheckWallCollisions() because it's really easy. It takes a reference to an Entity object and the direction the object is moving. All we need to do is check to see if the object is going to hit the left or right wall and return true or false accordingly. Add the following to "Main.cpp":

bool CheckWallCollisions(Entity& entity, Direction dir)
{
int temp_x; // stores the location of the entity after moving

// Get the location of the entity after it moves
switch (dir)
{
case LEFT:
{
temp_x = entity.screen_location.x - entity.x_speed;
} break;
case RIGHT:
{
// Notice that we have to add the entity's width to get its
// right(direction) coordinate
temp_x = (entity.screen_location.x + entity.screen_location.w) +
entity.x_speed;
} break;
}

if ( (temp_x <= 0) || (temp_x >= WINDOW_WIDTH) )
{
return true;
}

return false;
}

The function starts with a switch statement that determines the location of the object after it moves. It then checks that location with the sides of the game area. That's it!

CheckBallCollisions() is slightly more complicated. It takes a reference to the paddle that the ball is moving towards so the first thing we need to do is determine which paddle it is. Since we specified the Y locations of our paddles in "Defines.h", and because our paddles never move up or down, we can determine which paddle was passed to the function by checking its Y value against the defined values.

To see if there was a collision, we'll check to see if part of the ball is inside the given paddle. To understand why we would need to check to see if the ball is inside the paddle, imagine the paddle holding still and the ball moving towards it from the side. If the ball hits the side of the paddle, and our function only checks to see if the ball has hit the top of the paddle, the ball will pass right through our paddle. Considering the best way to win pong is by hitting the ball with the side of your paddle, I'd say we should be checking for more than just the top of the paddle hitting the ball.

Note that there is one problem that arises from checking to see if part of the ball is inside of a paddle. If the ball is near the bottom of the paddle when it hits it, it will be reflected backwards. The only problem is that our function will immediately detect another collision (because the ball is inside the paddle) and once again reflect the ball in the other direction. To avoid this, we just check to see if the ball is moving towards the paddle. If it isn't, we shouldn't be checking for collisions anyways.

If you have trouble understanding the code for this function, try drawing the game screen with a coordinate system. This should help you understand when to use the bottom of the ball (ball_y + ball_height) and when to use the top (ball_y). Note that all of the variables at the top of the function are just there to clean the code up a bit.

Add the following to "Main.cpp":

// Check to see if the ball is going to hit a paddle
bool CheckBallCollisions(Entity& paddle)
{
// Temporary values to keep things tidy
int ball_x = g_Ball.screen_location.x;
int ball_y = g_Ball.screen_location.y;
int ball_width = g_Ball.screen_location.w;
int ball_height = g_Ball.screen_location.h;
int ball_speed = g_Ball.y_speed;

int paddle_x = paddle.screen_location.x;
int paddle_y = paddle.screen_location.y;
int paddle_width = paddle.screen_location.w;
int paddle_height = paddle.screen_location.h;

// Get which paddle we're checking against
if ( paddle.screen_location.y == PLAYER_Y)
{
// Check to see if ball is in Y range of the player's paddle.
// We check its speed to see if it's even moving towards the player's paddle.
if ( (ball_speed > 0) && (ball_y + ball_height >= paddle_y) &&
(ball_y + ball_height <= paddle_y + paddle_height) ) // side hit
{
// If ball is in the X range of the paddle, return true.
if ( (ball_x <= paddle_x + paddle_width) && (ball_x + ball_width
>= paddle_x) )
{
return true;
}
}
}
else
{
// Check to see if ball is in Y range of the computer's paddle.
// We check its speed to see if it's even moving towards the computer's paddle.
if ( (ball_speed < 0) && (ball_y >= paddle_y) && (ball_y <= paddle_y +
paddle_height) )
{
// If ball is in the X range of the paddle, return true.
if ( (ball_x <= paddle_x + paddle_width) && (ball_x + ball_width >=
paddle_x) )
{
return true;
}
}
}

return false;
}
HandleBall()

HandleBall() heavily relies on MoveBall(), CheckBallCollisions(), HandlePlayerScore(), and HandleComputerScore(). The first thing we'll do is make a call to MoveBall(). We'll then check for collisions with the paddles using CheckBallCollisions(). If there is a collision, we determine what part of the paddle the ball hit and we change its speed accordingly. Finally, we check to see if the ball has moved passed one of the paddles. When this happens, we call the appropriate Handle...Score() function.

As previously discussed, when the ball hits a paddle we reverse its vertical speed. To determine its new horizontal speed, we divide the distance from the center of the paddle to the center of the ball by BALL_SPEED_MODIFIER. If the ball hits the center of the paddle, its horizontal speed will be 0. If the ball hits the edge of the paddle, its speed will be 10 (unless we change the size of the paddle or the value of BALL_SPEED_MODIFIER).

You'll notice that I once again use temporary variables to clean up the code here. I find this a great way to make the code more readable. It also saves me from having really long lines of code. Add the following to "Main.cpp":

void HandleBall()
{
// Start by moving the ball
MoveBall();

if ( CheckBallCollisions(g_Player) )
{
// Get center location of paddle and ball
int paddle_center = g_Player.screen_location.x +
g_Player.screen_location.w / 2;
int ball_center = g_Ball.screen_location.x +
g_Ball.screen_location.w / 2;

// Find the location on the paddle that the ball hit
int paddle_location = ball_center - paddle_center;

// Increase X speed according to distance from center of paddle.
g_Ball.x_speed = paddle_location / BALL_SPEED_MODIFIER;
g_Ball.y_speed = -g_Ball.y_speed;
}

if ( CheckBallCollisions(g_Computer) )
{
// Get center location of paddle
int paddle_center = g_Computer.screen_location.x +
g_Computer.screen_location.w / 2;
int ball_center = g_Ball.screen_location.x +
g_Ball.screen_location.w / 2;

// Find the location on the paddle that the ball hit
int paddle_location = ball_center - paddle_center;

// Increase X speed according to distance from center of paddle.
g_Ball.x_speed = paddle_location / BALL_SPEED_MODIFIER;
g_Ball.y_speed = -g_Ball.y_speed;
}

// Check to see if someone has scored
if (g_Ball.screen_location.y < 0)
{
HandlePlayerScore();
}
if (g_Ball.screen_location.y + g_Ball.screen_location.h > WINDOW_HEIGHT)
{
HandleComputerScore();
}
}

I'm assuming the code here is fairly self explanatory. I apologize if this seems like a bit of a code dump but I think it's pretty straight-forward (email me and let me know if it's not).
MoveBall()

Every frame we call HandleBall(), which in turn calls MoveBall(). MoveBall() simply moves the ball according to its current speed and checks to see if it hits a wall. If the ball does hit a wall, we just reverse its horizontal speed. Notice that since MoveBall() is constantly called, we just have to change the speed of the ball and wait for MoveBall() to get called again and actually move it.

The actual code is very simple. We start by adding the speed of the ball to its location. This makes the ball actually move. We then check to see if the ball has run into one of the walls. If it has, we negate its horizontal speed. Add the following code to "Main.cpp":

void MoveBall()
{
// Add the current speed of the ball to its location to move it
g_Ball.screen_location.x += g_Ball.x_speed;
g_Ball.screen_location.y += g_Ball.y_speed;

// If the ball is moving left, we see if it hits the wall. If does,
// we change its direction. We do the same thing if it's moving right.
if ( ( (g_Ball.x_speed < 0) && CheckWallCollisions(g_Ball, LEFT) ) ||
( (g_Ball.x_speed > 0) && CheckWallCollisions(g_Ball, RIGHT) ) )
{
g_Ball.x_speed = -g_Ball.x_speed;
}
}
HandleAI()

HandleAI() is probably the most complicated function in this tutorial. Before we discuss the code, we need to decide on what our AI will do. There are all kinds of ways to handle this. Initially I tried having the computer's paddle chase the ball by moving it until its center was over the ball. The problem with this was that the ball always got hit straight forward. It certainly didn't look like a very cunning AI.

To get around this, I decided to select a random number between 1 and 3. 1 represented the left of the computer's paddle, 2 represented the center, and 3 represented the right side. This way the computer made random decisions as to which part of its paddle to hit the ball with. This method turned out to make the computer pretty unpredictable, which I consider to be a good thing. This is the method I'll use for this tutorial.

The only problem now is figuring out when we should have the computer make a decision. Every time HandleAI() gets called we'll move the computer's paddle closer to the ball. If every frame we randomly decide on which part of the paddle to use, the computer's paddle will move around erratically because it will be constantly changing its mind.

To get around this, we'll record the speed of the ball every time this function finishes. At the start of the function, we'll check to see if the ball has changed speeds. If the speed changes, we know that the ball has changed direction and we should make a new decision. Otherwise we'll just keep moving the computer's paddle toward the ball until the appropriate part of the paddle is in the ball's path or the ball changes direction again.

One last thing to note is that if the ball's X location is near the computer paddle's center X location, the computer paddle will jerk back and forth. This is because it will first notice that its center is over the ball so it'll stop moving. Because the ball is still moving, the computer will notice that it is no longer over the ball so it will move again. If it passes the ball, it will notice that it is once again not over the ball so it will move back. This looks pretty bad in the game so we'll only move the computer's paddle if it is a certain distance from the ball.

Add the following to "Main.cpp":

// Move the computer's paddle and change its direction if necessary
void HandleAI()
{
// The first time this function gets called, we record the current speed
// of the ball and decide on which part of the paddle to hit the ball with.
static int last_speed = g_Ball.x_speed;
static int decision = rand() % 3 + 1;

// Keep the code tidy
int computer_x;
int ball_center = g_Ball.screen_location.x + g_Ball.screen_location.w / 2;

// See if ball has changed direction
if (last_speed != g_Ball.x_speed)
{
// Make a new decision if the ball changed direction.
// 1 == left side, 2 == right side, 3 = center
decision = rand() % 3 + 1;

last_speed = g_Ball.x_speed;
}

// Determine part of paddle to hit ball with according to decision
switch (decision)
{
case 1:
{
computer_x = g_Computer.screen_location.x;
} break;

case 2:
{
computer_x = g_Computer.screen_location.x +
g_Computer.screen_location.w;
} break;

case 3:
{
computer_x = g_Computer.screen_location.x +
g_Computer.screen_location.w / 2;
} break;
}

// See if ball is near computer's center. Prevents
// computer from rapidly moving back and forth.
if ( abs(computer_x - ball_center) < 10 )
{
return;
}

// Ball is to the left of the paddle
if (computer_x > ball_center)
{
// Make sure the paddle hasn't hit a side of the screen
if ( !CheckWallCollisions(g_Computer, LEFT) )
{
g_Computer.screen_location.x -= COMPUTER_SPEED;
}
}
// Ball is to the right of the paddle
else if (computer_x < ball_center)
{
// Make sure the paddle hasn't hit a side of the screen
if ( !CheckWallCollisions(g_Computer, RIGHT) )
{
g_Computer.screen_location.x += COMPUTER_SPEED;
}
}
}
HandlePlayerScore() and HandleComputerScore()

These two functions could probably be put into one, but what the heck! We start by increasing the player or computer's global score variable. We then reset the ball's location to the center of the screen. Note that we first place the ball's top-left corner at the center of the screen, and then we move it back by half of its radius. This places the center of the ball in the center of the screen.

To finish up, we just check to see if the player or computer has reached 10 points. If so, we reset the global score variables (in case the player decides to start a new game) and place the appropriate state function on the stack.

Add the following to "Main.cpp":

// Increase the player's score, reset ball, and see if player has won.
void HandlePlayerScore()
{
// Increase score
g_PlayerScore++;

// Reset ball
g_Ball.x_speed = 0;
g_Ball.y_speed = 0;
g_Ball.screen_location.x = (WINDOW_WIDTH / 2) - (BALL_DIAMETER / 2);
g_Ball.screen_location.y = (WINDOW_HEIGHT / 2) - (BALL_DIAMETER / 2);

// Check to see if player has won
if (g_PlayerScore == 10)
{
g_ComputerScore = 0;
g_PlayerScore = 0;

while (!g_StateStack.empty())
{
g_StateStack.pop();
}

StateStruct win;
win.StatePointer = GameWon;
g_StateStack.push(win);
}
}

// Increase computer's score, reset ball, and see it if computer has won.
void HandleComputerScore()
{
// Increase score
g_ComputerScore++;

// Reset ball
g_Ball.x_speed = 0;
g_Ball.y_speed = 0;
g_Ball.screen_location.x = (WINDOW_WIDTH / 2) - (BALL_DIAMETER / 2);
g_Ball.screen_location.y = (WINDOW_HEIGHT / 2) - (BALL_DIAMETER / 2);

// See if computer has won
if (g_ComputerScore == 10)
{
g_ComputerScore = 0;
g_PlayerScore = 0;
while (!g_StateStack.empty())
{
g_StateStack.pop();
}

StateStruct lose;
lose.StatePointer = GameLost;
g_StateStack.push(lose);
}
}
Conclusion

Well that's it for our Pong clone. If you try programming some of these old arcade games on your own, you'll find that you become much faster at it the more you do. Your code should also become a lot cleaner. Although you obviously become more advanced as you try more advanced projects, never count out the easy stuff as a good way to hone your skills. If you're impatient like me, you're probably thinking right now about making some large scale RPG or strategy game. I can assure you though that you'll get there a lot quicker by working through gradually harder projects than you will if you try to rush ahead.
ReadMore...

Saturday, December 27, 2008

(0) Comments

2D Tutorial Series Introduction

welcome to infomix.blogspot.com

What this tutorial covers

* Setting up SDL
* Opening a window with SDL
* Setting up a timer
* Starting the game loop
* Drawing bitmaps and text with SDL
* State stacks

Introduction

This tutorial is meant to be a quick introduction to setting up an SDL game project. It covers setting up SDL and using it to draw bitmaps and text. All of my SDL tutorials are geared towards using it to create specific games. If you 'd like some more general tutorials on using SDL, check out this site.

This tutorial uses the STL implementation of a stack, as well as function pointers, to handle the game states (menu, game, exit screen). Although I'll be briefly explaining these concepts in this tutorial, you can also check out my General Programming Tutorials for more information.

One word before we go on, I'm no programming expert and my code is not meant to be optimized or even to be the best way to get the job done. My main goal has been to write clean and understandable code that people new to game programming can understand. These tutorials are completely directed at people who are sick of making console apps and want to see what their new found programming skills can do.
Getting started

Since I'm assuming that you haven't worked with an API before, I'll now show you how to set up Visual Studio for SDL. Those of you who don't use Visual Studio can go to http://www.libsdl.org/ for instructions on how to set up SDL for your compiler. You'll need to go there anyways to download the latest version of SDL. Just scroll down to where it says "Download" and grab the newest version (it'll be under "Development Libraries").

Once you have it, install it to a directory of your choice (remember the directory, we'll need it soon). Now that you have it installed, open up Visual Studio (note that I'm using Visual Studio .NET for this discussion).

Go to Tools->Options and click on the Projects folder. In the top left, there's a drop down menu titled Show directories for:, bring that down and select Include Files. Click on the New Line button (looks like a folder) and add the path to the include folder which should be in the directory you installed SDL to.

Now go back to Show directories for: and select Library Files. Do the same thing as before, only this time add the lib folder from your SDL directory.

We have now told Visual Studio where to look for the files that make up the SDL API. This is a one time process, so you shouldn't have to worry about it again unless you reinstall Visual Studio.

Now go to your SDL directory and open the lib folder. Copy "SDL.dll" and paste it into your project directory. When you distrubute your SDL program, you must include this file in the same directory as your .exe file.

Unfortunately, SDL does not come with text support. A lot of functionality you'd expect from SDL actually doesn't come from SDL itself. It comes from other libraries which people have written themselves. You can get these libraries from the SDL website.

For text output, we'll be using the SDL_ttf (true type font) library. Assuming that the SDL website hasn't been changed since I downloaded the library, you should be able to just click here to get it. If the link doesn't work, email me and let me know. In the meantime, you can always get it from the SDL website if this link doesn't work.

Once you get the zip, extract it to where you installed SDL. It doesn't really matter where you extract it to actually, but this way things will be more organized. We have to do the same thing with SDL_ttf that we did with SDL itself to set it up. In Visual Studio, go to Tools->Options->Projects->VC++ Directories. Now select Include Files in the Show directories for drop-down and click the New Line button (looks like a folder). Add the path to the include folder located in the directory you extracted SDL_tff. Now do the same thing for Library Files only this time add the path to the lib folder in the directory you extracted SDL_tff.

In the lib folder that's in the directory you extracted SDL_TTF to, you'll find a file called "SDL_ttf.dll". You need to include this file when you distribute your program. Be sure to copy and paste it into your project folder now so you can run the game. It should be in the same folder that "SDL.dll" is in.

There's one more file you'll need to include. Whenever you use a font, you have to include the font file in your game directory. Be careful with what fonts you use though, some people have the nerve to copyright fonts. Always check before using a font. Anyways, download the arial font from here and stick it in your project directory.

Now let's start a Win32 Project and call it "SDL Introduction". This project will be used as a base for other SDL projects. It will open a window, start a timer (explained in a moment), and enter our game loop (also explained in a moment). First select New Project, then select Win32 Project from the list of templates. When the next window pops up, select Application Settings and then select Empty Project.

Once your project is loaded, you need to change one more setting. Select Project->SDL Application Framework Properties. Now select the C/C++ folder on the left and go to Code Generation. In the box that says Runtime library bring down the drop-down menu and select Multi-threaded DLL (/MD). You must do this for all of your SDL projects.

When using SDL, you have to include the GNU LGPL license (which SDL is released under) for legal purposes. Go to http://www.libsdl.org/ for more details, but rest assured that as long as you include "SDL.dll" and the GNU LGPL license (I include it in all of my projects, so you can get it from this tutorial's source code zip) you'll be complying with the license.
The Code

Before we start coding, you need to know how screen coordinates work. If you were given the point (20, 60) and were asked to find its location on the screen, you would probably go 20 pixels to the right from the left of your screen, and 60 pixels up from the bottom.

For us, the X value is normal, but the Y value is the opposite. You actually start from the top of the screen and move down. This means that you would go 20 units to the right from the left of the screen, and 60 pixels down from the top of the screen. Keep in mind then that if you want to move something down, you increase its Y value.

We will be using two files for this project. One will be named "Main.cpp" and will contain all of the code required to run our game. The other will be named "Defines.h" and will contain all of the constant values that we will be using. Add two files to your project and name them "Main.cpp" and "Defines.h".
Defines.h

Let's begin with "Defines.h". The #define directive allows us to associate a constant value with a more readable name. For example, we might need to specify the width of our window many times throughout our program. Instead of having to remember what the width of our window is, we can specify its size with #define WINDOW_WIDTH 800 and just use WINDOW_WIDTH anytime we need the width of our window. When we compile our program, the compiler will change any instance of WINDOW_WIDTH to 800 for us. The main advantage of doing this is that we only specify the value once in our program. If we decide to change it later, the compiler will automatically change every instance of WINDOW_WIDTH to the new value.

We might want to change the size of our window later so we'll define the dimensions of our window here. We also might want to change the caption at the top of our window so we'll define that here too. Add the following at the top of "Defines.h":

// Window related defines
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600
#define WINDOW_CAPTION "SDL Introduction"

We'll be using a timer to control the frame rate of our game. We'll get to our timer in a bit but for now you should know that it works in milliseconds and that it checks to see if a given amount of time has passed since we've processed something. Let's create two defines, one that represents our frames-per-second, and another that represents the time between each frame. Add the following to "Defines.h":

// Game related defines
#define FRAMES_PER_SECOND 30
#define FRAME_RATE 1000/FRAMES_PER_SECOND

Note that our timer works in milliseconds so we divide 1000 by FRAMES_PER_SECOND. We want 30 frames per second, which means a frame needs to be processed every 1000/30 milliseconds.
Main.cpp

The first thing we need to do in "Main.cpp" is tell our linker to link in the required SDL libraries. Add the following to "Main.cpp":

#pragma comment(lib, "SDL.lib")
#pragma comment(lib, "SDLmain.lib")
#pragma comment(lib, "SDL_TTF.lib")

Although we could have done this in our project settings, I prefer this method because we can now just copy "Main.cpp" into any of our projects and not mess around with any settings.
Includes

We will be using the STL implementation of a stack to handle our states. If you'd like a more thorough introduction to this concept, see my STL Tutorial and my Function Pointers Tutorial. The concept is fairly simple however. A stack is a data structure that stores data like a stack of plates stores plates. If you want to add a plate to the stack, you add it to the top. If you want to take a plate, you take it from the top.

Function pointers are exactly what they sound like: pointers that point to functions. There's nothing really complicated about them. We'll have function pointers that point to the functions that handle the states of our game (menu, game, exit screen) and push them onto our stack. If we push a pointer to our exit screen onto the stack, and then a pointer to our menu state, we'll have a stack that looks like this:

[exit screen]->[menu]

In each frame of our game, we'll call the function at the top of our stack, in this case our menu state. If the user selects to play the game, we push a pointer to the function that handles our game logic. Then our stack looks like this:

[exit screen]->[menu]->[game]

Now our game function will be called every frame. If the user presses the 'escape' key, we'll pop the game function off of our stack, and the stack will look like this:

[exit screen]->[menu]

If we press the 'escape' key again, the stack will look like this:

[exit screen]

Now the user will see our exit screen, which asks if the user really wants to quit. If the user decides to continue playing, we just push the menu state back onto our stack. If the user chooses to quit, we pop the exit screen state off the stack and our program will quit because the state stack will be empty.

Our includes will be pretty straight forward. We include the SDL header files and our "Defines.h" file. Add the following to "Main.cpp":

#include // We'll use the STL stack to store our function pointers
#include "SDL.h" // Main SDL header
#include "SDL_TTF.h" // True Type Font header
#include "Defines.h" // Our defines header

using namespace std; // save us from writing "std::" everywhere
Data

We initialize an STL stack with the following syntax:

stack stack_name;

The notation tells us that the stack is a templated class. This means that we can specify the type of data we want the stack to store. See my Templates Tutorial for more on templates.

The notation for a function pointer is this:

return_type (*pointer_name)(parameters);

One problem we have is that we can't do the following:

stack stack_name;

The STL stack does not accept function pointers as a data type. To get around this, we'll encapsulate a function pointer into a struct. Add the following to "Main.cpp":

// The STL stack can't take a function pointer as a type
// so we encapsulate a function pointer within a struct.
struct StateStruct
{
void (*StatePointer)();
};

// Global data
stack g_StateStack; // Our state stack

There's actually not much more data needed for this tutorial. One thing I need to explain though is the concept of double buffering. In graphics, a buffer is a location in memory that contains information about what we want to draw to the screen. The data that gets drawn to the screen is stored in the "front buffer".

Double buffering comes in when we want to change what is drawn to the screen. If we just write to the front buffer, we'll be drawing over what is currently on the screen while it is still being displayed. The player will actually see the screen being drawn, which leads to all kinds of problems. To get around this, we create a "back buffer" that does not get drawn. We first draw to the back buffer and then display the entire screen once it has been completely drawn on the back buffer.

We'll call our back buffer g_Window because it represents our game window. When we want to draw it, we'll call a function that replaces the contents of our front buffer with the back buffer.

The rest of the data is simple, we need a structure that stores the bitmap to be displayed during the game state, a structure to store events for when we handle input, and an integer that stores the time at which we last processed a game frame. Add the following to "Main.cpp":

SDL_Surface* g_Bitmap = NULL; // Our background image
SDL_Surface* g_Window = NULL; // Our back buffer
SDL_Event g_Event; // An SDL event structure for input
int g_Timer; // Our timer is just an integer

The SDL_Surface structure is just a buffer that stores image information. You can think of it as a painting canvas. You can draw whatever you want on it. The SDL_Event structure just stores event information like what keys the user has pressed.
Prototypes

Our game will handle three states, a menu, the actual game, and an exit screen. We'll have a function for each one of these states. Add the following to "Main.cpp":

// Functions to handle the three states of the game
void Menu();
void Game();
void Exit();

Every game project, in fact every project, usually needs some intialize and shutdown functionality. We'll write a couple of functions to handle this for us.

Our state functions will require some helper functions. Each state will need to clear the screen before it draws anything, so we'll write a function that clears the screen to black.

The menu and exit screen states will need a way to draw text. Writing text to the screen is actually a bit tricky in SDL so let's just write a function to handle this and never worry about it again.

The game state needs to draw our bitmap to the screen, so we'll have a function to handle that too.

Finally, each of our states will handle input. Because we'll probably want each state to handle input differently, we'll write three different input functions.

Add the following to "Main.cpp":

// Init and Shutdown functions
void Init();
void Shutdown();

// Helper functions for the game state functions
void DrawBackground();
void ClearScreen();
void DisplayText(string text, int x, int y, int size,
int fR, int fG, int fB, int bR, int bG, int bB);
void HandleMenuInput();
void HandleGameInput();
void HandleExitInput();

Note that DisplayText() takes the forgeound and background colors of the text. This will be explained in a moment.
main()

Our main()function will actually be quite simple. One thing to always remember though is that SDL requires you to include the following parameters:

int argc, char **argv

Aside from that, all we need to do with main is call Init(), call the top pointer on our state stack until the state stack is empty, and finish by calling Shutdown(). Add the following code to "Main.cpp":

int main(int argc, char **argv)
{
Init();

// Our game loop is just a while loop that breaks when our state stack is empty.
while (!g_StateStack.empty())
{
g_StateStack.top().StatePointer();
}

Shutdown();

return 0;
}

Notice that in our while loop (this would also be considered a "Game Loop") all we have to do is check to see if our stack still has something in it and then call the function pointer at the top. Also notice that a function pointer is called just like a function would be called.
Init() and Shutdown()

Initializing SDL is very simple. We make a call to SDL_Init(), which will intialize the SDL video and timer components. A call to SDL_SetVideoMode() will allow us to specify the dimensions and format of our window. The function also returns a pointer to our window, which we assign to g_Window. SDL_WM_SetCaption() allows us to set the caption at the top of our window.

Whenever we process a frame in our game, we'll reset our timer to the time at which we processed the frame. To start, we'll set our timer to the time at which we intialize our game.

SDL_LoadBMP() allows us to load our bitmap from a file. TTF_Init() initializes the True Type Font library.

We start our game in the menu so we should make sure that a pointer to Menu() is at the top of our state stack after initializing our game. We should also make sure that a pointer to Exit() is always at the bottom of the stack since it should always be the last screen the player sees. Add the following to "Main.cpp":

// This function initializes our game.
void Init()
{
// Initiliaze SDL video and our timer.
SDL_Init( SDL_INIT_VIDEO | SDL_INIT_TIMER);
// Setup our window's dimensions, bits-per-pixel (0 tells SDL to choose for us),
// and video format (SDL_ANYFORMAT leaves the decision to SDL). This function
// returns a pointer to our window which we assign to g_Window.
g_Window = SDL_SetVideoMode(WINDOW_WIDTH, WINDOW_HEIGHT, 0, SDL_ANYFORMAT);
// Set the title of our window.
SDL_WM_SetCaption(WINDOW_CAPTION, 0);
// Get the number of ticks since SDL was initialized.
g_Timer = SDL_GetTicks();

// Fill our bitmap structure with information.
g_Bitmap = SDL_LoadBMP("data/background.bmp");

// We start by adding a pointer to our exit state, this way
// it will be the last thing the player sees of the game.
StateStruct state;
state.StatePointer = Exit;
g_StateStack.push(state);

// Then we add a pointer to our menu state, this will
// be the first thing the player sees of our game.
state.StatePointer = Menu;
g_StateStack.push(state);

// Initialize the true type font library.
TTF_Init();
}

To shut our program down, we just tell SDL and TTF to shutdown. We also have to free our surfaces. Add the following to "Main.cpp":

// This function shuts down our game.
void Shutdown()
{
// Shutdown the true type font library.
TTF_Quit();

// Free our surfaces.
SDL_FreeSurface(g_Bitmap);
SDL_FreeSurface(g_Window);

// Tell SDL to shutdown and free any resources it was using.
SDL_Quit();
}
State Functions

At the beginning of each of our state functions we'll make a call to the appropriate input handling function. We'll also need to check to see if it's actually time to do any processing. We do this with our timer.

When it's time to process a new frame, we first clear our screen. We then do whatever rendering our state needs and call SDL_UpdateRect() to display our backbuffer.

To finish, we update our timer.

The specific rendering of our states is handled by other functions so we just make some simple function calls here. Add the following to "Main.cpp":

// This function handles the game's main menu. From here
// the player can select to enter the game, or quit.
void Menu()
{
// Here we compare the difference between the current time and the last time we
// handled a frame. If FRAME_RATE amount of time has, it's time for a new frame.
if ( (SDL_GetTicks() - g_Timer) >= FRAME_RATE )
{
HandleMenuInput();

// Make sure nothing from the last frame is still drawn.
ClearScreen();

DisplayText("Start (G)ame", 350, 250, 12, 255, 255, 255, 0, 0, 0);
DisplayText("(Q)uit Game", 350, 270, 12, 255, 255, 255, 0, 0, 0);

// Tell SDL to display our backbuffer. The four 0's will make SDL display the whole screen.
SDL_UpdateRect(g_Window, 0, 0, 0, 0);

// We've processed a frame so we now need to record the time at which we did it.
// This way we can compare this time with the next time our function gets called and
// see if enough time has passed between calls.
g_Timer = SDL_GetTicks();
}
}

// This function handles the main game. We'll control the
// drawing of the game as well as any necessary game logic.
void Game()
{
// Here we compare the difference between the current time and the last time we
// handled a frame. If FRAME_RATE amount of time has, it's time for a new frame.
if ( (SDL_GetTicks() - g_Timer) >= FRAME_RATE )
{
HandleGameInput();

// Make sure nothing from the last frame is still drawn.
ClearScreen();

// Draw the background of our 'game'.
DrawBackground();

// Tell SDL to display our backbuffer. The four 0's will make SDL display the whole screen.
SDL_UpdateRect(g_Window, 0, 0, 0, 0);

// We've processed a frame so we now need to record the time at which we did it.
// This way we can compare this time with the next time our function gets called and
// see if enough time has passed between calls.
g_Timer = SDL_GetTicks();
}
}

// This function handles the game's exit screen. It will display
// a message asking if the player really wants to quit.
void Exit()
{
// Here we compare the difference between the current time and the last time we
// handled a frame. If FRAME_RATE amount of time has, it's time for a new frame.
if ( (SDL_GetTicks() - g_Timer) >= FRAME_RATE )
{
HandleExitInput();
// Make sure nothing from the last frame is still drawn.
ClearScreen();

DisplayText("Quit Game (Y or N)?", 350, 250, 12, 255, 255, 255, 0, 0, 0);

// Tell SDL to display our backbuffer. The four 0's will make SDL display the whole screen.
SDL_UpdateRect(g_Window, 0, 0, 0, 0);

// We've processed a frame so we now need to record the time at which we did it.
// This way we can compare this time with the next time our function gets called and
// see if enough time has passed between calls.
g_Timer = SDL_GetTicks();
}
}
Rendering Functions

Let's start with DrawBackground(). SDL is great for stuff like this. All we need to do is define two rectangles and call SDL_BlitSurface(). The first rectangle stores the location in our bitmap file of the image we want to draw. The other rectangle stores the location within our window that we want our image displayed. The term "blit" means "block-image transfer". It just means that we transfer data from one area in memory to another (back buffer to front buffer). Add the following to "Main.cpp":

// This function draws the background
void DrawBackground()
{
// These structures tell SDL_BlitSurface() the location of what
// we want to blit and the destination we want it blitted to.
// Presently, we blit the entire surface to the entire screen.
SDL_Rect source = { 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT };
SDL_Rect destination = { 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT };

// This just 'block-image transfers' our bitmap to our window.
SDL_BlitSurface(g_Bitmap, &source, g_Window, &destination);
}

ClearScreen() simply clears the screen to black by calling SDL_FillRect(), which draws a rectangle of whatever color we specify. Add the following to "Main.cpp":

// This function simply clears the back buffer to black.
void ClearScreen()
{
// This function just fills a surface with a given color. The
// first 0 tells SDL to fill the whole surface. The second 0
// is for black.
SDL_FillRect(g_Window, 0, 0);
}

Displaying text using SDL takes a bit of work. We first have to create a font structure to specify the style and size of our text. We also need two color structures to hold the color of text and the color of what we want behind the text.

To actually display the text, we first need to render it to a temporary surface. We then blit that surface just like we did with our bitmap. To finish, we just close the font structure. Add the following to "Main.cpp":

// This function displays text to the screen. It takes the text
// to be displayed, the location to display it, the size of the
// text, and the color of the text and background.
void DisplayText(string text, int x, int y, int size, int fR,
int fG, int fB, int bR, int bG, int bB)
{
// Open our font and set its size to the given parameter.
TTF_Font* font = TTF_OpenFont("arial.ttf", size);

SDL_Color foreground = { fR, fG, fB}; // Text color.
SDL_Color background = { bR, bG, bB }; // Color of what's behind the text.

// This renders our text to a temporary surface. There
// are other text functions, but this one looks nice.
SDL_Surface* temp = TTF_RenderText_Shaded(font, text.c_str(), foreground, background);

// A structure storing the destination of our text.
SDL_Rect destination = { x, y, 0, 0 };

// Blit the text surface to our window surface, the NULL specifies the whole surface.
SDL_BlitSurface(temp, NULL, g_Window, &destination);

// Always free memory!
SDL_FreeSurface(temp);

// Close the font.
TTF_CloseFont(font);
}
Input Functions

To handle input, we first need to fill our event structure. A call to SDL_PollEvent() will take care of this. We then need to figure out the type of event. The two we will deal with are SDL_QUIT and SDL_KEYDOWN. SDL_QUIT occurs when the player clicks the 'X' in the caption bar. SDL_KEYDOWN occurs when a key is pressed.

When the user manually closes the window, we pop all of the states off of our stack so our game loop will quit and Shutdown() will be called. When the 'escape' key is pressed, we pop the top most state off of the stack.

The rest of our event handling is specific to each function but should be self-explanatory.

Note that SDL stores the key that was pressed in SDL_Event.key.keysym.sym. This is kind of ugly so don't feel bad if you just copy-paste it instead of memorizing it.

Add the following to "Main.cpp":

// This function receives player input and
// handles it for the game's menu screen.
void HandleMenuInput()
{
// Fill our event structure with event information.
if ( SDL_PollEvent(&g_Event) )
{
// Handle user manually closing game window
if (g_Event.type == SDL_QUIT)
{
// While state stack isn't empty, pop
while (!g_StateStack.empty())
{
g_StateStack.pop();
}

return; // game is over, exit the function
}

// Handle keyboard input here
if (g_Event.type == SDL_KEYDOWN)
{
if (g_Event.key.keysym.sym == SDLK_ESCAPE)
{
g_StateStack.pop();
return; // this state is done, exit the function
}
// Quit
if (g_Event.key.keysym.sym == SDLK_q)
{
g_StateStack.pop();
return; // game is over, exit the function
}
// Start Game
if (g_Event.key.keysym.sym == SDLK_g)
{
StateStruct temp;
temp.StatePointer = Game;
g_StateStack.push(temp);
return; // this state is done, exit the function
}
}
}
}

// This function receives player input and
// handles it for the main game state.
void HandleGameInput()
{
// Fill our event structure with event information.
if ( SDL_PollEvent(&g_Event) )
{
// Handle user manually closing game window
if (g_Event.type == SDL_QUIT)
{
// While state stack isn't empty, pop
while (!g_StateStack.empty())
{
g_StateStack.pop();
}

return; // game is over, exit the function
}

// Handle keyboard input here
if (g_Event.type == SDL_KEYDOWN)
{
if (g_Event.key.keysym.sym == SDLK_ESCAPE)
{
g_StateStack.pop();

return; // this state is done, exit the function
}
}
}
}

// This function receives player input and
// handles it for the game's exit screen.
void HandleExitInput()
{
// Fill our event structure with event information.
if ( SDL_PollEvent(&g_Event) )
{
// Handle user manually closing game window
if (g_Event.type == SDL_QUIT)
{
// While state stack isn't empty, pop
while (!g_StateStack.empty())
{
g_StateStack.pop();
}

return; // game is over, exit the function
}

// Handle keyboard input here
if (g_Event.type == SDL_KEYDOWN)
{
if (g_Event.key.keysym.sym == SDLK_ESCAPE)
{
g_StateStack.pop();

return; // this state is done, exit the function
}
// Yes
if (g_Event.key.keysym.sym == SDLK_y)
{
g_StateStack.pop();
return; // game is over, exit the function
}
// No
if (g_Event.key.keysym.sym == SDLK_n)
{
StateStruct temp;
temp.StatePointer = Menu;
g_StateStack.push(temp);
return; // this state is done, exit the function
}
}
}
}

Conclusion

That's all you need to know to start an SDL game project. All you need to do now is copy this framework into any of your game projects and you'll be able to get straight to the game specific code.
ReadMore...