Dodging asteroids game.
Dependencies: 4DGL-uLCD-SE PinDetect SDFileSystem mbed wave_player
main.cpp
- Committer:
- dylanslack
- Date:
- 2016-03-14
- Revision:
- 0:3f73e98442ec
File content as of revision 0:3f73e98442ec:
#include "LSM9DS1.h"
#include "uLCD_4DGL.h"
#include "wave_player.h"
#include "SDFileSystem.h"
#include <math.h>
#include <vector>
#include <time.h>
uLCD_4DGL uLCD(p28, p27, p30);
SDFileSystem sd(p5, p6, p7, p8, "sd");
Serial pc(USBTX, USBRX);
LSM9DS1 IMU(p9, p10, 0xD6, 0x3C);
AnalogOut DACout(p18);
wave_player waver(&DACout);
FILE *explosion = fopen("/sd/explosion.wav", "r");
int score = 0;
/**
* The SpaceCraft class has a single position variable and
* the methods required to move, draw, and clear a SpaceCraft
* instance.
*/
class SpaceCraft {
public:
SpaceCraft();
void move(float);
void draw();
void undraw();
int position;
};
/**
* Asteroids are similar to SpaceCrafts but are generated with
* a random size, position, and velocity after being invoked.
* The `isAlive` variable becomes false when the asteroid should
* be reset, where it is given another random position, size, and
* velocity.
*/
class Asteroid {
public:
Asteroid();
void move(int);
void reset();
void fall();
void draw();
void undraw();
bool isAlive;
int x, y, size, velocity;
};
SpaceCraft::SpaceCraft() {
position = 64;
}
Asteroid::Asteroid() {
x = (rand() % 100) + 10;
y = 0;
size = (int)((rand() % 4) + 6);
velocity = (int)((rand() % 3) + 1);
isAlive = true;
}
// moves the space craft by clearing it, advancing it, then redrawing it
void SpaceCraft::move(float x) {
undraw();
position = (int)(64.0 - (x * 64.0));
draw();
}
// moves the asteroid by clearing it, advancing it, then redrawing it
void Asteroid::move(int posY) {
if (y == posY) return;
undraw();
y = posY;
if (y >= 127) {
isAlive = false;
score++;
} else {
draw();
}
}
// moves the asteroid downward according to its velocity
void Asteroid::fall() {
move(y + velocity);
}
// resets the astroid by putting it back at the top with a random x coordinate
void Asteroid::reset() {
y = 0;
x = (rand() % 100) + 10;
velocity = (int)((rand() % 3) + 1);
isAlive = true;
}
// draws the asteroid
void Asteroid::draw() {
int largeCraterRadius = (int)(floor((float)size / 3.0));
int largeCraterX = x - largeCraterRadius;
int largeCraterY = y - largeCraterRadius;
int smallCraterRadius = (int)(floor((float)size / 4.0));
int smallCraterX = x + largeCraterRadius;
int smallCraterY = y + largeCraterRadius;
uLCD.filled_circle(x, y, size, 0x909090);
uLCD.circle(largeCraterX, largeCraterY, largeCraterRadius, 0x404040);
uLCD.circle(smallCraterX, smallCraterY, smallCraterRadius, 0x404040);
}
// draws the space craft
void SpaceCraft::draw() {
int width = 12;
int left = position - (width / 2);
int right = position + (width / 2);
int middle = left + (width / 2);
// draw the space craft (blue triangle)
uLCD.triangle(left, 120, middle, 106, right, 120, BLUE);
// draw the flame (red triangle)
uLCD.triangle(left + 4, 121, middle, 125, right - 4, 121, RED);
}
// clears the asteroid
void Asteroid::undraw() {
int largeCraterRadius = (int)(floor((float)size / 3.0));
int largeCraterX = x - largeCraterRadius;
int largeCraterY = y - largeCraterRadius;
int smallCraterRadius = (int)(floor((float)size / 4.0));
int smallCraterX = x + largeCraterRadius;
int smallCraterY = y + largeCraterRadius;
uLCD.filled_circle(x, y, size, BLACK);
uLCD.circle(largeCraterX, largeCraterY, largeCraterRadius, BLACK);
uLCD.circle(smallCraterX, smallCraterY, smallCraterRadius, BLACK);
}
// clears the space craft
void SpaceCraft::undraw() {
int width = 12;
int left = position - (width / 2);
int right = position + (width / 2);
int middle = left + (width / 2);
// clear the space craft (blue triangle)
uLCD.triangle(left, 120, middle, 106, right, 120, BLACK);
// clear the flame (red triangle)
uLCD.triangle(left + 4, 121, middle, 125, right - 4, 121, BLACK);
}
/**
* Returns true if there has been a collision between an asteroid and a ship.
* This a very basic collision: it determines if any of the ship's vertices
* interset with the asteroid's bounding circle. This fails for instances in
* which the asteroid intesects with the edge of a ship but not one of the
* vertices.
*
* This calculates vertex collision by comparing the circle's radius and the distance
* between a vertex and the center of an asteroid.
*/
bool checkCollision(SpaceCraft ship, Asteroid asteroid) {
// asteroid coordinates
int asteroidCenterX = asteroid.x;
int asteroidCenterY = asteroid.y;
int asteroidRadius = asteroid.size;
// ship coordinates
int shipLeftX = ship.position - 6;
int shipLeftY = 120;
int shipRightX = ship.position + 6;
int shipRightY = 120;
int shipMiddleX = ship.position;
int shipMiddleY = 106;
// left vertex detection
if (sqrt(pow(asteroidCenterX - shipLeftX, 2.0) + pow(asteroidCenterY - shipLeftY, 2.0)) <= asteroidRadius)
return true;
// right vertex detection
if (sqrt(pow(asteroidCenterX - shipRightX, 2.0) + pow(asteroidCenterY - shipRightY, 2.0)) <= asteroidRadius)
return true;
// middle vertex detection
if (sqrt(pow(asteroidCenterX - shipMiddleX, 2.0) + pow(asteroidCenterY - shipMiddleY, 2.0)) <= asteroidRadius)
return true;
return false;
}
SpaceCraft ship;
vector<Asteroid> asteroids;
int main() {
float tilt;
// don't forget to seed the rand function
srand(time(NULL));
uLCD.reset();
uLCD.cls();
uLCD.background_color(BLACK);
uLCD.printf("\nDodge asteroids!\n\r");
uLCD.printf("Loading...");
wait(1.0);
uLCD.cls();
wait(1.0);
// jack up the baudrate
uLCD.baudrate(3000000);
// initialize some asteroids
Asteroid a, b, c, d;
asteroids.push_back(a);
asteroids.push_back(b);
asteroids.push_back(c);
asteroids.push_back(d);
IMU.begin();
if (!IMU.begin()) {
pc.printf("Failed to communicate with IMU.\n");
return 0;
}
IMU.calibrate(1);
IMU.calibrateMag(0);
if (explosion == NULL) {
pc.printf("explosion.wav was not opened!\n");
return 0;
}
while(1) {
while(!IMU.accelAvailable());
IMU.readAccel();
/**
* `tilt` is the 'y' acceleration value, ranging between [-1.0, 1.0].
*
* This variable will control the spacecraft's horizontal position.
*/
tilt = IMU.calcAccel(IMU.ay);
for(size_t i = 0; i < asteroids.size(); i++) {
asteroids[i].fall();
// reset the asteroid if it goes below the screen
if (!asteroids[i].isAlive) {
asteroids[i].reset();
}
// play the explosion sound and end the game if there is a collision
if (checkCollision(ship, asteroids[i])) {
uLCD.background_color(RED);
uLCD.textbackground_color(RED);
waver.play(explosion);
fseek(explosion, 0, SEEK_SET);
uLCD.cls();
uLCD.color(WHITE);
uLCD.text_width(3);
uLCD.text_height(3);
uLCD.printf("\nGame\nOVER\n\n");
uLCD.text_width(1);
uLCD.text_height(1);
uLCD.printf("Score: %d", score);
return 0;
}
}
// update the score
uLCD.locate(0, 0);
uLCD.printf("Score: %d", score);
// move the ship based on the IMU's accelerometer reading
ship.move(tilt);
wait(0.01);
}
}