Dylan Slack / Mbed 2 deprecated DodgingAsteroids

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

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 #include "LSM9DS1.h"
00002 #include "uLCD_4DGL.h"
00003 #include "wave_player.h"
00004 #include "SDFileSystem.h"
00005 #include <math.h>
00006 #include <vector>
00007 #include <time.h>
00008 
00009 uLCD_4DGL uLCD(p28, p27, p30);
00010 SDFileSystem sd(p5, p6, p7, p8, "sd");
00011 Serial pc(USBTX, USBRX);
00012 LSM9DS1 IMU(p9, p10, 0xD6, 0x3C);
00013 AnalogOut DACout(p18);
00014 wave_player waver(&DACout);
00015 
00016 FILE *explosion = fopen("/sd/explosion.wav", "r");
00017 
00018 int score = 0;
00019 
00020 /**
00021  * The SpaceCraft class has a single position variable and
00022  * the methods required to move, draw, and clear a SpaceCraft
00023  * instance.
00024  */
00025 class SpaceCraft {
00026     public:
00027         SpaceCraft();
00028         void move(float);
00029         void draw();
00030         void undraw();
00031         int position;
00032 };
00033 
00034 /**
00035  * Asteroids are similar to SpaceCrafts but are generated with
00036  * a random size, position, and velocity after being invoked.
00037  * The `isAlive` variable becomes false when the asteroid should
00038  * be reset, where it is given another random position, size, and 
00039  * velocity.
00040  */
00041 class Asteroid {
00042     public:
00043         Asteroid();
00044         void move(int);
00045         void reset();
00046         void fall();
00047         void draw();
00048         void undraw();
00049         bool isAlive;
00050         int x, y, size, velocity;
00051 };
00052 
00053 SpaceCraft::SpaceCraft() {
00054     position = 64;
00055 }
00056 
00057 Asteroid::Asteroid() {    
00058     x = (rand() % 100) + 10;
00059     y = 0;
00060     size = (int)((rand() % 4) + 6);
00061     velocity = (int)((rand() % 3) + 1);
00062     isAlive = true;
00063 }
00064 
00065 // moves the space craft by clearing it, advancing it, then redrawing it
00066 void SpaceCraft::move(float x) {
00067     undraw();
00068     position = (int)(64.0 - (x * 64.0));
00069     draw();
00070 }
00071 
00072 // moves the asteroid by clearing it, advancing it, then redrawing it
00073 void Asteroid::move(int posY) {
00074     if (y == posY) return;
00075     
00076     undraw();
00077     y = posY;
00078     
00079     if (y >= 127) {
00080         isAlive = false;
00081         score++;
00082     } else {
00083         draw();   
00084     }
00085 }
00086 
00087 // moves the asteroid downward according to its velocity
00088 void Asteroid::fall() {
00089     move(y + velocity);
00090 }
00091 
00092 // resets the astroid by putting it back at the top with a random x coordinate
00093 void Asteroid::reset() {
00094     y = 0;
00095     x = (rand() % 100) + 10;
00096     velocity = (int)((rand() % 3) + 1);
00097     isAlive = true;
00098 }
00099 
00100 // draws the asteroid
00101 void Asteroid::draw() {
00102     int largeCraterRadius = (int)(floor((float)size / 3.0));
00103     int largeCraterX = x - largeCraterRadius;
00104     int largeCraterY = y - largeCraterRadius;
00105     
00106     int smallCraterRadius = (int)(floor((float)size / 4.0));
00107     int smallCraterX = x + largeCraterRadius;
00108     int smallCraterY = y + largeCraterRadius;
00109     
00110     uLCD.filled_circle(x, y, size, 0x909090);
00111     uLCD.circle(largeCraterX, largeCraterY, largeCraterRadius, 0x404040);
00112     uLCD.circle(smallCraterX, smallCraterY, smallCraterRadius, 0x404040);
00113 }
00114 
00115 // draws the space craft
00116 void SpaceCraft::draw() {
00117     int width = 12;
00118     int left = position - (width / 2);
00119     int right = position + (width / 2);
00120     int middle = left + (width / 2);
00121     
00122     // draw the space craft (blue triangle)
00123     uLCD.triangle(left, 120, middle, 106, right, 120, BLUE);
00124     
00125     // draw the flame (red triangle)
00126     uLCD.triangle(left + 4, 121, middle, 125, right - 4, 121, RED);
00127 }
00128 
00129 // clears the asteroid
00130 void Asteroid::undraw() {
00131     int largeCraterRadius = (int)(floor((float)size / 3.0));
00132     int largeCraterX = x - largeCraterRadius;
00133     int largeCraterY = y - largeCraterRadius;
00134     
00135     int smallCraterRadius = (int)(floor((float)size / 4.0));
00136     int smallCraterX = x + largeCraterRadius;
00137     int smallCraterY = y + largeCraterRadius;
00138     
00139     uLCD.filled_circle(x, y, size, BLACK);
00140     uLCD.circle(largeCraterX, largeCraterY, largeCraterRadius, BLACK);
00141     uLCD.circle(smallCraterX, smallCraterY, smallCraterRadius, BLACK);
00142 }
00143 
00144 // clears the space craft
00145 void SpaceCraft::undraw() {
00146     int width = 12;
00147     int left = position - (width / 2);
00148     int right = position + (width / 2);
00149     int middle = left + (width / 2);
00150     
00151     // clear the space craft (blue triangle)
00152     uLCD.triangle(left, 120, middle, 106, right, 120, BLACK);
00153     
00154     // clear the flame (red triangle)
00155     uLCD.triangle(left + 4, 121, middle, 125, right - 4, 121, BLACK);
00156 }
00157 
00158 /**
00159  * Returns true if there has been a collision between an asteroid and a ship.
00160  * This a very basic collision: it determines if any of the ship's vertices
00161  * interset with the asteroid's bounding circle. This fails for instances in
00162  * which the asteroid intesects with the edge of a ship but not one of the
00163  * vertices.
00164  *
00165  * This calculates vertex collision by comparing the circle's radius and the distance
00166  * between a vertex and the center of an asteroid.
00167  */
00168 bool checkCollision(SpaceCraft ship, Asteroid asteroid) {
00169     // asteroid coordinates
00170     int asteroidCenterX = asteroid.x;
00171     int asteroidCenterY = asteroid.y;
00172     int asteroidRadius = asteroid.size;
00173     
00174     // ship coordinates
00175     int shipLeftX = ship.position - 6;
00176     int shipLeftY = 120;
00177     int shipRightX = ship.position + 6;
00178     int shipRightY = 120;
00179     int shipMiddleX = ship.position;
00180     int shipMiddleY = 106;
00181        
00182     // left vertex detection
00183     if (sqrt(pow(asteroidCenterX - shipLeftX, 2.0) + pow(asteroidCenterY - shipLeftY, 2.0)) <= asteroidRadius)
00184         return true;
00185     
00186     // right vertex detection
00187     if (sqrt(pow(asteroidCenterX - shipRightX, 2.0) + pow(asteroidCenterY - shipRightY, 2.0)) <= asteroidRadius)
00188         return true;
00189     
00190     // middle vertex detection
00191     if (sqrt(pow(asteroidCenterX - shipMiddleX, 2.0) + pow(asteroidCenterY - shipMiddleY, 2.0)) <= asteroidRadius)
00192         return true;
00193     
00194     return false;
00195 }
00196 
00197 SpaceCraft ship;
00198 vector<Asteroid> asteroids;
00199 
00200 int main() {
00201     float tilt;
00202     
00203     // don't forget to seed the rand function
00204     srand(time(NULL));
00205     
00206     uLCD.reset();
00207     uLCD.cls();
00208     uLCD.background_color(BLACK);
00209     uLCD.printf("\nDodge asteroids!\n\r");
00210     uLCD.printf("Loading...");
00211     
00212     wait(1.0);
00213     uLCD.cls();
00214     wait(1.0);
00215 
00216     // jack up the baudrate
00217     uLCD.baudrate(3000000);
00218     
00219     // initialize some asteroids
00220     Asteroid a, b, c, d;
00221     asteroids.push_back(a);
00222     asteroids.push_back(b);
00223     asteroids.push_back(c);
00224     asteroids.push_back(d);
00225     
00226     IMU.begin();
00227     
00228     if (!IMU.begin()) {
00229         pc.printf("Failed to communicate with IMU.\n");
00230         return 0;
00231     }
00232     
00233     IMU.calibrate(1);
00234     IMU.calibrateMag(0);
00235     
00236     if (explosion == NULL) {
00237         pc.printf("explosion.wav was not opened!\n");
00238         return 0;
00239     }
00240         
00241     while(1) {
00242         while(!IMU.accelAvailable());
00243         IMU.readAccel();
00244 
00245         /**
00246          * `tilt` is the 'y' acceleration value, ranging between [-1.0, 1.0].
00247          *
00248          * This variable will control the spacecraft's horizontal position.
00249          */
00250         tilt = IMU.calcAccel(IMU.ay);
00251         
00252         for(size_t i = 0; i < asteroids.size(); i++) {
00253             asteroids[i].fall();
00254 
00255             // reset the asteroid if it goes below the screen
00256             if (!asteroids[i].isAlive) {
00257                 asteroids[i].reset();
00258             }
00259             
00260             // play the explosion sound and end the game if there is a collision
00261             if (checkCollision(ship, asteroids[i])) {
00262                 uLCD.background_color(RED);
00263                 uLCD.textbackground_color(RED);
00264                 waver.play(explosion);
00265                 fseek(explosion, 0, SEEK_SET);
00266                 uLCD.cls();
00267                 uLCD.color(WHITE);
00268                 uLCD.text_width(3);
00269                 uLCD.text_height(3);
00270                 uLCD.printf("\nGame\nOVER\n\n");
00271                 uLCD.text_width(1);
00272                 uLCD.text_height(1);
00273                 uLCD.printf("Score: %d", score);
00274                 return 0;
00275             }
00276         }
00277         
00278         // update the score
00279         uLCD.locate(0, 0);
00280         uLCD.printf("Score: %d", score);
00281         
00282         // move the ship based on the IMU's accelerometer reading
00283         ship.move(tilt);
00284         wait(0.01);
00285     }
00286 }