Dodging asteroids game.
Dependencies: 4DGL-uLCD-SE PinDetect SDFileSystem mbed wave_player
main.cpp@0:3f73e98442ec, 2016-03-14 (annotated)
- Committer:
- dylanslack
- Date:
- Mon Mar 14 03:08:37 2016 +0000
- Revision:
- 0:3f73e98442ec
Initial commit
Who changed what in which revision?
User | Revision | Line number | New 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 | } |