# HG changeset patch
# User andrewm
# Date 1415890688 -3600
# Node ID 49f22e1246b20619b59615da5f64b44dd9cbf1e5
# Parent 09f03ac40fcc11d94e9770f3beb426b2ee6c0514
Tank wars!
diff -r 09f03ac40fcc -r 49f22e1246b2 .cproject
--- a/.cproject Sat Nov 08 16:16:55 2014 +0100
+++ b/.cproject Thu Nov 13 15:58:08 2014 +0100
@@ -82,7 +82,7 @@
-
+
@@ -156,7 +156,7 @@
-
+
diff -r 09f03ac40fcc -r 49f22e1246b2 projects/tank_wars/game.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/tank_wars/game.cpp Thu Nov 13 15:58:08 2014 +0100
@@ -0,0 +1,305 @@
+/*
+ * game.cpp
+ *
+ * Created on: Nov 10, 2014
+ * Author: parallels
+ */
+
+#include
+#include
+#include "vector_graphics.h"
+#include "../../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;
+
+// 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 = randomFloat(screenHeight/2, screenHeight-5);
+ float player2Height = 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;
+ playerHasWon = 2;
+ }
+ else if((tank2X - projectilePositionX)*(tank2X - projectilePositionX) +
+ (tank2Y - projectilePositionY)*(tank2Y - projectilePositionY)
+ <= tankRadius * tankRadius)
+ {
+ projectileInMotion = false;
+ playerHasWon = 1;
+ }
+ else if(projectilePositionX < 0 || projectilePositionX >= screenWidth) {
+ // Check collision whether projectile has exited the screen to the left or right
+ projectileInMotion = false;
+ nextPlayersTurn();
+ }
+ else if(projectilePositionY >= groundLevel[(int)floorf(projectilePositionX)]) {
+ // Check for projectile collision with ground
+ projectileInMotion = false;
+ 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;
+}
+
+// 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;
+}
diff -r 09f03ac40fcc -r 49f22e1246b2 projects/tank_wars/game.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/tank_wars/game.h Thu Nov 13 15:58:08 2014 +0100
@@ -0,0 +1,36 @@
+/*
+ * game.h
+ *
+ * Created on: Nov 10, 2014
+ * Author: parallels
+ */
+
+#ifndef GAME_H_
+#define GAME_H_
+
+// Initialisation
+void setupGame(int width, int height);
+void restartGame();
+
+// Update physics
+void nextGameFrame();
+
+// State updaters
+void setTank1CannonAngle(float angle);
+void setTank2CannonAngle(float angle);
+void setTank1CannonStrength(float strength);
+void setTank2CannonStrength(float strength);
+void fireProjectile();
+
+// State queries
+bool gameStatusPlayer1Turn();
+bool gameStatusProjectileInMotion();
+int gameStatusWinner();
+
+// Render screen; returns length of buffer used
+int drawGame(float *buffer, int bufferSize);
+
+// Cleanup and memory release
+void cleanupGame();
+
+#endif /* GAME_H_ */
diff -r 09f03ac40fcc -r 49f22e1246b2 projects/tank_wars/main.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/tank_wars/main.cpp Thu Nov 13 15:58:08 2014 +0100
@@ -0,0 +1,101 @@
+/*
+ * main.cpp
+ *
+ * Created on: Oct 24, 2014
+ * Author: parallels
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include "../../include/RTAudio.h"
+
+extern int gScreenFramesPerSecond;
+
+using namespace std;
+
+// Handle Ctrl-C by requesting that the audio rendering stop
+void interrupt_handler(int var)
+{
+ gShouldStop = true;
+}
+
+// Print usage information
+void usage(const char * processName)
+{
+ cerr << "Usage: " << processName << " [options]" << endl;
+
+ BeagleRT_usage();
+
+ cerr << " --fps [-f] value: Set target frames per second\n";
+ cerr << " --help [-h]: Print this menu\n";
+}
+
+int main(int argc, char *argv[])
+{
+ RTAudioSettings settings; // Standard audio settings
+
+ struct option customOptions[] =
+ {
+ {"help", 0, NULL, 'h'},
+ {"fps", 1, NULL, 'f'},
+ {NULL, 0, NULL, 0}
+ };
+
+ // Set default settings
+ BeagleRT_defaultSettings(&settings);
+
+ // Parse command-line arguments
+ while (1) {
+ int c;
+ if ((c = BeagleRT_getopt_long(argc, argv, "hf:", customOptions, &settings)) < 0)
+ break;
+ switch (c) {
+ case 'f':
+ gScreenFramesPerSecond = atoi(optarg);
+ if(gScreenFramesPerSecond < 1)
+ gScreenFramesPerSecond = 1;
+ if(gScreenFramesPerSecond > 100)
+ gScreenFramesPerSecond = 100;
+ break;
+ case 'h':
+ usage(basename(argv[0]));
+ exit(0);
+ case '?':
+ default:
+ usage(basename(argv[0]));
+ exit(1);
+ }
+ }
+
+ // Initialise the PRU audio device
+ if(BeagleRT_initAudio(&settings, 0) != 0) {
+ cout << "Error: unable to initialise audio" << endl;
+ return -1;
+ }
+
+ // Start the audio device running
+ if(BeagleRT_startAudio()) {
+ cout << "Error: unable to start real-time audio" << endl;
+ return -1;
+ }
+
+ // Set up interrupt handler to catch Control-C
+ signal(SIGINT, interrupt_handler);
+
+ // Run until told to stop
+ while(!gShouldStop) {
+ usleep(100000);
+ }
+
+ // Stop the audio device
+ BeagleRT_stopAudio();
+
+ // Clean up any resources allocated for audio
+ BeagleRT_cleanupAudio();
+
+ // All done!
+ return 0;
+}
diff -r 09f03ac40fcc -r 49f22e1246b2 projects/tank_wars/render.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/tank_wars/render.cpp Thu Nov 13 15:58:08 2014 +0100
@@ -0,0 +1,275 @@
+/*
+ * render.cpp
+ *
+ * Created on: Oct 24, 2014
+ * Author: parallels
+ */
+
+
+#include "../../include/RTAudio.h"
+#include "../../include/Utilities.h"
+#include "game.h"
+#include
+#include
+#include
+#include
+
+float gFrequency;
+float gPhase;
+float gInverseSampleRate;
+int gNumChannels;
+
+int gInputTank1Angle = 0; // Inputs for the cannon angles
+int gInputTank2Angle = 1;
+int gInputLauncher = 2; // Input for launcher FSR
+
+int gOutputX = 0; // Outputs for the scope
+int gOutputY = 1;
+int gOutputPlayer1LED = 2;
+int gOutputPlayer2LED = 3;
+
+int gGameFramesPerSecond = 60; // How often the physics are updated
+int gGameFrameInterval; // ...and in frames
+int gSamplesUntilNextFrame; // Counter until next update
+int gSamplesSinceFinish = 0; // How long since somebody won?
+bool gGameShouldRestart = false;// Whether we need to reinitiliase the game
+
+// Counter for overall number of samples that have elapsed
+unsigned int gSampleCounter = 0;
+
+// 1st-order filter and peak detector for launcher input
+float gLauncherLastSample = 0;
+float gLauncherFilterPole = 0.8;
+float gLauncherPeakValue = 0;
+float gLauncherPeakFilterPole = 0.999;
+float gLauncherNoiseThreshold = 0.01 * MATRIX_MAX;
+float gLauncherMinimumPeak = 0.1 * MATRIX_MAX;
+bool gLauncherTriggered = false;
+
+// Screen update rate; affects buffer size. Actual contents of buffer
+// may be smaller than this
+int gScreenWidth = 512;
+int gScreenHeight = 512;
+int gScreenFramesPerSecond = 25;
+
+// Double buffer for rendering screen. Each is an interleaved buffer
+// of XY data.
+float *gScreenBuffer1, *gScreenBuffer2;
+float *gScreenBufferWrite, *gScreenBufferRead;
+int gScreenBufferMaxLength; // What is the total buffer allocated?
+int gScreenBufferReadLength; // How long is the read buffer?
+int gScreenBufferWriteLength; // How long is the write (next) buffer?
+int gScreenBufferReadPointer; // Where are we in the read buffer now?
+int gScreenBufferNextUpdateLocation; // When should we render the next buffer?
+bool gScreenNextBufferReady; // Is the next buffer ready to go?
+
+// Auxiliary (low-priority) task for updating the screen
+AuxiliaryTask gScreenUpdateTask;
+
+void screen_update();
+
+// initialise_render() is called once before the audio rendering starts.
+// Use it to perform any initialisation and allocation which is dependent
+// on the period size or sample rate.
+//
+// userData holds an opaque pointer to a data structure that was passed
+// in from the call to initAudio().
+//
+// Return true on success; returning false halts the program.
+
+bool initialise_render(int numChannels, int numMatrixFramesPerPeriod,
+ int numAudioFramesPerPeriod, float matrixSampleRate,
+ float audioSampleRate, void *userData)
+{
+ srandom(time(NULL));
+
+ // Verify we are running with matrix enabled
+ if(numMatrixFramesPerPeriod*2 != numAudioFramesPerPeriod) {
+ rt_printf("Error: this example needs the matrix enabled, running at half audio rate\n");
+ return false;
+ }
+
+ // Initialise the screen buffers
+ gScreenBufferMaxLength = 2 * matrixSampleRate / gScreenFramesPerSecond;
+ gScreenBuffer1 = new float[gScreenBufferMaxLength];
+ gScreenBuffer2 = new float[gScreenBufferMaxLength];
+ if(gScreenBuffer1 == 0 || gScreenBuffer2 == 0) {
+ rt_printf("Error initialising screen buffers\n");
+ return false;
+ }
+
+ gScreenBufferRead = gScreenBuffer1;
+ gScreenBufferWrite = gScreenBuffer2;
+ gScreenBufferReadLength = gScreenBufferWriteLength = 0;
+ gScreenBufferReadPointer = 0;
+ gScreenBufferNextUpdateLocation = 0;
+ gScreenNextBufferReady = false;
+
+ // Initialise the game
+ setupGame(gScreenWidth, gScreenHeight);
+ gGameFrameInterval = matrixSampleRate / gGameFramesPerSecond;
+ gSamplesUntilNextFrame = gGameFrameInterval;
+
+ // Initialise auxiliary tasks
+ if((gScreenUpdateTask = createAuxiliaryTaskLoop(&screen_update, 90,
+ "beaglert-screen-update")) == 0)
+ return false;
+
+ return true;
+}
+
+// Swap buffers on the screen
+void swap_buffers()
+{
+ if(gScreenBufferRead == gScreenBuffer1) {
+ gScreenBufferRead = gScreenBuffer2;
+ gScreenBufferWrite = gScreenBuffer1;
+ }
+ else {
+ gScreenBufferRead = gScreenBuffer1;
+ gScreenBufferWrite = gScreenBuffer2;
+ }
+
+ gScreenBufferReadLength = gScreenBufferWriteLength;
+ gScreenBufferReadPointer = 0;
+
+ // Schedule next update for 3/4 of the way through the buffer
+ gScreenBufferNextUpdateLocation = gScreenBufferReadLength * 0.75;
+ gScreenNextBufferReady = false;
+}
+
+// render() is called regularly at the highest priority by the audio engine.
+// Input and output are given from the audio hardware and the other
+// ADCs and DACs (if available). If only audio is available, numMatrixFrames
+// will be 0.
+
+void render(int numMatrixFrames, int numAudioFrames, float *audioIn, float *audioOut,
+ uint16_t *matrixIn, uint16_t *matrixOut)
+{
+ for(int n = 0; n < numMatrixFrames; n++) {
+ // First-order lowpass filter to remove noise on launch FSR
+ float rawSample = analogRead(gInputLauncher, n);
+ float launchSample = gLauncherFilterPole * gLauncherLastSample +
+ (1.0f - gLauncherFilterPole) * rawSample;
+ gLauncherLastSample = launchSample;
+
+ // Peak-detect on launch signal
+ if(launchSample >= gLauncherPeakValue) {
+ gLauncherPeakValue = launchSample;
+ gLauncherTriggered = false;
+ }
+ else {
+ if(gLauncherPeakValue - launchSample > gLauncherNoiseThreshold && !gLauncherTriggered) {
+ // Detected a peak; is it big enough overall?
+ if(gLauncherPeakValue >= gLauncherMinimumPeak) {
+ gLauncherTriggered = true;
+ // Peak detected-- fire!!
+ // Set both cannon strengths but only one will
+ // fire depending on whose turn it is
+ float strength = map(gLauncherPeakValue,
+ gLauncherMinimumPeak, MATRIX_MAX,
+ 0.5f, 10.0f);
+ setTank1CannonStrength(strength);
+ setTank2CannonStrength(strength);
+ fireProjectile();
+ }
+ }
+
+ gLauncherPeakValue *= gLauncherPeakFilterPole;
+ }
+
+ if(--gSamplesUntilNextFrame <= 0) {
+ // Update game physics and cannon angles
+ gSamplesUntilNextFrame = gGameFrameInterval;
+
+ setTank1CannonAngle(map(analogRead(gInputTank1Angle, n),
+ 0, MATRIX_MAX, M_PI, 0));
+ setTank2CannonAngle(map(analogRead(gInputTank2Angle, n),
+ 0, MATRIX_MAX, M_PI, 0));
+ nextGameFrame();
+ }
+
+ if(gScreenBufferReadPointer >= gScreenBufferReadLength - 1
+ && gScreenNextBufferReady) {
+ // Got to the end; swap buffers
+ swap_buffers();
+ }
+
+ // Push current screen buffer to the matrix output
+ if(gScreenBufferReadPointer < gScreenBufferReadLength - 1) {
+ float x = gScreenBufferRead[gScreenBufferReadPointer++];
+ float y = gScreenBufferRead[gScreenBufferReadPointer++];
+
+ // Rescale screen coordinates to matrix ranges; invert the Y
+ // coordinate to go from normal screen coordinates to scope coordinates
+ analogWrite(gOutputX, n, constrain(map(x, 0, gScreenWidth, 0, MATRIX_MAX), 0, MATRIX_MAX));
+ analogWrite(gOutputY, n, constrain(map(y, 0, gScreenHeight, MATRIX_MAX, 0), 0, MATRIX_MAX));
+ }
+ else {
+ // Still not ready! Write 0 until something happens
+ analogWrite(gOutputX, n, 0);
+ analogWrite(gOutputY, n, 0);
+ }
+
+ if(gameStatusWinner() != 0) {
+ // Blink one LED to show who won
+ // Blink both LEDs when projectile is in motion
+ uint16_t val = (gSampleCounter % 4000 > 2000) ? MATRIX_MAX : 0;
+ analogWrite(gOutputPlayer1LED, n, gameStatusWinner() == 1 ? val : 0);
+ analogWrite(gOutputPlayer2LED, n, gameStatusWinner() == 2 ? val : 0);
+
+ // After 5 seconds, restart the game
+ gSamplesSinceFinish++;
+ if(gSamplesSinceFinish > 22050*5)
+ gGameShouldRestart = true;
+ }
+ else if(gameStatusProjectileInMotion()) {
+ // Blink both LEDs when projectile is in motion
+ uint16_t val = (gSampleCounter % 2000 > 1000) ? MATRIX_MAX : 0;
+ analogWrite(gOutputPlayer1LED, n, val);
+ analogWrite(gOutputPlayer2LED, n, val);
+ }
+ else if(gameStatusPlayer1Turn()) {
+ analogWrite(gOutputPlayer1LED, n, MATRIX_MAX);
+ analogWrite(gOutputPlayer2LED, n, 0);
+ }
+ else {
+ analogWrite(gOutputPlayer2LED, n, MATRIX_MAX);
+ analogWrite(gOutputPlayer1LED, n, 0);
+ }
+
+ // Check if we have reached the point where we should next update
+ if(gScreenBufferReadPointer >= gScreenBufferNextUpdateLocation &&
+ !gScreenNextBufferReady) {
+ // Update the screen at lower priority than the audio thread
+ scheduleAuxiliaryTask(gScreenUpdateTask);
+ }
+
+ gSampleCounter++;
+ }
+}
+
+void screen_update()
+{
+ // If we should restart, reinitialise the game
+ if(gGameShouldRestart) {
+ restartGame();
+ gGameShouldRestart = false;
+ gSamplesSinceFinish = 0;
+ }
+
+ // Render the game based on the current state
+ gScreenBufferWriteLength = drawGame(gScreenBufferWrite, gScreenBufferMaxLength);
+
+ // Flag it as ready to go
+ gScreenNextBufferReady = true;
+}
+
+// cleanup_render() is called once at the end, after the audio has stopped.
+// Release any resources that were allocated in initialise_render().
+
+void cleanup_render()
+{
+ // Clean up the game state
+ cleanupGame();
+}
diff -r 09f03ac40fcc -r 49f22e1246b2 projects/tank_wars/vector_graphics.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/tank_wars/vector_graphics.cpp Thu Nov 13 15:58:08 2014 +0100
@@ -0,0 +1,72 @@
+/*
+ * vector_graphics.cpp
+ *
+ * Created on: Nov 10, 2014
+ * Author: parallels
+ */
+
+#include
+
+// Draw a line between two points at a specified rate in
+// pixels per buffer sample. Indicate maximum available space.
+// Returns space used
+int renderLine(float x1, float y1, float x2, float y2, float speed,
+ float *buffer, int maxLength) {
+ // Figure out length of line and therefore how many samples
+ // are needed to represent it based on the speed (rounded to nearest int)
+ float totalLineLength = sqrtf((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));
+ int samplesNeeded = floorf(totalLineLength / speed + 0.5);
+
+ // Now render into the buffer
+ int length = 0;
+ float scaleFactor = 1.0f / samplesNeeded;
+ for(int n = 0; n < samplesNeeded; n++) {
+ if(length >= maxLength - 1)
+ return length;
+ // X coordinate
+ *buffer++ = x1 + (float)n * scaleFactor * (x2 - x1);
+ // Y coordinate
+ *buffer++ = y1 + (float)n * scaleFactor * (y2 - y1);
+ length += 2;
+ }
+
+ return length;
+}
+
+// Draw an arc around a centre point at a specified rate of pixels
+// per buffer sample. Indicate maximum available space.
+// Returns space used
+int renderArc(float x, float y, float radius, float thetaMin, float thetaMax,
+ float speed, float *buffer, int maxLength) {
+ // Figure out circumference of arc and therefore how many samples
+ // are needed to represent it based on the speed (rounded to nearest int)
+ float circumference = (thetaMax - thetaMin) * radius;
+ int samplesNeeded = floorf(circumference / speed + 0.5);
+
+ // Now render into the buffer
+ int length = 0;
+ float scaleFactor = 1.0f / samplesNeeded;
+ for(int n = 0; n < samplesNeeded; n++) {
+ if(length >= maxLength - 1)
+ return length;
+ // Get current angle
+ float theta = thetaMin + (float)n * scaleFactor * (thetaMax - thetaMin);
+
+ // Convert polar to cartesian coordinates
+ *buffer++ = x + radius * cosf(theta);
+ *buffer++ = y + radius * sinf(theta);
+
+ length += 2;
+ }
+
+ return length;
+}
+
+// Draw a single point for a specified number of frames
+void renderPoint(float x, float y, float *buffer, float length) {
+ while(length > 0) {
+ *buffer++ = x;
+ *buffer++ = y;
+ length--;
+ }
+}
diff -r 09f03ac40fcc -r 49f22e1246b2 projects/tank_wars/vector_graphics.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/tank_wars/vector_graphics.h Thu Nov 13 15:58:08 2014 +0100
@@ -0,0 +1,18 @@
+/*
+ * vector_graphics.h
+ *
+ * Created on: Nov 10, 2014
+ * Author: parallels
+ */
+
+#ifndef VECTOR_GRAPHICS_H_
+#define VECTOR_GRAPHICS_H_
+
+int renderLine(float x1, float y1, float x2, float y2, float speed,
+ float *buffer, int maxLength);
+int renderArc(float x, float y, float radius, float thetaMin, float thetaMax,
+ float speed, float *buffer, int maxLength);
+void renderPoint(float x, float y, float *buffer, float length);
+
+
+#endif /* VECTOR_GRAPHICS_H_ */