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