annotate examples/10-Instruments/tank-wars/game.cpp @ 543:8f8809c77dda prerelease

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