Advertisement

pong.c

Started by September 06, 2001 06:06 PM
11 comments, last by bpj1138 23 years, 2 months ago
Here's my PONG source code.. This game program should compile on Windows using the mingw gcc compiler. You will also need DirectX header files. MinGW C compiler: http://www.mingw.org DirectX header files mirror: http://www.geocities.com/bpj1138/directx6_1-cyg.tar.gz compiler commands: gcc -O2 -m486 -mwindows -o pong pong.c -lopengl32 -lglu32 -lglut32 -ldsound strip pong.exe


/*
 * Pong 
 *
 *   This implementation of the classic video game uses
 * OpenGL, GLUT, and DirectSound libraries.
 *
 * Author:
 * Bart Jaszcz
 * bpj@po.cwru.edu
 * http://www.geocities.com/bpj1138/
 *
 */

#include <windows.h>
#include <dsound.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include <math.h>
#include <string.h>

void setupGraphics(int, char**);			// init GLUT and OpenGL
void animateCallback();					// main game loop
void displayCallback();					// redraw screen
void reshapeCallback(int, int);				// resize screen
void keyboardCallback(unsigned char, int, int);		// key pressed
void keyboardUpCallback(unsigned char, int, int);	// key released	
void specialCallback(int, int, int);			// non-ascii key press
void specialUpCallback(int, int, int);			// non-ascii key release
void visibilityCallback(int);				// if window visible
void refreshScore();					// refresh scoreboard
void setupSound();					// init DirectSound
LPDIRECTSOUNDBUFFER createSound(double, double);	// generate sound clip
void playSound(LPDIRECTSOUNDBUFFER);			// play sound clip

// sound variables

LPDIRECTSOUNDBUFFER lpDSB1, lpDSB2, lpDSB3;
LPDIRECTSOUND lpDS;
PCMWAVEFORMAT pcmwf;
int soundEnabled = FALSE;

// graphics variables

HWND hWnd;					// window handle
int windowId = -1;				// GLUT window ID
int viewportWidth = -1;				// screen width
int viewportHeight = -1;			// screen height
int visibilityState = GLUT_NOT_VISIBLE;		// if window visible

// player 1 & 2

#define PLAYER_ONE 0
#define PLAYER_TWO 1

// game states

#define GAME_STOPPED 0
#define GAME_ACTIVE 1
#define BALL_MISSED 2

// player 1 & 2 variables 

float p1 = 0.0f, p2 = 0.0f;		// player 1 & 2 y positions
float p1v = 0.0f, p2v = 0.0f;		// player 1 & 2 y velocities

// ball variables

float bx = 0.0f, by = 0.0f;		// ball x, y position
float vx = 0.0f, vy = 0.0f;		// ball velocity

// initial game state

int gameState = GAME_STOPPED;
int nextPlayer = PLAYER_ONE;
int playerOnePoints = 0;
int playerTwoPoints = 0;
int lastTickCount = 0;

// dimensions and constants

#define BALL_SPEED 2.663f
#define PADDLE_SPEED 1.856f
#define PADDLE_OFFSET 0.923f
#define PADDLE_WIDTH 0.021f
#define PADDLE_HEIGHT 0.128f
#define WALL_OFFSET 0.735f
#define WALL_THICKNESS 0.037f
#define BALL_SIZE 0.025f
#define DEFLECT 0.081f
#define WALL (WALL_OFFSET - WALL_THICKNESS - BALL_SIZE)
#define PLANE (PADDLE_OFFSET - PADDLE_WIDTH - BALL_SIZE)

// text font

void *font = GLUT_BITMAP_TIMES_ROMAN_24;
char scoreString[16];
int scoreLength;
double scoreWidth;

// main loop

void animateCallback() {
	float elapsedTime;
	int currentTickCount;
	float displacement, distance; 
	float py, dx, dy;
	// get elapsed time
	currentTickCount = GetTickCount();
	elapsedTime = (float) (currentTickCount - lastTickCount) * 0.001f;	// in seconds
	lastTickCount = currentTickCount;
	// make sure elapsed time is in range
	if (elapsedTime < 0.001f)
		elapsedTime = 0.001f;
	if (elapsedTime > 0.1f)
		elapsedTime = 0.1f;
	// multiply ball velocity by elapsed time
	dx = vx * elapsedTime;
	dy = vy * elapsedTime;
	// move ball
	bx += dx;
	by += dy;
	// check if ball is colliding with wall
	if ((vy > 0.0f) && (by > WALL)) {
		playSound(lpDSB2);
		// correct ball position
		distance = by - WALL;		// y overshot
		by -= distance;			// subtract from y
		// calculate x displacement of same magnitude
		displacement = distance * dx / dy;
		bx -= displacement;		// subtract from x
		// negate y velocity (bounce)
		vy = -vy;
	}
	if ((vy < 0.0f) && (by < -WALL)) {
		playSound(lpDSB2);
		// correct ball position
		distance = -by - WALL;		// y overshot
		by += distance;			// add to y
		// calculate x displacement of same magnitude
		displacement = distance * dx / dy;
		bx -= displacement;		// subtract from x
		// negate y velocity (bounce)
		vy = -vy;
	}
	// move player two
	if (p2v != 0.0f) {
		if (p2v > 0.0f) {
			if (p2 < (WALL_OFFSET - WALL_THICKNESS - PADDLE_HEIGHT))
				p2 += p2v * elapsedTime;
		}
		else {
			if (p2 > (-WALL_OFFSET + WALL_THICKNESS + PADDLE_HEIGHT))
				p2 += p2v * elapsedTime;
		}
	}
	// move player one
	if (p1v != 0.0f) {
		if (p1v > 0.0f) {
			if (p1 < (WALL_OFFSET - WALL_THICKNESS - PADDLE_HEIGHT))
				p1 += p1v * elapsedTime;
		}
		else {
			if (p1 > (-WALL_OFFSET + WALL_THICKNESS + PADDLE_HEIGHT))
				p1 += p1v * elapsedTime;
		}
	}
	// check for collision with paddles
	if (gameState == GAME_ACTIVE) {
		// player two
		if ((vx > 0.0f) && (bx > PLANE)) {
			// figure out where ball crossed paddle plane
			distance = bx - PLANE;			// x overshot
			displacement = distance * dy / dx;	// y displacement of the same magnitude
			py = by - displacement;			// y position where ball crossed plane
			// calculate distance from where ball crossed plane to where paddle is.
			displacement = p2 - py;
			distance = (float) fabs((double) displacement);
			if (distance > (PADDLE_HEIGHT + BALL_SIZE)) {
				// player two missed ball
				gameState = BALL_MISSED;
				nextPlayer = PLAYER_TWO;
			}
			else {
				// player two returned ball, calculate new velocity
				playSound(lpDSB1);
				vy = BALL_SPEED * distance / (PADDLE_HEIGHT + BALL_SIZE + DEFLECT);
				vx = vy - BALL_SPEED;
				// get correct direction
				if (displacement > 0.0f)
					vy = -vy;
			}
		}	
		// player one
		if ((vx < 0.0f) && (bx < -PLANE)) {
			// figure out where ball crossed paddle plane
			distance = -bx - PLANE;			// x overshot
			displacement = distance * dy / dx;	// y displacement of the same magnitude
			py = by - displacement;			// y position where ball crossed plane
			// calculate distance from where ball crossed plane to where paddle is.
			displacement = p1 - py;
			distance = (float) fabs((double) displacement);
			if (distance > (PADDLE_HEIGHT + BALL_SIZE)) {
				// player one missed ball
				gameState = BALL_MISSED;
				nextPlayer = PLAYER_ONE;
			}
			else {
				// player one returned ball, calculate new velocity
				playSound(lpDSB1);
				vy = BALL_SPEED * distance / (PADDLE_HEIGHT + BALL_SIZE + DEFLECT);
				vx = BALL_SPEED - vy;
				if (displacement  > 0.0f)
					vy = -vy;
			}
		}
	}
	if (gameState == BALL_MISSED) {
		// reset ball once it moves out of view
		if ((vx > 0.0f) && (bx > (1.0f + BALL_SIZE))) {
			playSound(lpDSB3);
			gameState = GAME_STOPPED;
			bx = by = vx = vy = 0.0f;		// zero ball position/velocity
			playerOnePoints++;
			refreshScore();
		}
		if ((vx < 0.0f) && (bx < -(1.0f + BALL_SIZE))) {
			playSound(lpDSB3);
			gameState = GAME_STOPPED;
			bx = by = vx = vy = 0.0f;		// zero ball position/velocity
			playerTwoPoints++;
			refreshScore();
		}
	}	
	// only update the screen if window is visible
	if (visibilityState == GLUT_VISIBLE)
		glutPostRedisplay();
}

// draw screen

void displayCallback() {
	int i;
	double w, h;
	// clear screen
	glClear(GL_COLOR_BUFFER_BIT);
	// draw score
        glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	w = (double) viewportWidth;
	h = (double) viewportHeight;
        gluOrtho2D(0.0, w, 0.0, h);
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
        glLoadIdentity();
	glRasterPos2d((w / 2.0) - (scoreWidth / 2.0), h * 0.9); 
	for (i = 0; i < scoreLength; i++)
	        glutBitmapCharacter(font, scoreString);
        glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();
	// draw polygons
	glBegin(GL_QUADS);
	// draw top wall
	glVertex2f(1.0f, WALL_OFFSET + WALL_THICKNESS);
	glVertex2f(-1.0f, WALL_OFFSET + WALL_THICKNESS);
	glVertex2f(-1.0f, WALL_OFFSET - WALL_THICKNESS);
	glVertex2f(1.0f, WALL_OFFSET - WALL_THICKNESS);
	// draw bottom wall
	glVertex2f(1.0f, -WALL_OFFSET + WALL_THICKNESS);
	glVertex2f(-1.0f, -WALL_OFFSET + WALL_THICKNESS);
	glVertex2f(-1.0f, -WALL_OFFSET - WALL_THICKNESS);
	glVertex2f(1.0f, -WALL_OFFSET - WALL_THICKNESS);	
	// draw ball
	glVertex2f(bx + BALL_SIZE, by + BALL_SIZE);
	glVertex2f(bx - BALL_SIZE, by + BALL_SIZE);
	glVertex2f(bx - BALL_SIZE, by - BALL_SIZE);
	glVertex2f(bx + BALL_SIZE, by - BALL_SIZE);
	// draw player 2
	glVertex2f(PADDLE_OFFSET + PADDLE_WIDTH, p2 + PADDLE_HEIGHT);
	glVertex2f(PADDLE_OFFSET - PADDLE_WIDTH, p2 + PADDLE_HEIGHT);
	glVertex2f(PADDLE_OFFSET - PADDLE_WIDTH, p2 - PADDLE_HEIGHT);
	glVertex2f(PADDLE_OFFSET + PADDLE_WIDTH, p2 - PADDLE_HEIGHT);
	// draw player 1
	glVertex2f(-PADDLE_OFFSET + PADDLE_WIDTH, p1 + PADDLE_HEIGHT);
	glVertex2f(-PADDLE_OFFSET - PADDLE_WIDTH, p1 + PADDLE_HEIGHT);
	glVertex2f(-PADDLE_OFFSET - PADDLE_WIDTH, p1 - PADDLE_HEIGHT);
	glVertex2f(-PADDLE_OFFSET + PADDLE_WIDTH, p1 - PADDLE_HEIGHT);
	glEnd();
	glFlush();
	glutSwapBuffers();
}

// keyboard input

void keyboardCallback(unsigned char key, int x, int y) {
	// player 1 key press
	switch (key) {
		// player one
		case 'W': 
		case 'w': 
			p1v = PADDLE_SPEED;
			break;
		case 'S':
		case 's':
			p1v = -PADDLE_SPEED; 
			break;
		case 'D':
		case 'd':
			if ((gameState == GAME_STOPPED) && (nextPlayer == PLAYER_ONE)) {
				if (playerTwoPoints == 10) {
					playerOnePoints = 0;
					playerTwoPoints = 0;
					refreshScore();
				}
				vx = -BALL_SPEED;
				gameState = GAME_ACTIVE;
			}
			break;
		case 27:	// ESCAPE
			exit(0);
	}
}

void specialCallback(int key, int x, int y) {
	// player 2 key press
	switch (key) {
		// player two
		case GLUT_KEY_UP: 
			p2v = PADDLE_SPEED;
			break;
		case GLUT_KEY_DOWN:
			p2v = -PADDLE_SPEED; 
			break;
		case GLUT_KEY_LEFT:
			if ((gameState == GAME_STOPPED) && (nextPlayer == PLAYER_TWO)) {
				if (playerOnePoints == 10) {
					playerOnePoints = 0;
					playerTwoPoints = 0;
					refreshScore();
				}
				vx = BALL_SPEED;
				gameState = GAME_ACTIVE;
			}
			break;
	}
}

void keyboardUpCallback(unsigned char key, int x, int y) {
	// player 1 key release
	switch (key) {
		case 'W': 
		case 'w' : 
			if (p1v > 0.0f)
				p1v = 0.0f; 
			break;
		case 'S' :
		case 's' : 
			if (p1v < 0.0f)
				p1v = 0.0f; 
			break;
	}
}

void specialUpCallback(int key, int x, int y){
	// player 2 key release
	switch (key) {
		case GLUT_KEY_UP: 
			if (p2v > 0.0f)
				p2v = 0.0f; 
			break;
		case GLUT_KEY_DOWN:
			if (p2v < 0.0f)
				p2v = 0.0f; 
			break;
	}
}

void refreshScore() {
	int i;
	sprintf(scoreString, "%d/%d", playerOnePoints, playerTwoPoints);
	scoreLength = strlen(scoreString);
	scoreWidth = 0.0;
	for (i = 0; i < scoreLength; i++)
		scoreWidth += (double) glutBitmapWidth(font, scoreString);
}

void reshapeCallback(int width, int height) {
	viewportWidth = width;
	viewportHeight = height;
	glViewport(0, 0, viewportWidth, viewportHeight);
	glutPostRedisplay();
}

void setupGraphics(int argc, char **argv) {	
	GLfloat ambientLight[] = { 1.0f, 1.0f, 1.0f, 1.0f };
	GLfloat emissiveLight[] = { 0.0f, 0.0f, 0.0f, 1.0f };
	GLfloat diffuseLight[] = { 1.0f, 1.0f, 1.0f, 1.0f };
	GLfloat specularLight[] = { 1.0f, 1.0f, 1.0f, 1.0f };

	GLfloat materialAmbient[] = { 0.1f, 0.1f, 0.1f, 1.0f };
	GLfloat materialDiffuse[] = { 0.1f, 0.1f, 0.1f, 1.0f };
	GLfloat materialEmission[] = { 0.0f, 0.0f, 0.0f, 1.0f };
	GLfloat materialSpecular[] = { 1.0f, 1.0f, 1.0f, 1.0f };

	GLfloat lightPosition[] = { 0.0f, 0.0f, 1.0f, 1.0f };
	
	// setup GLUT

	viewportWidth = 374; viewportHeight = 200;
	glutInitWindowSize(viewportWidth, viewportHeight);
	glutInitWindowPosition(70, 70);
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
	windowId = glutCreateWindow("Pong");
	glutSetWindow(windowId);
	glutDisplayFunc(displayCallback);
	glutReshapeFunc(reshapeCallback);
	glutVisibilityFunc(visibilityCallback);
	glutKeyboardFunc(keyboardCallback);
	glutKeyboardUpFunc(keyboardUpCallback);
	glutSpecialFunc(specialCallback);
	glutSpecialUpFunc(specialUpCallback);
	glutIdleFunc(animateCallback);
	glutMouseFunc(NULL);
	glutMotionFunc(NULL);
	glutPassiveMotionFunc(NULL);
	glutShowWindow();

	// get window handle

	hWnd = FindWindow(NULL, "Pong");

	// setup OpenGL

	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
	glEnable(GL_LIGHTING);
	glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight);
	glLightfv(GL_LIGHT0, GL_EMISSION, emissiveLight);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);
	glLightfv(GL_LIGHT0, GL_SPECULAR, specularLight);
	glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
	glEnable(GL_LIGHT0);
	glEnable(GL_BLEND);
	glEnable(GL_COLOR_MATERIAL);
	glShadeModel(GL_SMOOTH);
	glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
	glMaterialfv(GL_FRONT, GL_AMBIENT, materialAmbient);
	glMaterialfv(GL_FRONT, GL_DIFFUSE, materialDiffuse);
	glMaterialfv(GL_FRONT, GL_EMISSION, materialEmission);
	glMaterialfv(GL_FRONT, GL_SPECULAR, materialSpecular);
	glMateriali(GL_FRONT, GL_SHININESS, 128);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_NORMALIZE);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
}

void visibilityCallback(int state) {
	visibilityState = state;
	if (state == GLUT_VISIBLE)
		glutPostRedisplay();		
}

void setupSound() {
	if (DirectSoundCreate(NULL, &lpDS, NULL) == DS_OK) {
		if (IDirectSound_SetCooperativeLevel(lpDS, hWnd, DSSCL_NORMAL) == DS_OK) {
			// set up wave format structure
			memset(&pcmwf, 0, sizeof(PCMWAVEFORMAT));
			pcmwf.wf.wFormatTag = WAVE_FORMAT_PCM;
			pcmwf.wf.nChannels = 1;
			pcmwf.wf.nSamplesPerSec = 44100;
			pcmwf.wBitsPerSample = 16;
			pcmwf.wf.nBlockAlign = pcmwf.wf.nChannels * pcmwf.wBitsPerSample / 8;
			pcmwf.wf.nAvgBytesPerSec = pcmwf.wf.nSamplesPerSec * pcmwf.wf.nBlockAlign;
			soundEnabled = TRUE;
		}
	}		
}

LPDIRECTSOUNDBUFFER createSound(double frequency, double seconds) {
	LPDIRECTSOUNDBUFFER lpDSB;
	DSBUFFERDESC dsbdesc;
	HRESULT hr;
	DWORD samples, bytes;
	unsigned short *buffer;
	double angle, value, increment;
	int i;
	// locked area of direct sound buffer
	LPVOID lpvPtr1, lpvPtr2;
	DWORD dwBytes1, dwBytes2;
	// make sure direct sound was initialized
	if (!soundEnabled)
		return;
	// set up direct sound buffer descriptor
	memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
	dsbdesc.dwSize = sizeof(DSBUFFERDESC);
	dsbdesc.dwFlags = DSBCAPS_CTRLDEFAULT;
	samples = (int) (44100.0 * seconds);
	bytes = pcmwf.wf.nBlockAlign * samples;
	dsbdesc.dwBufferBytes = bytes;
	dsbdesc.lpwfxFormat = (LPWAVEFORMATEX) &pcmwf;
	// create direct sound buffer
	hr = IDirectSound_CreateSoundBuffer(lpDS, &dsbdesc, &lpDSB, NULL);
	if (!SUCCEEDED(hr)) {
		soundEnabled = FALSE;
		return;
	}
	// lock buffer
	hr = IDirectSoundBuffer_Lock(lpDSB, 0, bytes, &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0);
	if (hr == DSERR_BUFFERLOST) {
		IDirectSoundBuffer_Restore(lpDSB);
		hr = IDirectSoundBuffer_Lock(lpDSB, 0, bytes, &lpvPtr1, 
			&dwBytes1, &lpvPtr2, &dwBytes2, 0);
	}
	// write to buffer
	if (SUCCEEDED(hr)) {
		buffer = (unsigned short *) lpvPtr1;
		angle = 0.0;
		increment = frequency * seconds / (2.0 * 3.14159);
		for (i = 0; i < samples; i++) {
			value = sin(angle);
			buffer = (unsigned short) ((value + 1.0) * 32768.0);
			angle += increment; 
		}
		IDirectSoundBuffer_Unlock(lpDSB, lpvPtr1, dwBytes1, lpvPtr2, dwBytes2);
	}
	else {
		soundEnabled = FALSE;
	}
	return lpDSB;
}

void playSound(LPDIRECTSOUNDBUFFER lpDSB) {
	if (!soundEnabled)
		return;
	IDirectSoundBuffer_SetCurrentPosition(lpDSB, 0);
	if (!SUCCEEDED(IDirectSoundBuffer_Play(lpDSB, 0, 0, 0)))
		soundEnabled = FALSE;
}

int main(int argc, char **argv) {
	setupGraphics(argc, argv);
	setupSound();
	lpDSB1 = createSound(3000.0, 0.12);
	lpDSB2 = createSound(1200.0, 0.04);
	lpDSB3 = createSound(750.0, 0.23);
	refreshScore();
	lastTickCount = GetTickCount();
	glutMainLoop();
	return 0;
}
  </pre>   


Edited by - bpj1138 on September 6, 2001 7:09:07 PM    </i>  

Edited by - bpj1138 on September 6, 2001 7:12:55 PM

Edited by - bpj1138 on September 6, 2001 7:18:30 PM    
--bart
For future reference, you might want to refrain from doing that.



------------------------------
"I''m a decorated astronaut, I don''t make those kind of mistakes."
"Oh now wait a minute. Look I''ll show ya. I''ll enter the same calculations using what we like to call ''The Right Way''."

-Rem
Zirem Software
------------------------------"I'm a decorated astronaut, I don't make those kind of mistakes.""Oh now wait a minute. Look I'll show ya. I'll enter the same calculations using what we like to call 'The Right Way'."-RemZirem Software
Advertisement
what is your problem?
--bart
You should better upload your source code to a site like geocities and give the URL on the board. It''s far more convenient and don''t use as much bandwidth of the GameDev''s server.

And by the way, an exe version of the executable may be quite convenient too.
look sir, i didn''t ask for your opinion, and i assume neither did gamedev. if you are in fact, game dev police, i will remove my message.

as to posting it here as text, that''s exactly what i wanted to do, seeing how this is a game development board about coding games, right? and i''m gonna continue to do so regardless of how concerned you are about gamedev''s bandwith.

good day..
--bart
Ah, this is funny. Especially the bit where bpj1138 tries to justify doing that, considering it would have taken no effort to put that on geocities! He must be having problems with reality.
Advertisement
Take note that I have been watching this thread. I opted not to take action on this thread for a couple of reasons. None of which I have to explain...

As for the lengthy post, the nice and appropriate thing to do would be to find free hosting on a site (like geocities for example) and then give the URL like has already been mentioned. The other alternative would be to submit an artical to gamedev along the lines of "Beginning Game Programming -- How To Make Pong" for example if you wish to share your ideas with others in this kind of format.

Please don''t think that I am jumping your case as I do welcome you here to this forum, site, and your willingness to share information with others. I would suggest watching the threads for a few weeks and going through the topics already posted and get a feel for how things work. That''s just good advice in general regardless of which on-line community you visit.



YAP-YFIO

-deadlinegrunt

~deadlinegrunt

the reality is that this is a game development board, i posted some code, that''s the reality.

--bart
The reality is that the vast majority don''t like to see more than ~50 lines of code with the rare exception of someone asking for it. It would be great if you could give some respect to some of the norms of the boards you frequent. Thanks,


Mike
"Unintentional death of one civilian by the US is a tragedy; intentional slaughter of a million by Saddam - a statistic." - Unknown
Oh, excuse me for coding pong in more than 50 lines of code, I should have tried harder..
--bart

This topic is closed to new replies.

Advertisement