annotate projects/tank_wars/game.cpp @ 68:59edd5780fef

Changed d-box code to run cleanly when built on board. Updated Makefile to add ne10 include path on board. Some extra docs in Utilities.h
author andrewm
date Fri, 17 Jul 2015 16:57:08 +0100
parents fbfeb5895efd
children 8d80eda512cd
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@68 11 #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 }