annotate projects/tank_wars/game.cpp @ 268:8d80eda512cd prerelease

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