It’s been a while since I last updated on this, but hopefully you’ve brushed up on your C and OpenGL skills. Here you’ll find the entire source code to a pong implementation in C and OpenGL.
Enjoy!
In interest of actually finally getting this post out the door, I’m just going to post the code! This is actually a snapshot of the code from a month or so back and I apologize for not tidying it up. The reason I’m using a snapshot from a while back is because I have a newer, more glitzy version with a scoreboard and some simple effects that aren’t really necessary to get the basic game down. I will post that code in a future post though when I’ve polished it up a bit more! This is also most definitely not up to proper coding standards, but it gets the job done. It was a late night hack-fest, so please disregard the lack of neatness!
A word on use..
You’re free to use the code however you like. If you use it though, I would most definitely appreciate it if you post a link to Last Stop on your website or blog and a mention in the credits of the application. Please also credit the references to Timothy M. Rodriguez. You don’t have to do this, but it’d be nice. I’d also very much love an e-mail or comment letting me know about your project!
On to the Code!
#include <stdlib.h>
#include <stdio.h>
#include <math.h>#include <GLUT/glut.h>
static int lastFrameTime = 0, windowWidth = 0, windowHeight = 0, now = 0;
static float midWidth = 0, midHeight = 0;
static float elapsedTime = 0, elapsedMilliSeconds = 0;
static char player1Move = 0, player2Move = 0;
static float player1V = 0, player2V = 0;static float PI = 3.14159265358979323846264;
//scores
static int player1Score = 0, player2Score = 0;//the following variables calculate a random starting projectile trajectory
static float randomDirection;
static long longMax = 0×7fffffff; //max of longs 2^31-1 (2^(n-1)-1 for 2’s complement) static float trajectory[2] = {0, 0};static float projMove[2] = {0, 0};
static double trajectory[2] = {0, 0};
static char moveSpeed = 70;void resetProjectile(void) {
//set starting random trajectory
randomDirection = 2 * PI * (float) random() / (float) 0×7fffffff;
trajectory[0] = (float) cos(randomDirection);
trajectory[1] = (float) sin(randomDirection);
projMove[0] = midWidth;
projMove[1] = midHeight;
}void display(void)
{
//get window parameters
windowWidth = glutGet(GLUT_WINDOW_WIDTH);
windowHeight = glutGet(GLUT_WINDOW_HEIGHT);midWidth = windowWidth/2;
midHeight = windowHeight/2;//start timing code
if (lastFrameTime == 0) {
lastFrameTime = glutGet(GLUT_ELAPSED_TIME);
resetProjectile(); //since this is the fist time, this is actually a set
}now = glutGet(GLUT_ELAPSED_TIME);
elapsedMilliSeconds = now – lastFrameTime;
elapsedTime = elapsedMilliSeconds/1000.0f;
lastFrameTime = now;glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glShadeModel(GL_FLAT);//calculate move distances
switch (player1Move) {
case 1: //move up
player1V += 150 * elapsedTime;
break;
case -1: //move up
player1V -= 150 * elapsedTime;
break;
default: //do nothing
break;
}
//check out of bounds
if (player1V > (windowHeight/2)-50) {
//bounce back
player1Move = -1;
}
if (player1V < -(windowHeight/2)+50) {
//bounce back other way
player1Move = 1;
}switch (player2Move) {
case 1: //move up
player2V += 150 * elapsedTime;
break;
case -1: //move up
player2V -= 150 * elapsedTime;
break;
default: //do nothing
break;
}
//check out of bounds
if (player2V > (windowHeight/2)-50) {
//bounce back
player2Move = -1;
}
if (player2V < -(windowHeight/2)+50) {
//bounc back other way
player2Move = 1;
}//create and position player 1
glPushMatrix();glColor3ub(255, 0, 0); //red
glTranslatef(0.0f, player1V, 0.0f);
glBegin(GL_QUADS);
glVertex2f(20.0f, (midHeight-50));
glVertex2f(20.0f, (midHeight+50));
glVertex2f(50.0f, (midHeight+50));
glVertex2f(50.0f, (midHeight-50));
glEnd();glPopMatrix();
//create and position player 2
glPushMatrix();glColor3ub(0, 0, 255); //blue
glTranslatef(0.0f, player2V, 0.0f);
glBegin(GL_QUADS);
glVertex2f(windowWidth-20.0f, (midHeight-50));
glVertex2f(windowWidth-20.0f, (midHeight+50));
glVertex2f(windowWidth-50.0f, (midHeight+50));
glVertex2f(windowWidth-50.0f, (midHeight-50));
glEnd();glPopMatrix();
/***************************************
* Projectile CODE!!!
*
***************************************///calculate move distances
projMove[0] += moveSpeed * trajectory[0] * elapsedTime;
projMove[1] += moveSpeed * trajectory[1] * elapsedTime;//create and move projectile
glPushMatrix();glColor3ub(255, 255, 255);
glTranslatef(projMove[0], projMove[1], 0.0f);
glBegin(GL_QUADS);
glVertex2f(-10, -10);
glVertex2f(10, -10);
glVertex2f(10, 10);
glVertex2f(-10, 10);
glEnd();glPopMatrix();
//set bounds for projectile
if (projMove[0] > (windowWidth)) { //player 1 scored – out of bounds right
player1Score++;
printf(”Player 1 Scored! Score: %f “, player1Score);
printf(”projMove[0] %f projMove[1] %f “, projMove[0], projMove[1]);
resetProjectile();
}if (projMove[0] < (0)) { //player 2 scored – out of bounds left
player2Score++;
resetProjectile();
}if (projMove[1] > (windowHeight)) { //out of bounds top
resetProjectile();
}if (projMove[1] < (0)) { //out of bounds bottom
resetProjectile();
}//collision detection code
/******************************
* END PROJECTILE CODE
*
******************************/glutSwapBuffers();
}
void reshape(int width, int height)
{
glViewport(0, 0, width, height);glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, width, 0, height);
glMatrixMode(GL_MODELVIEW);
}void idle(void)
{
glutPostRedisplay();
}void keyboard(unsigned char key, int x, int y) {
switch (key) {
case ‘w’:
//do something
player1Move=1;
glutPostRedisplay();
break;
case ’s’:
//do something
player1Move=-1;
glutPostRedisplay();
break;
case ‘a’:
player1Move=0;
glutPostRedisplay();
break;
case ‘o’:
//do something
player2Move=1;
glutPostRedisplay();
break;
case ‘l’:
//do something
player2Move=-1;
glutPostRedisplay();
break;
case ‘k’:
player2Move=0;
glutPostRedisplay();
break;
default:
//some other key
break;
}}
void keyboardup(unsigned char key, int x, int y) {
switch (key) {
case ‘i’:
//do something
player1Move=0;
glutPostRedisplay();
break;
case ‘u’:
player2Move=0;
glutPostRedisplay();
break;
default:
//some other key
break;
}}
int main(int argc, char** argv)
{
glutInit(&argc, argv);glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
glutInitWindowSize(500, 500);glutCreateWindow(”Tim GL”);
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutKeyboardFunc(keyboard);
glutKeyboardUpFunc(keyboardup);
glutIdleFunc(idle);glutMainLoop();
return EXIT_SUCCESS;
}
Well that’s a lot of code, but don’t be intimidated! If you read through it (maybe a couple of times), you’ll start to understand it. The main loop is short and sweet. We start glut, create a window with it, and then register callback functions for displaying the game, reshaping (what to do if someone changes the size of the window), and for keyboard downpress and release events (keyboard func and keyboardupfunct). Our display function then pretty much does all the work, and I use a bunch of global variables to keep track of state, such as where the projectile and the paddles are. There’s also some brute force vanilla collision detection, which I hope you can appreciate. The funny part is that most real collision detection systems use this concept, called the bounding box. In pong, the bounding box of the paddle is the paddle.
You can get a compiled Mac OS X version here, but if you have your IDE setup right a straight copy and paste should work. (If you had to make any changes, please let me know.)
Let any and all questions loose in the comments..
Hey thanks for the tutorial… the code above compiles (after fixing quotes) but there is no collision dectection. Could you post it?
Thanks again.
@Cdr,
Thanks for checking out the site. I used a snapshot that was just slightly prior to the full collision detection and didn’t notice. (Sorry!) But I just put up the latest snapshot of the code up for you, which includes all that and more. I’ve put it in another post: http://laststop.spaceislimited.org/2009/01/19/programming-pong-in-c-and-opengl-part-vi/