2009
01.09

Well, if you followed the previous tutorial, we have a nice window, with nothing at all related to Pong inside it. Let’s create some paddles!

OK, So, sticking with the whole ‘modular’ process we have followed to so far, we aren’t just going to render a quad and throw a texture on it. We need to create an Entity Class, which will be the base class for any Entity (Ball, Paddles, Walls) we are going to need to use. Again, i know it’s a bit of hassle, but it’s all for the greater good, it means we’ll be able to add objects in and have certain standard functionality for any object, making rendering them much easier. Let’s see the class definition.

Entity.h

#ifndef ENTITY_H_
#define ENTITY_H_
 
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <vector>
 
#include <gl/glew.h>
#include <sdl/SDL.h>
#include <sdl/SDL_ttf.h>
 
//This isn't actually used yet really, but we will use it later, for checking what type of entity we are
enum ObjectType
{
	Type_Object, Type_Wall, Type_Ball, Type_Paddle, Type_Goal
};
 
class Entity
{
protected:
	float _width, _height;
	GLuint _texture;
	GLfloat _r, _g, _b;
	ObjectType _type;
 
	//Load up a bitmap from the provided filename so we can texture the quad with it.
	void CreateTextureFromFile(const char *filename);
 
	//Run a check to see if two entitys are colliding
	bool CollideBoundingBox(SDL_Rect a, SDL_Rect b);
 
public:
	float xVel, yVel;
	float _x, _y, _z;
 
	Entity();
 
	//Overrideable, so we know that Type of object this is (Enum ObjectType)
	virtual ObjectType Type();
 
	//Set the type of this object
	void SetType(ObjectType theType);
 
	//Check against another Entity if there is a collision occuring
	bool IsColliding(Entity *targetObject);
 
	//Default render method, just renders a quad
	void Render();
 
	//Initialise this Entity, with the following values...
	void Initialise(float width, float height, float x, float y);
	void Initialise(float width, float height, float x, float y, const char *texturelocation);
 
	//Give it a pretty colour? Not required...
	void SetColour(GLfloat r, GLfloat g, GLfloat b);
};
#endif /* ENTITY_H_ */

And there it is, all the methods have been commented enough to make them clear. They should be more understandable when we go through the declaration.

Now this is going to be quite long, as we are also going to support texturing in this class. So i will be taking you through that too.

First off, create a file in the same folder (Pong/src) called Entity.cpp, this is where all the logic will be held! For now, just throw this code into it, it should be fairly understandable as is.

Entity.cpp

#include "Entity.h"
 
Entity::Entity()
{
        //Initialise all the variables we are going to use...
	this-&gt;_x = this-&gt;_y = this-&gt;xVel = this-&gt;yVel = this-&gt;_r = this-&gt;_g = this-&gt;_b = 0.0f;
	this-&gt;_texture = 0;
}

Let’s get onto the definitions of the fun stuff! I say we start with Initialise, as it’s really the first method(s) that get called. So let’s see it.

void Entity::Initialise(float width, float height, float x, float y)
{
        //Literally just set the variables, these won't get used again until render.
	_width = width;
	_height = height;
	_x = x;
	_y = y;
}
 
void Entity::Initialise(float width, float height, float x, float y, const char *texturelocation)
{
        //Call the other Initialise method, just so we can set the values
	this-&gt;Initialise(width, height, x, y);
 
        ///Create a texture, using the textureLocation variable passed in
	CreateTextureFromFile(texturelocation);
}
 
void Entity::SetColour(GLfloat r, GLfloat g, GLfloat b)
{
	this->_r = r;
	this->_g = g;
	this->_b = b;
}

Now, any bright spark will know this won’t now compile, because i haven’t given you the definition for CreateTextureFromFile, well thats next, so be patient! ;) It’s quite long, so i have commented it in detail…

void Entity::CreateTextureFromFile(const char *filename)
{
	//We will use this Surface to load the image onto, before we create a texture out of it.
	SDL_Surface *surface;
 
	//This will store the texture format for OpenGL, i.e. RGB, RGBA, BGR, etc
	GLenum texture_format;
 
	//This stores how many colours we have in the image, i.e. if it's only got RGB, we have 3
	GLint nOfColors;
 
	//Lets load up the image provided by the filename parameter
	if ((surface = SDL_LoadBMP(filename)))
	{
		//Whoo, we have loaded it, let's find it's format!
		nOfColors = surface-&gt;format-&gt;BytesPerPixel;
 
		//Do we have 4 colours? Red, Green, Blue and Alpha
		if (nOfColors == 4)
		{
			//Yes, which order is it? Red First?
			if (surface-&gt;format-&gt;Rmask == 0x000000ff)
				texture_format = GL_RGBA;
			else
				texture_format = GL_BGRA;
		}
		else if (nOfColors == 3)
		{
			//Oh, we only have 3 colours? OK, again, which order are those 3 colours in?
			if (surface-&gt;format-&gt;Rmask == 0x000000ff)
				texture_format = GL_RGB;
			else
				texture_format = GL_BGR;
		}
		else
			printf("warning: the image is not truecolor..  this will probably breakn");
 
		// Have OpenGL generate a texture object handle for us, using our _texture Member variable, so we always have access to it
		glGenTextures(1, &amp;_texture);
 
		// Bind the texture object to our _textureMember variable
		glBindTexture(GL_TEXTURE_2D, _texture);
 
		// Set a few parameters on our Texture, so it displays as we want it to.
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
 
		// Set all the image data of the texture, here we set it's width, it's height, the format, and the actual data (surface-&gt;pixels)
		glTexImage2D(GL_TEXTURE_2D, 0, nOfColors, surface-&gt;w, surface-&gt;h, 0, texture_format, GL_UNSIGNED_BYTE, surface-&gt;pixels);
	}
	else
	{
		//General error message if file could not be found
		std::cout &lt;&lt; "SDL could not load " &lt;&lt; filename &lt;&lt; ": " &lt;&lt; SDL_GetError() &lt;&lt; std::endl;
		SDL_Quit();
	}
 
	// Free the SDL_Surface only if it was successfully created
	if (surface)
	{
		//Free the memory used by the surface, as we have now loaded all the data up into OpenGL, it has it's own copy of the image in memory
		SDL_FreeSurface(surface);
	}
}

So, now we have can create an Entity, give it a texture, but we still can’t see it? Damn, i guess we should render it then.

void Entity::Render()
{
	//If we have an xVelocity or a yVelocity, then increment the X or Y position based on their velocity
	if (xVel != 0 || yVel != 0)
	{
		_x += this-&gt;xVel;
		_y += this-&gt;yVel;
	}
 
	//Check to see if we have a texture loaded up, if we do, then enable texturing and bind the texture, so that the next thing
	//we render with texturing, it will get this texture.
	if (_texture != 0)
	{
		//Enable Texturing
		glEnable(GL_TEXTURE_2D);
		//Bind the Texture to this object
		glBindTexture(GL_TEXTURE_2D, _texture);
	}
 
	//Stack the current settings for colour, so we can set the colour for this object, then pop the settings back when we are done
	glPushAttrib(GL_CURRENT_BIT);
 
	//Draw this object the correct colour, notice we set the _r, _g, _b color. This WILL effect the colour of your textures.
	glColor3f(_r, _g, _b);
 
	//Move the object to the correct location in space, we are only working in 2D so don't need to worry about the Z Value
	glTranslatef(_x, _y, 0);
 
	//Tell OpenGL we want to render a quad, which is a 4 sided polygon
	glBegin(GL_QUADS);
 
	//Now, here we need to check to see if we loaded up a texture, if we did then we need to tell opengl how to render the texture
	//i.e. where the top left is. I won't comment the others, as this one should explain them all. We set 0,0 because that is the
	//upper left of the quad, and that is how we want our Image to appear. We could render it upside down of course, but thats silly.
	if (_texture != 0)
		glTexCoord2i(0, 0);
 
	//So, now we have told opengl where to render our texture, we need to give it something to render it onto. We want to create vertex
	//which is just a point in 3d space, of course as we are creating a quad, we need to specify 4 of them. This is the top left corner
	//of the box, we next do the bottom left, then the bottom right, then the top right. OGL will then render that as a 4 sided polygon.
	glVertex3f(0, 0, 0);
 
	//Texturing again
	if (_texture != 0)
		glTexCoord2i(0, 1);
 
	//Bottom Left Corner, see how we specify the height. This will create an object which is _height pixels high.
	glVertex3f(0, _height, 0);
 
	//Texturing again
	if (_texture != 0)
		glTexCoord2i(1, 1);
 
	//Bottom left, again, with the height, also now with the width
	glVertex3f(_width, _height, 0);
 
	//Texturing again
	if (_texture != 0)
		glTexCoord2i(1, 0);
 
	//Now for the top right, just need to tell it the width
	glVertex3f(_width, 0, 0);
	glEnd();
 
	//Pop the colour settings back from the stack
	glPopAttrib();
 
	//Disable Texturing again, so nothing else gets this texture applied to them.
	if (_texture != 0)
		glDisable(GL_TEXTURE_2D);
}

There is the beast, this will now allow us to render a 2D object (technically it is a 3d object) onto the screen, with a designated texture. Should we so with, this is just preparation for the future, we aren’t going to be texturing anything in this tutorial

So, we can create an entity, give it a texture, and now render it, what else is there?! Well, we need to know when two Entity’s are touching each other, otherwise how will we know when we’ve scored a goal? Or even worse, the ball won’t bounce off the wall and it’ll just float into space, and then no-one wins. And nobody likes a draw.

Let’s do some collision detection, it’s only simple 2D stuff. This function was taken from GPWiki, which is brilliant for Game Programming. This is the exact link to the article i used. I didn’t see the point in-reinventing the wheel, even though it is a very simple wheel.

Here’s the definition of the Collision methods.

bool Entity::CollideBoundingBox(SDL_Rect a, SDL_Rect b)
{
	if (b.x + b.w &lt; a.x)
		return false;
	if (b.x &gt; a.x + a.w)
		return false;
 
	if (b.y + b.h &lt; a.y)
		return false;
	if (b.y &gt; a.y + a.h)
		return false;
 
	return true; //bounding boxes intersect
}
 
bool Entity::IsColliding(Entity *targetObject)
{
        //This isn't the most optimal way of doing this, but it does the trick for pong. We just create two rectangles
        //for this entity and the target one, so we can see if they are overlapping
	SDL_Rect thisRect;
	thisRect.h = this-&gt;_height;
	thisRect.w = this-&gt;_width;
	thisRect.x = this-&gt;_x;
	thisRect.y = this-&gt;_y;
 
	SDL_Rect targetRect;
	targetRect.h = targetObject-&gt;_height;
	targetRect.w = targetObject-&gt;_width;
	targetRect.x = targetObject-&gt;_x;
	targetRect.y = targetObject-&gt;_y;
 
        //Run the check, to see if they are colliding
	return CollideBoundingBox(thisRect, targetRect);
}

Now we can check to see if two objects are colliding. I.e. the Ball with the Wall, or the Ball with a paddle.

Here are the other two methods, that are really simple and won’t actually get used just yet. This again, is for future use. It’s just for storing and retrieving the Entity’s type, i.e. Ball

ObjectType Entity::Type()
{
	return _type;
}
 
void Entity::SetType(ObjectType theType)
{
	_type = theType;
}

Right, let’s get a paddle on our screen, that’s enough preperation! Note: Don’t forget, we need to add this class to the Makefiles! Don’t remember how? Follow the previous tutorial, but instead of adding Pong.o we want Entity.o. I will attach the entire folder at the end so you could just take those makefiles.

In your Pong.h, you will now need to create a Two Entity instances and include the Entity header.

/*Add this Line*/
#include "Entity.h"
/*Add this Line*/
 
class Pong
{
        /*Add this Line*/
	Entity player1, player2;
        /*Add this Line*/
};

These will be both the paddles, for the left and the right of the screen.

Now, in our Pong classes InitialiseGeometry method, we need to do just that, Initialise the Entity Instances.

void Pong::InitialiseGeometry()
{
	//As the height of our Paddles, is 80 pixels, we need to determine where the center of the window would be for our paddles.
	//Simple as ((height / 2) - (paddle_height - 2))
	float playerStartY = ((_height / 2) - 40);
 
	//Now Initialise our objects we want to be showing, we set their Width and Height, the their position, fairly simple
       player1.Initialise(15, 80, 20, playerStartY);
 
       //Now we set Player1's color, for this tutorial they both will be white.
       player1.SetColour(1, 1, 1);
 
       //Exactly the same again, except this one is drawn on the right hand side
       player2.Initialise(15, 80, (_width - 35), playerStartY);
       player2.SetColour(1, 1, 1);
}

So we have 2 Paddles now (effectively), both are white, one on the left, one on the right. Both centered vertically. Great! Oh wait, we can’t see them!

Let’s render them!! Can you guess which method we want to render them in? (I’ll give you a hint…Render.)

glClear(GL_COLOR_BUFFER_BIT);
 
//glPushMatrix, will store the current state of the matrix in a special little box, so we can't mess it up. Which gives us a license to do
//what ever we like to it, as we can just pop it back out of the box, and restore it back to the way it was. This is especially useful when
//we are moving objects around space. You may be thinking, we aren't moving them, they are static. Actually, they're not. We are creating
//the boxes at 0,0 (top left in this perspective), then we are moving them to their location.
glPushMatrix();
 
//Call the Entity's render method, which creates a quad, set's it's colour and renders it
player1.Render();
 
//Pop the matrix back to it's previous state, so now any objects we create, will be in the top left, and our first object will be where we left it.
glPopMatrix();
 
//Same as above, but with Player2.
glPushMatrix();
player2.Render();
glPopMatrix();
 
SDL_GL_SwapBuffers();

Now, compile your game. Run it, just like in the previous tutorials.

make
./Pong

IF compile was a success, you should be presented with a black window and two Paddles. Like this…

pongstep3

If you are, then great! Well done. If not, leave a comment with the problem and i’ll try and see what happened.

As always, here is the entire project source. Also as always, limited comments. Learn from reading the blog, not from taking code ;)

Pong Step 3.zip

Enjoy! Look forward to seeing you again soon. Well, your stat on my web logs anyway!

2 comments so far

Add Your Comment
  1. hi, i finished your first 3 tutorials on opengl and sdl on ubuntu for pong. Was looking forward to the last couple so I had a working game. Did you ever finish them? I can’t seem to find any more entries regarding this series.

  2. hy, nice work on the pong. looking forward to see the next entry of the pong game :}

Spiked Software Coding Articles is Digg proof thanks to caching by WP Super Cache