8cf082ba55fe51a67b9c06ddb2daa2beedc1ebf9
[pong] / pong.c
1 #if defined(WIN32)
2 #pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
3 #include "glut.h"
4 #elif defined(__APPLE__) || defined(MACOSX)
5 #include <GLUT/glut.h>
6 #else
7 #include <GL/glut.h>
8 #endif
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <math.h>
13
14 // Amount to increase speed by on x, y axis, and when bouncing
15 #define SPEEDXINC 1.1
16 #define SPEEDYINC 1.1
17 #define SPEEDBINC 0.15
18 #define PADDLESPEED 3.0
19 #define FRAME 40
20 #define PADDLESIZE 10
21 #define HEIGHT 100
22
23 #define STATE_UI 0
24 #define STATE_1P 1
25 #define STATE_2P 2
26
27 static char* menu = "";
28 static int state = 0;
29 static int p1move = 0;
30 static int p2move = 0;
31 static int score1 = 0;
32 static int score2 = 0;
33 static GLdouble paddle1 = 0.0;
34 static GLdouble paddle2 = 0.0;
35 static GLdouble ballX = 0.0;
36 static GLdouble ballY = 0.0;
37 static GLdouble ballVecX = 0.0;
38 static GLdouble ballVecY = 0.0;
39
40
41 static inline double fmax(double a, double b) {
42 return a > b ? a : b;
43 }
44
45 static void initBall(void) {
46 ballX = 0.0;
47 ballY = 0.0;
48 if(ballVecX < 0)
49 ballVecX = -1.2;
50 else
51 ballVecX = 1.2;
52 ballVecY = 0.7;
53 }
54
55 static void run(void) {
56 // AI is on
57 if(state == STATE_1P) {
58 if(ballY < paddle2 - PADDLESIZE)
59 p2move = -1;
60 else if(ballY > paddle2 + PADDLESIZE)
61 p2move = 1;
62 else
63 p2move = 0;
64 }
65
66 // Move the paddles
67 paddle1 += p1move * PADDLESPEED;
68 paddle2 += p2move * PADDLESPEED;
69 if(paddle1 < -HEIGHT)
70 paddle1 = -HEIGHT;
71 if(paddle1 > HEIGHT)
72 paddle1 = HEIGHT;
73 if(paddle2 < -HEIGHT)
74 paddle2 = -HEIGHT;
75 if(paddle2 > HEIGHT)
76 paddle2 = HEIGHT;
77
78 // Check for collisions with paddles
79 if(ballVecX < 0 && ballX <= -HEIGHT + -ballVecX * 1.5) {
80 if(ballY >= paddle1 - PADDLESIZE && ballY <= paddle1 + PADDLESIZE) {
81 GLdouble speedChange = fmax(1.0, fabs(paddle1 - ballY) * SPEEDBINC);
82 ballVecX *= -speedChange * SPEEDXINC;
83 ballVecY *= speedChange * SPEEDYINC;
84 }
85 }
86 if(ballVecX > 0 && ballX >= HEIGHT - ballVecX * 1.5) {
87 if(ballY >= paddle2 - PADDLESIZE && ballY <= paddle2 + PADDLESIZE) {
88 GLdouble speedChange = fmax(1.0, fabs(paddle2 - ballY) * SPEEDBINC);
89 ballVecX *= -speedChange * SPEEDXINC;
90 ballVecY *= speedChange * SPEEDYINC;
91 }
92 }
93
94 // Check if it's past the top or bottom of the screen
95 if(ballY >= HEIGHT || ballY <= -HEIGHT) {
96 ballVecY = -ballVecY;
97 }
98
99 // Check if it's past the sides of the screen
100 if(ballX >= HEIGHT) {
101 ++score1;
102 initBall();
103 }
104 if(ballX <= -HEIGHT) {
105 ++score2;
106 initBall();
107 }
108
109 // Move the ball
110 ballX += ballVecX;
111 ballY += ballVecY;
112
113 // Check scores for winners..
114 if(score1 == 9) {
115 menu = "Player 1 wins!";
116 state = STATE_UI;
117 score1 = score2 = 0;
118 }
119 if(score2 == 9) {
120 menu = "Player 2 wins!";
121 state = STATE_UI;
122 score1 = score2 = 0;
123 }
124 }
125
126 static void timer(int lastTime) {
127 if(state == STATE_UI) {
128 return;
129 }
130
131 int curTime = glutGet(GLUT_ELAPSED_TIME);
132 do {
133 lastTime += FRAME;
134 run();
135 } while(lastTime + FRAME < curTime);
136 glutPostRedisplay();
137 glutTimerFunc(FRAME - (curTime - lastTime), timer, curTime);
138 }
139
140 static void startGame(void) {
141 glutTimerFunc(FRAME, timer, glutGet(GLUT_ELAPSED_TIME));
142 initBall();
143 }
144
145 static void drawText(double x, double y, char* str) {
146 glRasterPos2d(x, y);
147 while(*str) {
148 glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, *str);
149 ++str;
150 }
151 }
152
153 static void menuDisplay(void) {
154 drawText(-20, 50, "Awesome PONG!");
155 drawText(-50, -50, "Press 1 for a single player game.");
156 drawText(-50, -60, "Press 2 for a multi player game.");
157 drawText(-50, -70, "Keys: w/s and i/k");
158 drawText(-20, 0, menu);
159 }
160
161 static void gameDisplay(void) {
162 // Draw the paddles
163 glLineWidth(2.0);
164 glBegin(GL_LINES);
165 glVertex2d(-HEIGHT + 1, paddle1 - PADDLESIZE);
166 glVertex2d(-HEIGHT + 1, paddle1 + PADDLESIZE);
167 glVertex2d( HEIGHT - 1, paddle2 + PADDLESIZE);
168 glVertex2d( HEIGHT - 1, paddle2 - PADDLESIZE);
169 glEnd();
170
171 // Draw the ball
172 glBegin(GL_LINES);
173 glVertex2d(ballX, ballY);
174 glVertex2d(ballX - ballVecX, ballY - ballVecY);
175 glEnd();
176
177 // Write the score
178 glRasterPos2d(-5.0, HEIGHT - 10.0);
179 glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, '0' + score1);
180 glRasterPos2d( 5.0, HEIGHT - 10.0);
181 glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, '0' + score2);
182 }
183
184 static void display(void) {
185 glClear(GL_COLOR_BUFFER_BIT);
186 glColor3d(0.0, 0.0, 0.0);
187
188 if(state == STATE_UI) {
189 menuDisplay();
190 } else {
191 gameDisplay();
192 }
193
194 glutSwapBuffers();
195 }
196
197 static void resize(int w, int h) {
198 glViewport(0, 0, w, h);
199 glLoadIdentity();
200 glOrtho(-HEIGHT, HEIGHT, -HEIGHT, HEIGHT, -1.0, 1.0);
201 }
202
203 static void menuKeyboard(unsigned char key) {
204 switch(key) {
205 case '1':
206 state = STATE_1P;
207 break;
208
209 case '2':
210 state = STATE_2P;
211 break;
212
213 default:
214 return;
215 }
216 startGame();
217 }
218
219 static void gameKeyboard(unsigned char key) {
220 switch(key) {
221 case 'w':
222 p1move = 1;
223 break;
224 case 's':
225 p1move = -1;
226 break;
227
228 case 'i':
229 p2move = 1;
230 break;
231 case 'k':
232 p2move = -1;
233 break;
234
235 default:
236 return;
237 }
238 glutPostRedisplay();
239 }
240
241 static void keyboard(unsigned char key, int x, int y) {
242 (void)x;(void)y;
243
244 switch(key) {
245 case 'q':
246 case 'Q':
247 case '\033':
248 exit(0);
249 }
250
251
252 if(state == STATE_UI) {
253 menuKeyboard(key);
254 } else {
255 gameKeyboard(key);
256 }
257 }
258
259 static void keyboardUp(unsigned char key, int x, int y) {
260 (void)x;(void)y;
261
262 switch(key) {
263 case 'w':
264 case 's':
265 p1move = 0;
266 break;
267
268 case 'i':
269 case 'k':
270 p2move = 0;
271 p2move = 0;
272 break;
273 }
274 }
275
276 int main(int argc, char *argv[]) {
277 glutInitWindowPosition(100, 100);
278 glutInitWindowSize(640, 480);
279 glutInit(&argc, argv);
280 glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
281 glutCreateWindow(argv[0]);
282 glutDisplayFunc(display);
283 glutReshapeFunc(resize);
284 glutKeyboardFunc(keyboard);
285 glutKeyboardUpFunc(keyboardUp);
286 glClearColor(1.0, 1.0, 1.0, 0.0);
287 glutMainLoop();
288 return 0;
289 }
290