annotate projects/tank_wars/game.cpp @ 12:a6beeba3a648

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