Mercurial > hg > beaglert
view examples/10-Instruments/tank-wars/game.cpp @ 493:a23d74e2f6cb prerelease
Minor changes to doxygen
author | Robert Jack <robert.h.jack@gmail.com> |
---|---|
date | Tue, 21 Jun 2016 18:50:03 +0100 |
parents | |
children |
line wrap: on
line source
/* * game.cpp * * Created on: Nov 10, 2014 * Author: parallels */ #include <cmath> #include <cstdlib> #include "vector_graphics.h" #include <Utilities.h> // Virtual screen size int screenWidth, screenHeight; // Basic information on the terrain and the tanks float *groundLevel; // Y coordinate of the ground for each X coordinate float tank1X, tank1Y, tank2X, tank2Y; // Positions of the two tanks float tankRadius = 20; // Radius of the tanks float cannonLength = 40; // How long the cannon on each tank extends float gravity = 0.05; // Strength of gravity // Current state of the game int playerHasWon = 0; // 1 if player 1 wins, 2 if player 2 wins, 0 if game in progress bool player1Turn = true; // true if it's player 1's turn; false otherwise float tank1CannonAngle = M_PI/2; float tank2CannonAngle = M_PI/2; // Direction the tank cannons are pointing float tank1CannonStrength = 3; float tank2CannonStrength = 3; // Strength of intended projectile launch // Location of the projectile bool projectileInMotion = false; float projectilePositionX, projectilePositionY; float projectileVelocityX, projectileVelocityY; // Infor needed for sound rendering bool collisionJustOccurred = false; bool tankHitJustOccurred = false; // Useful utility function for generating random floating-point values float randomFloat(float low, float hi) { float r = (float)random() / (float)RAND_MAX; return map(r, 0, 1, low, hi); } // Restart the game, without reallocating memory void restartGame() { float player1Height = screenHeight * 3/4; // randomFloat(screenHeight/2, screenHeight-5); float player2Height = screenHeight - 5; // randomFloat(screenHeight/2, screenHeight-5); for(int i = 0; i < screenWidth * 0.2; i++) { groundLevel[i] = player1Height; } for(int i = screenWidth * 0.2; i < screenWidth * 0.8; i++) { groundLevel[i] = player1Height + (player2Height - player1Height) * (i - screenWidth*0.2)/(screenWidth*0.6); } for(int i = screenWidth * 0.8; i < screenWidth; i++) { groundLevel[i] = player2Height; } // Set the location of the two tanks so they rest on the ground at opposite sides tank1X = screenWidth * 0.1; tank1Y = player1Height; tank2X = screenWidth * 0.9; tank2Y = player2Height; playerHasWon = 0; projectileInMotion = false; } // Initialise the game void setupGame(int width, int height) { // Set the screen size screenWidth = width; screenHeight = height; // Initialize the ground level groundLevel = new float[screenWidth]; restartGame(); } // Advance the turn to the next player void nextPlayersTurn() { player1Turn = !player1Turn; } // Move forward one frame on the game physics void nextGameFrame() { if(!projectileInMotion) return; // Update position of projectile projectilePositionX += projectileVelocityX; projectilePositionY += projectileVelocityY; projectileVelocityY += gravity; // Check collision with tanks first: a collision with tank 1 means player 2 wins and vice-versa if((tank1X - projectilePositionX)*(tank1X - projectilePositionX) + (tank1Y - projectilePositionY)*(tank1Y - projectilePositionY) <= tankRadius * tankRadius) { projectileInMotion = false; collisionJustOccurred = false; tankHitJustOccurred = true; playerHasWon = 2; } else if((tank2X - projectilePositionX)*(tank2X - projectilePositionX) + (tank2Y - projectilePositionY)*(tank2Y - projectilePositionY) <= tankRadius * tankRadius) { projectileInMotion = false; collisionJustOccurred = false; tankHitJustOccurred = true; playerHasWon = 1; } else if(projectilePositionX < 0 || projectilePositionX >= screenWidth) { // Check collision whether projectile has exited the screen to the left or right projectileInMotion = false; collisionJustOccurred = true; nextPlayersTurn(); } else if(projectilePositionY >= groundLevel[(int)floorf(projectilePositionX)]) { // Check for projectile collision with ground projectileInMotion = false; collisionJustOccurred = true; nextPlayersTurn(); } } // Updates for game state void setTank1CannonAngle(float angle) { tank1CannonAngle = angle; } void setTank2CannonAngle(float angle) { tank2CannonAngle = angle; } void setTank1CannonStrength(float strength) { tank1CannonStrength = strength; } void setTank2CannonStrength(float strength) { tank2CannonStrength = strength; } // FIRE! void fireProjectile() { // Can't fire while projectile is already moving, or if someone has won if(projectileInMotion) return; if(playerHasWon != 0) return; if(player1Turn) { projectilePositionX = tank1X + cannonLength * cosf(tank1CannonAngle); projectilePositionY = tank1Y - cannonLength * sinf(tank1CannonAngle); projectileVelocityX = tank1CannonStrength * cosf(tank1CannonAngle); projectileVelocityY = -tank1CannonStrength * sinf(tank1CannonAngle); } else { projectilePositionX = tank2X + cannonLength * cosf(tank2CannonAngle); projectilePositionY = tank2Y - cannonLength * sinf(tank2CannonAngle); projectileVelocityX = tank2CannonStrength * cosf(tank2CannonAngle); projectileVelocityY = -tank2CannonStrength * sinf(tank2CannonAngle); } // GO! projectileInMotion = true; } // Game state queries bool gameStatusPlayer1Turn() { return player1Turn; } bool gameStatusProjectileInMotion() { return projectileInMotion; } int gameStatusWinner() { return playerHasWon; } bool gameStatusCollisionOccurred() { if(collisionJustOccurred) { collisionJustOccurred = false; return true; } return false; } bool gameStatusTankHitOccurred() { if(tankHitJustOccurred) { tankHitJustOccurred = false; return true; } return false; } float gameStatusProjectileHeight() { return projectilePositionY / (float)screenHeight; } // Clean up any allocated memory for the game void cleanupGame() { delete groundLevel; } // Drawing routines. Arguments are (interleaved) buffer to render // into, the available size, and the target for how many samples // to use (actual usage might vary slightly). Regardless of // lengthTarget, never use more than bufferSize samples. int drawGround(float *buffer, int bufferSize, int framesTarget) { int length; // Calculate total length of ground line, to arrive at a speed calculation float totalLineLength = 0.4f*screenWidth + sqrtf(0.36f*screenWidth*screenWidth + (tank2Y-tank1Y)*(tank2Y-tank1Y)); // Speed is calculated in pixels per frame float speed = totalLineLength / (float)framesTarget; // Draw three lines: platforms for tanks and the connecting line. // Eventually, render a more complex ground from the array. length = renderLine(0, tank1Y, screenWidth * 0.2, tank1Y, speed, buffer, bufferSize); length += renderLine(screenWidth * 0.2, tank1Y, screenWidth * 0.8, tank2Y, speed, &buffer[length], bufferSize - length); length += renderLine(screenWidth * 0.8, tank2Y, screenWidth, tank2Y, speed, &buffer[length], bufferSize - length); return length; } int drawTanks(float *buffer, int bufferSize, int framesTarget) { int length = 0; // Calculate total length of tank lines, to arrive at a speed calculation float totalLineLength = 2.0*M_PI*tankRadius + 2.0*(cannonLength - tankRadius); // Speed is calculated in pixels per frame float speed = totalLineLength / (float)framesTarget; if(playerHasWon != 2) { // Tank 1 body = semicircle + line length += renderArc(tank1X, tank1Y, tankRadius, M_PI, 2.0 * M_PI, speed, buffer, bufferSize); length += renderLine(tank1X + tankRadius, tank1Y, tank1X - tankRadius, tank1Y, speed, &buffer[length], bufferSize - length); // Tank 1 cannon (line depending on angle) length += renderLine(tank1X + tankRadius * cosf(tank1CannonAngle), tank1Y - tankRadius * sinf(tank1CannonAngle), tank1X + cannonLength * cosf(tank1CannonAngle), tank1Y - cannonLength * sinf(tank1CannonAngle), speed, &buffer[length], bufferSize - length); } if(playerHasWon != 1) { // Same idea for tank 2 length += renderArc(tank2X, tank2Y, tankRadius, M_PI, 2.0 * M_PI, speed, &buffer[length], bufferSize - length); length += renderLine(tank2X + tankRadius, tank2Y, tank2X - tankRadius, tank2Y, speed, &buffer[length], bufferSize - length); length += renderLine(tank2X + tankRadius * cosf(tank2CannonAngle), tank2Y - tankRadius * sinf(tank2CannonAngle), tank2X + cannonLength * cosf(tank2CannonAngle), tank2Y - cannonLength * sinf(tank2CannonAngle), speed, &buffer[length], bufferSize - length); } return length; } int drawProjectile(float *buffer, int bufferSize, int framesTarget) { if(!projectileInMotion) return 0; // Draw a point for a specified number of frames (each containing X and Y) // Return the number of items used in the buffer, which will be twice // the number of frames unless the buffer is full if(bufferSize/2 < framesTarget) { renderPoint(projectilePositionX, projectilePositionY, buffer, bufferSize/2); return bufferSize; } else { renderPoint(projectilePositionX, projectilePositionY, buffer, framesTarget); return framesTarget*2; } } // Main drawing routine entry point int drawGame(float *buffer, int bufferSize) { int length; // Based on buffer size, come up with speeds for each of the elements // 50% of time to ground; 30% to the tanks and 20% to the projectile // Give a margin of 25% beyond so we don't run out of buffer space // if things take longer to draw than we guess they will const float amountToUse = 0.375; // 0.75/2 because two samples per frame const float groundFraction = 0.5 * amountToUse; const float tankFraction = 0.3 * amountToUse; const float projectileFraction = 0.2 * amountToUse; length = drawGround(buffer, bufferSize, bufferSize * groundFraction); length += drawTanks(&buffer[length], bufferSize - length, bufferSize * tankFraction); length += drawProjectile(&buffer[length], bufferSize - length, bufferSize * projectileFraction); return length; }