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