Dodging asteroids game.

Dependencies:   4DGL-uLCD-SE PinDetect SDFileSystem mbed wave_player

Committer:
dylanslack
Date:
Mon Mar 14 03:08:37 2016 +0000
Revision:
0:3f73e98442ec
Initial commit

Who changed what in which revision?

UserRevisionLine numberNew contents of line
dylanslack 0:3f73e98442ec 1 #include "LSM9DS1.h"
dylanslack 0:3f73e98442ec 2 #include "uLCD_4DGL.h"
dylanslack 0:3f73e98442ec 3 #include "wave_player.h"
dylanslack 0:3f73e98442ec 4 #include "SDFileSystem.h"
dylanslack 0:3f73e98442ec 5 #include <math.h>
dylanslack 0:3f73e98442ec 6 #include <vector>
dylanslack 0:3f73e98442ec 7 #include <time.h>
dylanslack 0:3f73e98442ec 8
dylanslack 0:3f73e98442ec 9 uLCD_4DGL uLCD(p28, p27, p30);
dylanslack 0:3f73e98442ec 10 SDFileSystem sd(p5, p6, p7, p8, "sd");
dylanslack 0:3f73e98442ec 11 Serial pc(USBTX, USBRX);
dylanslack 0:3f73e98442ec 12 LSM9DS1 IMU(p9, p10, 0xD6, 0x3C);
dylanslack 0:3f73e98442ec 13 AnalogOut DACout(p18);
dylanslack 0:3f73e98442ec 14 wave_player waver(&DACout);
dylanslack 0:3f73e98442ec 15
dylanslack 0:3f73e98442ec 16 FILE *explosion = fopen("/sd/explosion.wav", "r");
dylanslack 0:3f73e98442ec 17
dylanslack 0:3f73e98442ec 18 int score = 0;
dylanslack 0:3f73e98442ec 19
dylanslack 0:3f73e98442ec 20 /**
dylanslack 0:3f73e98442ec 21 * The SpaceCraft class has a single position variable and
dylanslack 0:3f73e98442ec 22 * the methods required to move, draw, and clear a SpaceCraft
dylanslack 0:3f73e98442ec 23 * instance.
dylanslack 0:3f73e98442ec 24 */
dylanslack 0:3f73e98442ec 25 class SpaceCraft {
dylanslack 0:3f73e98442ec 26 public:
dylanslack 0:3f73e98442ec 27 SpaceCraft();
dylanslack 0:3f73e98442ec 28 void move(float);
dylanslack 0:3f73e98442ec 29 void draw();
dylanslack 0:3f73e98442ec 30 void undraw();
dylanslack 0:3f73e98442ec 31 int position;
dylanslack 0:3f73e98442ec 32 };
dylanslack 0:3f73e98442ec 33
dylanslack 0:3f73e98442ec 34 /**
dylanslack 0:3f73e98442ec 35 * Asteroids are similar to SpaceCrafts but are generated with
dylanslack 0:3f73e98442ec 36 * a random size, position, and velocity after being invoked.
dylanslack 0:3f73e98442ec 37 * The `isAlive` variable becomes false when the asteroid should
dylanslack 0:3f73e98442ec 38 * be reset, where it is given another random position, size, and
dylanslack 0:3f73e98442ec 39 * velocity.
dylanslack 0:3f73e98442ec 40 */
dylanslack 0:3f73e98442ec 41 class Asteroid {
dylanslack 0:3f73e98442ec 42 public:
dylanslack 0:3f73e98442ec 43 Asteroid();
dylanslack 0:3f73e98442ec 44 void move(int);
dylanslack 0:3f73e98442ec 45 void reset();
dylanslack 0:3f73e98442ec 46 void fall();
dylanslack 0:3f73e98442ec 47 void draw();
dylanslack 0:3f73e98442ec 48 void undraw();
dylanslack 0:3f73e98442ec 49 bool isAlive;
dylanslack 0:3f73e98442ec 50 int x, y, size, velocity;
dylanslack 0:3f73e98442ec 51 };
dylanslack 0:3f73e98442ec 52
dylanslack 0:3f73e98442ec 53 SpaceCraft::SpaceCraft() {
dylanslack 0:3f73e98442ec 54 position = 64;
dylanslack 0:3f73e98442ec 55 }
dylanslack 0:3f73e98442ec 56
dylanslack 0:3f73e98442ec 57 Asteroid::Asteroid() {
dylanslack 0:3f73e98442ec 58 x = (rand() % 100) + 10;
dylanslack 0:3f73e98442ec 59 y = 0;
dylanslack 0:3f73e98442ec 60 size = (int)((rand() % 4) + 6);
dylanslack 0:3f73e98442ec 61 velocity = (int)((rand() % 3) + 1);
dylanslack 0:3f73e98442ec 62 isAlive = true;
dylanslack 0:3f73e98442ec 63 }
dylanslack 0:3f73e98442ec 64
dylanslack 0:3f73e98442ec 65 // moves the space craft by clearing it, advancing it, then redrawing it
dylanslack 0:3f73e98442ec 66 void SpaceCraft::move(float x) {
dylanslack 0:3f73e98442ec 67 undraw();
dylanslack 0:3f73e98442ec 68 position = (int)(64.0 - (x * 64.0));
dylanslack 0:3f73e98442ec 69 draw();
dylanslack 0:3f73e98442ec 70 }
dylanslack 0:3f73e98442ec 71
dylanslack 0:3f73e98442ec 72 // moves the asteroid by clearing it, advancing it, then redrawing it
dylanslack 0:3f73e98442ec 73 void Asteroid::move(int posY) {
dylanslack 0:3f73e98442ec 74 if (y == posY) return;
dylanslack 0:3f73e98442ec 75
dylanslack 0:3f73e98442ec 76 undraw();
dylanslack 0:3f73e98442ec 77 y = posY;
dylanslack 0:3f73e98442ec 78
dylanslack 0:3f73e98442ec 79 if (y >= 127) {
dylanslack 0:3f73e98442ec 80 isAlive = false;
dylanslack 0:3f73e98442ec 81 score++;
dylanslack 0:3f73e98442ec 82 } else {
dylanslack 0:3f73e98442ec 83 draw();
dylanslack 0:3f73e98442ec 84 }
dylanslack 0:3f73e98442ec 85 }
dylanslack 0:3f73e98442ec 86
dylanslack 0:3f73e98442ec 87 // moves the asteroid downward according to its velocity
dylanslack 0:3f73e98442ec 88 void Asteroid::fall() {
dylanslack 0:3f73e98442ec 89 move(y + velocity);
dylanslack 0:3f73e98442ec 90 }
dylanslack 0:3f73e98442ec 91
dylanslack 0:3f73e98442ec 92 // resets the astroid by putting it back at the top with a random x coordinate
dylanslack 0:3f73e98442ec 93 void Asteroid::reset() {
dylanslack 0:3f73e98442ec 94 y = 0;
dylanslack 0:3f73e98442ec 95 x = (rand() % 100) + 10;
dylanslack 0:3f73e98442ec 96 velocity = (int)((rand() % 3) + 1);
dylanslack 0:3f73e98442ec 97 isAlive = true;
dylanslack 0:3f73e98442ec 98 }
dylanslack 0:3f73e98442ec 99
dylanslack 0:3f73e98442ec 100 // draws the asteroid
dylanslack 0:3f73e98442ec 101 void Asteroid::draw() {
dylanslack 0:3f73e98442ec 102 int largeCraterRadius = (int)(floor((float)size / 3.0));
dylanslack 0:3f73e98442ec 103 int largeCraterX = x - largeCraterRadius;
dylanslack 0:3f73e98442ec 104 int largeCraterY = y - largeCraterRadius;
dylanslack 0:3f73e98442ec 105
dylanslack 0:3f73e98442ec 106 int smallCraterRadius = (int)(floor((float)size / 4.0));
dylanslack 0:3f73e98442ec 107 int smallCraterX = x + largeCraterRadius;
dylanslack 0:3f73e98442ec 108 int smallCraterY = y + largeCraterRadius;
dylanslack 0:3f73e98442ec 109
dylanslack 0:3f73e98442ec 110 uLCD.filled_circle(x, y, size, 0x909090);
dylanslack 0:3f73e98442ec 111 uLCD.circle(largeCraterX, largeCraterY, largeCraterRadius, 0x404040);
dylanslack 0:3f73e98442ec 112 uLCD.circle(smallCraterX, smallCraterY, smallCraterRadius, 0x404040);
dylanslack 0:3f73e98442ec 113 }
dylanslack 0:3f73e98442ec 114
dylanslack 0:3f73e98442ec 115 // draws the space craft
dylanslack 0:3f73e98442ec 116 void SpaceCraft::draw() {
dylanslack 0:3f73e98442ec 117 int width = 12;
dylanslack 0:3f73e98442ec 118 int left = position - (width / 2);
dylanslack 0:3f73e98442ec 119 int right = position + (width / 2);
dylanslack 0:3f73e98442ec 120 int middle = left + (width / 2);
dylanslack 0:3f73e98442ec 121
dylanslack 0:3f73e98442ec 122 // draw the space craft (blue triangle)
dylanslack 0:3f73e98442ec 123 uLCD.triangle(left, 120, middle, 106, right, 120, BLUE);
dylanslack 0:3f73e98442ec 124
dylanslack 0:3f73e98442ec 125 // draw the flame (red triangle)
dylanslack 0:3f73e98442ec 126 uLCD.triangle(left + 4, 121, middle, 125, right - 4, 121, RED);
dylanslack 0:3f73e98442ec 127 }
dylanslack 0:3f73e98442ec 128
dylanslack 0:3f73e98442ec 129 // clears the asteroid
dylanslack 0:3f73e98442ec 130 void Asteroid::undraw() {
dylanslack 0:3f73e98442ec 131 int largeCraterRadius = (int)(floor((float)size / 3.0));
dylanslack 0:3f73e98442ec 132 int largeCraterX = x - largeCraterRadius;
dylanslack 0:3f73e98442ec 133 int largeCraterY = y - largeCraterRadius;
dylanslack 0:3f73e98442ec 134
dylanslack 0:3f73e98442ec 135 int smallCraterRadius = (int)(floor((float)size / 4.0));
dylanslack 0:3f73e98442ec 136 int smallCraterX = x + largeCraterRadius;
dylanslack 0:3f73e98442ec 137 int smallCraterY = y + largeCraterRadius;
dylanslack 0:3f73e98442ec 138
dylanslack 0:3f73e98442ec 139 uLCD.filled_circle(x, y, size, BLACK);
dylanslack 0:3f73e98442ec 140 uLCD.circle(largeCraterX, largeCraterY, largeCraterRadius, BLACK);
dylanslack 0:3f73e98442ec 141 uLCD.circle(smallCraterX, smallCraterY, smallCraterRadius, BLACK);
dylanslack 0:3f73e98442ec 142 }
dylanslack 0:3f73e98442ec 143
dylanslack 0:3f73e98442ec 144 // clears the space craft
dylanslack 0:3f73e98442ec 145 void SpaceCraft::undraw() {
dylanslack 0:3f73e98442ec 146 int width = 12;
dylanslack 0:3f73e98442ec 147 int left = position - (width / 2);
dylanslack 0:3f73e98442ec 148 int right = position + (width / 2);
dylanslack 0:3f73e98442ec 149 int middle = left + (width / 2);
dylanslack 0:3f73e98442ec 150
dylanslack 0:3f73e98442ec 151 // clear the space craft (blue triangle)
dylanslack 0:3f73e98442ec 152 uLCD.triangle(left, 120, middle, 106, right, 120, BLACK);
dylanslack 0:3f73e98442ec 153
dylanslack 0:3f73e98442ec 154 // clear the flame (red triangle)
dylanslack 0:3f73e98442ec 155 uLCD.triangle(left + 4, 121, middle, 125, right - 4, 121, BLACK);
dylanslack 0:3f73e98442ec 156 }
dylanslack 0:3f73e98442ec 157
dylanslack 0:3f73e98442ec 158 /**
dylanslack 0:3f73e98442ec 159 * Returns true if there has been a collision between an asteroid and a ship.
dylanslack 0:3f73e98442ec 160 * This a very basic collision: it determines if any of the ship's vertices
dylanslack 0:3f73e98442ec 161 * interset with the asteroid's bounding circle. This fails for instances in
dylanslack 0:3f73e98442ec 162 * which the asteroid intesects with the edge of a ship but not one of the
dylanslack 0:3f73e98442ec 163 * vertices.
dylanslack 0:3f73e98442ec 164 *
dylanslack 0:3f73e98442ec 165 * This calculates vertex collision by comparing the circle's radius and the distance
dylanslack 0:3f73e98442ec 166 * between a vertex and the center of an asteroid.
dylanslack 0:3f73e98442ec 167 */
dylanslack 0:3f73e98442ec 168 bool checkCollision(SpaceCraft ship, Asteroid asteroid) {
dylanslack 0:3f73e98442ec 169 // asteroid coordinates
dylanslack 0:3f73e98442ec 170 int asteroidCenterX = asteroid.x;
dylanslack 0:3f73e98442ec 171 int asteroidCenterY = asteroid.y;
dylanslack 0:3f73e98442ec 172 int asteroidRadius = asteroid.size;
dylanslack 0:3f73e98442ec 173
dylanslack 0:3f73e98442ec 174 // ship coordinates
dylanslack 0:3f73e98442ec 175 int shipLeftX = ship.position - 6;
dylanslack 0:3f73e98442ec 176 int shipLeftY = 120;
dylanslack 0:3f73e98442ec 177 int shipRightX = ship.position + 6;
dylanslack 0:3f73e98442ec 178 int shipRightY = 120;
dylanslack 0:3f73e98442ec 179 int shipMiddleX = ship.position;
dylanslack 0:3f73e98442ec 180 int shipMiddleY = 106;
dylanslack 0:3f73e98442ec 181
dylanslack 0:3f73e98442ec 182 // left vertex detection
dylanslack 0:3f73e98442ec 183 if (sqrt(pow(asteroidCenterX - shipLeftX, 2.0) + pow(asteroidCenterY - shipLeftY, 2.0)) <= asteroidRadius)
dylanslack 0:3f73e98442ec 184 return true;
dylanslack 0:3f73e98442ec 185
dylanslack 0:3f73e98442ec 186 // right vertex detection
dylanslack 0:3f73e98442ec 187 if (sqrt(pow(asteroidCenterX - shipRightX, 2.0) + pow(asteroidCenterY - shipRightY, 2.0)) <= asteroidRadius)
dylanslack 0:3f73e98442ec 188 return true;
dylanslack 0:3f73e98442ec 189
dylanslack 0:3f73e98442ec 190 // middle vertex detection
dylanslack 0:3f73e98442ec 191 if (sqrt(pow(asteroidCenterX - shipMiddleX, 2.0) + pow(asteroidCenterY - shipMiddleY, 2.0)) <= asteroidRadius)
dylanslack 0:3f73e98442ec 192 return true;
dylanslack 0:3f73e98442ec 193
dylanslack 0:3f73e98442ec 194 return false;
dylanslack 0:3f73e98442ec 195 }
dylanslack 0:3f73e98442ec 196
dylanslack 0:3f73e98442ec 197 SpaceCraft ship;
dylanslack 0:3f73e98442ec 198 vector<Asteroid> asteroids;
dylanslack 0:3f73e98442ec 199
dylanslack 0:3f73e98442ec 200 int main() {
dylanslack 0:3f73e98442ec 201 float tilt;
dylanslack 0:3f73e98442ec 202
dylanslack 0:3f73e98442ec 203 // don't forget to seed the rand function
dylanslack 0:3f73e98442ec 204 srand(time(NULL));
dylanslack 0:3f73e98442ec 205
dylanslack 0:3f73e98442ec 206 uLCD.reset();
dylanslack 0:3f73e98442ec 207 uLCD.cls();
dylanslack 0:3f73e98442ec 208 uLCD.background_color(BLACK);
dylanslack 0:3f73e98442ec 209 uLCD.printf("\nDodge asteroids!\n\r");
dylanslack 0:3f73e98442ec 210 uLCD.printf("Loading...");
dylanslack 0:3f73e98442ec 211
dylanslack 0:3f73e98442ec 212 wait(1.0);
dylanslack 0:3f73e98442ec 213 uLCD.cls();
dylanslack 0:3f73e98442ec 214 wait(1.0);
dylanslack 0:3f73e98442ec 215
dylanslack 0:3f73e98442ec 216 // jack up the baudrate
dylanslack 0:3f73e98442ec 217 uLCD.baudrate(3000000);
dylanslack 0:3f73e98442ec 218
dylanslack 0:3f73e98442ec 219 // initialize some asteroids
dylanslack 0:3f73e98442ec 220 Asteroid a, b, c, d;
dylanslack 0:3f73e98442ec 221 asteroids.push_back(a);
dylanslack 0:3f73e98442ec 222 asteroids.push_back(b);
dylanslack 0:3f73e98442ec 223 asteroids.push_back(c);
dylanslack 0:3f73e98442ec 224 asteroids.push_back(d);
dylanslack 0:3f73e98442ec 225
dylanslack 0:3f73e98442ec 226 IMU.begin();
dylanslack 0:3f73e98442ec 227
dylanslack 0:3f73e98442ec 228 if (!IMU.begin()) {
dylanslack 0:3f73e98442ec 229 pc.printf("Failed to communicate with IMU.\n");
dylanslack 0:3f73e98442ec 230 return 0;
dylanslack 0:3f73e98442ec 231 }
dylanslack 0:3f73e98442ec 232
dylanslack 0:3f73e98442ec 233 IMU.calibrate(1);
dylanslack 0:3f73e98442ec 234 IMU.calibrateMag(0);
dylanslack 0:3f73e98442ec 235
dylanslack 0:3f73e98442ec 236 if (explosion == NULL) {
dylanslack 0:3f73e98442ec 237 pc.printf("explosion.wav was not opened!\n");
dylanslack 0:3f73e98442ec 238 return 0;
dylanslack 0:3f73e98442ec 239 }
dylanslack 0:3f73e98442ec 240
dylanslack 0:3f73e98442ec 241 while(1) {
dylanslack 0:3f73e98442ec 242 while(!IMU.accelAvailable());
dylanslack 0:3f73e98442ec 243 IMU.readAccel();
dylanslack 0:3f73e98442ec 244
dylanslack 0:3f73e98442ec 245 /**
dylanslack 0:3f73e98442ec 246 * `tilt` is the 'y' acceleration value, ranging between [-1.0, 1.0].
dylanslack 0:3f73e98442ec 247 *
dylanslack 0:3f73e98442ec 248 * This variable will control the spacecraft's horizontal position.
dylanslack 0:3f73e98442ec 249 */
dylanslack 0:3f73e98442ec 250 tilt = IMU.calcAccel(IMU.ay);
dylanslack 0:3f73e98442ec 251
dylanslack 0:3f73e98442ec 252 for(size_t i = 0; i < asteroids.size(); i++) {
dylanslack 0:3f73e98442ec 253 asteroids[i].fall();
dylanslack 0:3f73e98442ec 254
dylanslack 0:3f73e98442ec 255 // reset the asteroid if it goes below the screen
dylanslack 0:3f73e98442ec 256 if (!asteroids[i].isAlive) {
dylanslack 0:3f73e98442ec 257 asteroids[i].reset();
dylanslack 0:3f73e98442ec 258 }
dylanslack 0:3f73e98442ec 259
dylanslack 0:3f73e98442ec 260 // play the explosion sound and end the game if there is a collision
dylanslack 0:3f73e98442ec 261 if (checkCollision(ship, asteroids[i])) {
dylanslack 0:3f73e98442ec 262 uLCD.background_color(RED);
dylanslack 0:3f73e98442ec 263 uLCD.textbackground_color(RED);
dylanslack 0:3f73e98442ec 264 waver.play(explosion);
dylanslack 0:3f73e98442ec 265 fseek(explosion, 0, SEEK_SET);
dylanslack 0:3f73e98442ec 266 uLCD.cls();
dylanslack 0:3f73e98442ec 267 uLCD.color(WHITE);
dylanslack 0:3f73e98442ec 268 uLCD.text_width(3);
dylanslack 0:3f73e98442ec 269 uLCD.text_height(3);
dylanslack 0:3f73e98442ec 270 uLCD.printf("\nGame\nOVER\n\n");
dylanslack 0:3f73e98442ec 271 uLCD.text_width(1);
dylanslack 0:3f73e98442ec 272 uLCD.text_height(1);
dylanslack 0:3f73e98442ec 273 uLCD.printf("Score: %d", score);
dylanslack 0:3f73e98442ec 274 return 0;
dylanslack 0:3f73e98442ec 275 }
dylanslack 0:3f73e98442ec 276 }
dylanslack 0:3f73e98442ec 277
dylanslack 0:3f73e98442ec 278 // update the score
dylanslack 0:3f73e98442ec 279 uLCD.locate(0, 0);
dylanslack 0:3f73e98442ec 280 uLCD.printf("Score: %d", score);
dylanslack 0:3f73e98442ec 281
dylanslack 0:3f73e98442ec 282 // move the ship based on the IMU's accelerometer reading
dylanslack 0:3f73e98442ec 283 ship.move(tilt);
dylanslack 0:3f73e98442ec 284 wait(0.01);
dylanslack 0:3f73e98442ec 285 }
dylanslack 0:3f73e98442ec 286 }