comparison examples/10-Instruments/tank_wars/game.cpp @ 464:8fcfbfb32aa0 prerelease

Examples reorder with subdirectories. Added header to each project. Moved Doxygen to bottom of render.cpp.
author Robert Jack <robert.h.jack@gmail.com>
date Mon, 20 Jun 2016 16:20:38 +0100
parents
children
comparison
equal deleted inserted replaced
463:c47709e8b5c9 464:8fcfbfb32aa0
1 /*
2 * game.cpp
3 *
4 * Created on: Nov 10, 2014
5 * Author: parallels
6 */
7
8 #include <cmath>
9 #include <cstdlib>
10 #include "vector_graphics.h"
11 #include <Utilities.h>
12
13 // Virtual screen size
14 int screenWidth, screenHeight;
15
16 // Basic information on the terrain and the tanks
17 float *groundLevel; // Y coordinate of the ground for each X coordinate
18 float tank1X, tank1Y, tank2X, tank2Y; // Positions of the two tanks
19 float tankRadius = 20; // Radius of the tanks
20 float cannonLength = 40; // How long the cannon on each tank extends
21 float gravity = 0.05; // Strength of gravity
22
23 // Current state of the game
24 int playerHasWon = 0; // 1 if player 1 wins, 2 if player 2 wins, 0 if game in progress
25 bool player1Turn = true; // true if it's player 1's turn; false otherwise
26 float tank1CannonAngle = M_PI/2;
27 float tank2CannonAngle = M_PI/2; // Direction the tank cannons are pointing
28 float tank1CannonStrength = 3;
29 float tank2CannonStrength = 3; // Strength of intended projectile launch
30
31 // Location of the projectile
32 bool projectileInMotion = false;
33 float projectilePositionX, projectilePositionY;
34 float projectileVelocityX, projectileVelocityY;
35
36 // Infor needed for sound rendering
37 bool collisionJustOccurred = false;
38 bool tankHitJustOccurred = false;
39
40 // Useful utility function for generating random floating-point values
41 float randomFloat(float low, float hi)
42 {
43 float r = (float)random() / (float)RAND_MAX;
44 return map(r, 0, 1, low, hi);
45 }
46
47 // Restart the game, without reallocating memory
48 void restartGame()
49 {
50 float player1Height = screenHeight * 3/4; // randomFloat(screenHeight/2, screenHeight-5);
51 float player2Height = screenHeight - 5; // randomFloat(screenHeight/2, screenHeight-5);
52 for(int i = 0; i < screenWidth * 0.2; i++) {
53 groundLevel[i] = player1Height;
54 }
55 for(int i = screenWidth * 0.2; i < screenWidth * 0.8; i++) {
56 groundLevel[i] = player1Height + (player2Height - player1Height) * (i - screenWidth*0.2)/(screenWidth*0.6);
57 }
58 for(int i = screenWidth * 0.8; i < screenWidth; i++) {
59 groundLevel[i] = player2Height;
60 }
61
62 // Set the location of the two tanks so they rest on the ground at opposite sides
63 tank1X = screenWidth * 0.1;
64 tank1Y = player1Height;
65 tank2X = screenWidth * 0.9;
66 tank2Y = player2Height;
67
68 playerHasWon = 0;
69 projectileInMotion = false;
70 }
71
72 // Initialise the game
73 void setupGame(int width, int height)
74 {
75 // Set the screen size
76 screenWidth = width;
77 screenHeight = height;
78
79 // Initialize the ground level
80 groundLevel = new float[screenWidth];
81
82 restartGame();
83 }
84
85 // Advance the turn to the next player
86 void nextPlayersTurn() {
87 player1Turn = !player1Turn;
88 }
89
90
91 // Move forward one frame on the game physics
92 void nextGameFrame()
93 {
94 if(!projectileInMotion)
95 return;
96
97 // Update position of projectile
98 projectilePositionX += projectileVelocityX;
99 projectilePositionY += projectileVelocityY;
100 projectileVelocityY += gravity;
101
102 // Check collision with tanks first: a collision with tank 1 means player 2 wins and vice-versa
103 if((tank1X - projectilePositionX)*(tank1X - projectilePositionX) +
104 (tank1Y - projectilePositionY)*(tank1Y - projectilePositionY)
105 <= tankRadius * tankRadius)
106 {
107 projectileInMotion = false;
108 collisionJustOccurred = false;
109 tankHitJustOccurred = true;
110 playerHasWon = 2;
111 }
112 else if((tank2X - projectilePositionX)*(tank2X - projectilePositionX) +
113 (tank2Y - projectilePositionY)*(tank2Y - projectilePositionY)
114 <= tankRadius * tankRadius)
115 {
116 projectileInMotion = false;
117 collisionJustOccurred = false;
118 tankHitJustOccurred = true;
119 playerHasWon = 1;
120 }
121 else if(projectilePositionX < 0 || projectilePositionX >= screenWidth) {
122 // Check collision whether projectile has exited the screen to the left or right
123 projectileInMotion = false;
124 collisionJustOccurred = true;
125 nextPlayersTurn();
126 }
127 else if(projectilePositionY >= groundLevel[(int)floorf(projectilePositionX)]) {
128 // Check for projectile collision with ground
129 projectileInMotion = false;
130 collisionJustOccurred = true;
131 nextPlayersTurn();
132 }
133 }
134
135 // Updates for game state
136 void setTank1CannonAngle(float angle)
137 {
138 tank1CannonAngle = angle;
139 }
140
141 void setTank2CannonAngle(float angle)
142 {
143 tank2CannonAngle = angle;
144 }
145
146 void setTank1CannonStrength(float strength)
147 {
148 tank1CannonStrength = strength;
149 }
150
151 void setTank2CannonStrength(float strength)
152 {
153 tank2CannonStrength = strength;
154 }
155
156 // FIRE!
157 void fireProjectile()
158 {
159 // Can't fire while projectile is already moving, or if someone has won
160 if(projectileInMotion)
161 return;
162 if(playerHasWon != 0)
163 return;
164
165 if(player1Turn) {
166 projectilePositionX = tank1X + cannonLength * cosf(tank1CannonAngle);
167 projectilePositionY = tank1Y - cannonLength * sinf(tank1CannonAngle);
168 projectileVelocityX = tank1CannonStrength * cosf(tank1CannonAngle);
169 projectileVelocityY = -tank1CannonStrength * sinf(tank1CannonAngle);
170 }
171 else {
172 projectilePositionX = tank2X + cannonLength * cosf(tank2CannonAngle);
173 projectilePositionY = tank2Y - cannonLength * sinf(tank2CannonAngle);
174 projectileVelocityX = tank2CannonStrength * cosf(tank2CannonAngle);
175 projectileVelocityY = -tank2CannonStrength * sinf(tank2CannonAngle);
176 }
177
178 // GO!
179 projectileInMotion = true;
180 }
181
182 // Game state queries
183 bool gameStatusPlayer1Turn()
184 {
185 return player1Turn;
186 }
187
188 bool gameStatusProjectileInMotion()
189 {
190 return projectileInMotion;
191 }
192
193 int gameStatusWinner()
194 {
195 return playerHasWon;
196 }
197
198 bool gameStatusCollisionOccurred()
199 {
200 if(collisionJustOccurred) {
201 collisionJustOccurred = false;
202 return true;
203 }
204 return false;
205 }
206
207 bool gameStatusTankHitOccurred()
208 {
209 if(tankHitJustOccurred) {
210 tankHitJustOccurred = false;
211 return true;
212 }
213 return false;
214 }
215
216
217 float gameStatusProjectileHeight()
218 {
219 return projectilePositionY / (float)screenHeight;
220 }
221
222 // Clean up any allocated memory for the game
223 void cleanupGame()
224 {
225 delete groundLevel;
226 }
227
228 // Drawing routines. Arguments are (interleaved) buffer to render
229 // into, the available size, and the target for how many samples
230 // to use (actual usage might vary slightly). Regardless of
231 // lengthTarget, never use more than bufferSize samples.
232
233 int drawGround(float *buffer, int bufferSize, int framesTarget)
234 {
235 int length;
236
237 // Calculate total length of ground line, to arrive at a speed calculation
238 float totalLineLength = 0.4f*screenWidth
239 + sqrtf(0.36f*screenWidth*screenWidth
240 + (tank2Y-tank1Y)*(tank2Y-tank1Y));
241
242 // Speed is calculated in pixels per frame
243 float speed = totalLineLength / (float)framesTarget;
244
245 // Draw three lines: platforms for tanks and the connecting line.
246 // Eventually, render a more complex ground from the array.
247 length = renderLine(0, tank1Y, screenWidth * 0.2, tank1Y,
248 speed, buffer, bufferSize);
249 length += renderLine(screenWidth * 0.2, tank1Y, screenWidth * 0.8, tank2Y,
250 speed, &buffer[length], bufferSize - length);
251 length += renderLine(screenWidth * 0.8, tank2Y, screenWidth, tank2Y,
252 speed, &buffer[length], bufferSize - length);
253
254 return length;
255 }
256
257 int drawTanks(float *buffer, int bufferSize, int framesTarget)
258 {
259 int length = 0;
260
261 // Calculate total length of tank lines, to arrive at a speed calculation
262 float totalLineLength = 2.0*M_PI*tankRadius + 2.0*(cannonLength - tankRadius);
263
264 // Speed is calculated in pixels per frame
265 float speed = totalLineLength / (float)framesTarget;
266
267 if(playerHasWon != 2) {
268 // Tank 1 body = semicircle + line
269 length += renderArc(tank1X, tank1Y, tankRadius, M_PI, 2.0 * M_PI,
270 speed, buffer, bufferSize);
271 length += renderLine(tank1X + tankRadius, tank1Y,
272 tank1X - tankRadius, tank1Y,
273 speed, &buffer[length], bufferSize - length);
274 // Tank 1 cannon (line depending on angle)
275 length += renderLine(tank1X + tankRadius * cosf(tank1CannonAngle),
276 tank1Y - tankRadius * sinf(tank1CannonAngle),
277 tank1X + cannonLength * cosf(tank1CannonAngle),
278 tank1Y - cannonLength * sinf(tank1CannonAngle),
279 speed, &buffer[length], bufferSize - length);
280 }
281
282 if(playerHasWon != 1) {
283 // Same idea for tank 2
284 length += renderArc(tank2X, tank2Y, tankRadius, M_PI, 2.0 * M_PI,
285 speed, &buffer[length], bufferSize - length);
286 length += renderLine(tank2X + tankRadius, tank2Y,
287 tank2X - tankRadius, tank2Y,
288 speed, &buffer[length], bufferSize - length);
289 length += renderLine(tank2X + tankRadius * cosf(tank2CannonAngle),
290 tank2Y - tankRadius * sinf(tank2CannonAngle),
291 tank2X + cannonLength * cosf(tank2CannonAngle),
292 tank2Y - cannonLength * sinf(tank2CannonAngle),
293 speed, &buffer[length], bufferSize - length);
294 }
295
296 return length;
297 }
298
299 int drawProjectile(float *buffer, int bufferSize, int framesTarget)
300 {
301 if(!projectileInMotion)
302 return 0;
303
304 // Draw a point for a specified number of frames (each containing X and Y)
305 // Return the number of items used in the buffer, which will be twice
306 // the number of frames unless the buffer is full
307
308 if(bufferSize/2 < framesTarget) {
309 renderPoint(projectilePositionX, projectilePositionY, buffer, bufferSize/2);
310 return bufferSize;
311 }
312 else {
313 renderPoint(projectilePositionX, projectilePositionY, buffer, framesTarget);
314 return framesTarget*2;
315 }
316 }
317
318 // Main drawing routine entry point
319 int drawGame(float *buffer, int bufferSize)
320 {
321 int length;
322
323 // Based on buffer size, come up with speeds for each of the elements
324 // 50% of time to ground; 30% to the tanks and 20% to the projectile
325 // Give a margin of 25% beyond so we don't run out of buffer space
326 // if things take longer to draw than we guess they will
327 const float amountToUse = 0.375; // 0.75/2 because two samples per frame
328 const float groundFraction = 0.5 * amountToUse;
329 const float tankFraction = 0.3 * amountToUse;
330 const float projectileFraction = 0.2 * amountToUse;
331
332 length = drawGround(buffer, bufferSize, bufferSize * groundFraction);
333 length += drawTanks(&buffer[length], bufferSize - length,
334 bufferSize * tankFraction);
335 length += drawProjectile(&buffer[length], bufferSize - length,
336 bufferSize * projectileFraction);
337
338 return length;
339 }