annotate projects/tank_wars/game.cpp @ 47:643cbee74eda newapi

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