/*
* 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
pong.c
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
--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''."
-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
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.
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..
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.
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
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
Mike
"Unintentional death of one civilian by the US is a tragedy; intentional slaughter of a million by Saddam - a statistic." - Unknown
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement