Mercurial > hg > beaglert
comparison examples/10-Instruments/tank-wars/game.cpp @ 493:a23d74e2f6cb prerelease
Minor changes to doxygen
author | Robert Jack <robert.h.jack@gmail.com> |
---|---|
date | Tue, 21 Jun 2016 18:50:03 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
492:e9821d65b9ba | 493:a23d74e2f6cb |
---|---|
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 } |