Flappy Bird game on mbed with a micro LCD screen, class D amp, speaker, SD card reader/writer, 5-button navigation switch, and potentiometer speed control

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

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 #include "mbed.h"
00002 #include "rtos.h"
00003 #include "PinDetect.h"
00004 #include "uLCD_4DGL.h"
00005 #include "Speaker.h"
00006 #include "SDFileSystem.h"
00007 #include "pipe.h"
00008 #include "bird.h"
00009 #include "soundBuilder.h"
00010 #include <stdlib.h>
00011 
00012 // image sectors on ulcd sd card
00013 #define BACKGROUND 0x0000, 0x0000   // background
00014 #define FLAPPY     0x0000, 0x0041   // bird
00015 #define PIPEUP     0x0000, 0x0043   // pipe pointing up
00016 #define PIPEDOWN16 0x0000, 0x0054   // pipe pointing down with a height of 16
00017 #define PIPEDOWN32 0x0000, 0x0057   // pipe pointing down with a height of 32
00018 #define PIPEDOWN48 0x0000, 0x005C   // pipe pointing down with a height of 48
00019 #define PIPEDOWN64 0x0000, 0x0063   // pipe pointing down with a height of 64
00020 #define PIPEDOWN80 0x0000, 0x006C   // pipe pointing down with a height of 80
00021 #define PIPEDOWN96 0x0000, 0x0077   // pipe pointing down with a height of 96
00022 
00023 // Nav Switch
00024 PinDetect nsUp(p16);        // up button
00025 PinDetect nsCenter(p15);    // center button
00026 PinDetect nsDown(p13);      // down button
00027 
00028 // uLCD
00029 uLCD_4DGL uLCD(p28, p27, p30);
00030 
00031 // Speaker
00032 Speaker mySpeaker(p25);
00033 
00034 // SD File System
00035 SDFileSystem sd(p5, p6, p7, p8, "sd");
00036 
00037 // Potentiometer
00038 AnalogIn pot(p20);
00039 
00040 // Bird
00041 Bird flappyBird;
00042 
00043 // Pipe
00044 Pipe pipe;
00045 
00046 int score = 0;
00047 int oldScore = 0;
00048 int gameSpeed = 0;
00049  
00050 // State machine definitions
00051 enum gameStateType {START, WAIT, GAME_SETUP, GAME, LOSE};
00052 /* State Definitions:
00053  * START -- Creates the start screen
00054  * WAIT -- After the start screen, goes into wait where mbed waits for player to start the game
00055  * GAME_SETUP -- Initializes game objects such as the bird and pipe
00056  * GAME -- When the user actually gets to play
00057  * LOSE -- clears the screen, prints you lose and if you get a high score, stores it, then goes back to start
00058  */
00059  
00060 // Global state machine variable
00061 gameStateType gameState = START;
00062 
00063 // function that draws each frame of the game on the lcd
00064 void drawScreen() {
00065     
00066     // clears the pipe from its previous location
00067     uLCD.filled_rectangle(                  // draws a rectangle the size of the pipe
00068         pipe.getOldX(),                     // the pipe's old x position
00069         0,                                  // the top of the screen
00070         pipe.getOldX() + pipe.getWidth(),   // the pipe's old x position + the pipe's width
00071         127,                                // the bottom of the screen
00072         0x4EC0CA                            // the color of the background
00073     );
00074     
00075     // draws the lower pipe at its current location
00076     uLCD.set_sector_address(PIPEUP);
00077     int x = pipe.getX();
00078     int y = pipe.getY() + 32;
00079     uLCD.display_image(x,y);
00080     
00081     // draws the upper pipe at its current location
00082     switch (pipe.getType()) {
00083         case PIPE16:
00084             uLCD.set_sector_address(PIPEDOWN16);
00085             break;
00086         case PIPE32:
00087             uLCD.set_sector_address(PIPEDOWN32);
00088             break;
00089         case PIPE48:
00090             uLCD.set_sector_address(PIPEDOWN48);
00091             break;
00092         case PIPE64:
00093             uLCD.set_sector_address(PIPEDOWN64);
00094             break;
00095         case PIPE80:
00096             uLCD.set_sector_address(PIPEDOWN80);
00097             break;
00098         case PIPE96:
00099             uLCD.set_sector_address(PIPEDOWN96);
00100             break;
00101     }
00102     x = pipe.getX();
00103     y = 0;
00104     uLCD.display_image(x,y);
00105     
00106     // clears the bird from its previous location
00107     uLCD.filled_rectangle(                              // draws a rectangle the size of the bird
00108         flappyBird.getX(),                              // the bird's current x position
00109         flappyBird.getOldY(),                           // the bird's old y position
00110         flappyBird.getX() + flappyBird.getWidth(),      // the bird's x position + the bird's width
00111         flappyBird.getOldY() + flappyBird.getHeight(),  // the bird's old y position + the bird's height
00112         0x4EC0CA                                        // the color of the background
00113     );
00114     
00115     // draws the bird at its current location
00116     uLCD.set_sector_address(FLAPPY);
00117     x = flappyBird.getX();
00118     y = flappyBird.getY();
00119     uLCD.display_image(x,y);
00120     
00121     // prints the player's current score
00122     uLCD.locate(1,1);
00123     uLCD.printf("%d", score);
00124 }
00125 
00126 // function that determines if the bird has collided with a pipe
00127 bool checkCollision() {
00128 
00129     if (pipe.getX() <= flappyBird.getX() + flappyBird.getWidth()
00130         && flappyBird.getX() <= pipe.getX() + pipe.getWidth()) {
00131         return (flappyBird.getY() < pipe.getY() || flappyBird.getY() >= (pipe.getY() + 32));
00132     } else {
00133         return false;   
00134     }
00135 }
00136 
00137 // Nav switch callbacks
00138 
00139 void nsUp_hit_callback() // the up button is pressed
00140 {
00141     switch (gameState)
00142     {
00143     case GAME: // if game is running, then move the bird upwards
00144         flappyBird.move(DIRECTION_UP);
00145         break;
00146     }
00147 }
00148  
00149 void nsDown_hit_callback() // the down button is pressed
00150 {
00151     switch (gameState)
00152     {
00153     case GAME: // if game is running, then move the bird downwards
00154         flappyBird.move(DIRECTION_DOWN);
00155         break;
00156     }
00157 }
00158 
00159 void nsCenter_hit_callback() // the center button is pressed
00160 {
00161     switch (gameState)
00162     {
00163     case WAIT: // if game is waiting to start, set up the game
00164         gameState = GAME_SETUP;
00165         break;
00166     }
00167 }
00168 
00169 // thread that plays game sounds through the speaker
00170 void speaker_thread(void const *argument) {
00171 
00172     Speaker *player = &mySpeaker;
00173 
00174     // Start Song
00175     float sFreq[] = {550,750,550,750};
00176     float sDur[] = {.3,.3,.3,.3};
00177     float sVol[] = {.5,.5,.5,.5};
00178     SoundBuilder startSong(sFreq, sDur, sVol, sizeof(sFreq)/sizeof(*sFreq), player);
00179     
00180     // End Song
00181     float eFreq[] = {300,200,250,225,200,150,150,100};
00182     float eDur[] = {.3,.3,.3,.3,.3,.3,.3,.3};
00183     float eVol[] = {.5,.5,.5,.5,.5,.5,.5,.5};
00184     SoundBuilder endSong(eFreq, eDur, eVol, sizeof(eFreq)/sizeof(*eFreq), player);
00185 
00186     while (true) {
00187         switch (gameState) {
00188         case GAME: // if game is running and user dodges a pipe, play a note
00189             if (oldScore < score) {
00190                 mySpeaker.PlayNote(440, 0.1, 0.5);
00191                 oldScore = score;
00192             }
00193             break;
00194         case START: // play a song at the start of the game
00195             startSong.playSong();
00196             break;
00197         case LOSE: // play a song when the player loses the game
00198             endSong.playSong();
00199             wait(5);
00200             break;
00201         }
00202     }
00203 }
00204 
00205 int main() {
00206     
00207     // Setup internal pullup resistors for nav switch input pins
00208     nsUp.mode(PullUp);
00209     nsDown.mode(PullUp);
00210     nsCenter.mode(PullUp);
00211     // Wait for pullup to take effect
00212     wait(0.1);
00213     // Attaches nav switch inputs to the callback functions
00214     nsUp.attach_deasserted(&nsUp_hit_callback);
00215     nsDown.attach_deasserted(&nsDown_hit_callback);
00216     nsCenter.attach_deasserted(&nsCenter_hit_callback);
00217     // Set sample frequency for nav switch interrupts
00218     nsUp.setSampleFrequency();
00219     nsDown.setSampleFrequency();
00220     nsCenter.setSampleFrequency();
00221     
00222     // initialize the lcd
00223     uLCD.media_init();
00224     uLCD.cls();
00225     uLCD.background_color(0x4EC0CA);
00226     uLCD.textbackground_color(0x4EC0CA);
00227     uLCD.color(WHITE);
00228     
00229     int highscore = 0;  // variable to store high score
00230     int i = 0;          // variable for seeding random pipe generation
00231     
00232     Thread thread1(speaker_thread); // start speaker thread
00233     
00234     while (1) 
00235     {   
00236         switch (gameState)
00237         {
00238         case START:
00239         
00240             // read current high score from a text file on the sd card
00241             FILE *fp = fopen("/sd/highscore.txt", "r");
00242             if(fp == NULL) {
00243                 error("Could not open file for read\n");
00244             }
00245             fscanf(fp, "%i", &highscore);
00246             fclose(fp);
00247             
00248             // display start screen on lcd
00249             uLCD.cls();
00250             uLCD.locate(0,0);
00251             uLCD.printf("Flappy Bird(ish)!\n\n");
00252             uLCD.printf("Press Fire\nto Start\n\n");
00253             uLCD.printf("High Score: %i", highscore);
00254             gameState = WAIT;
00255             break;
00256             
00257         case GAME_SETUP:
00258         
00259             // initialize game objects and draw initial frame on the lcd
00260             uLCD.cls();
00261             pipe = Pipe();
00262             drawScreen();
00263             srand(i);
00264             score = 0;
00265             oldScore = 0;
00266             gameState = GAME;
00267             break;
00268             
00269         case GAME:
00270         
00271             // read the game speed from the potentiometer
00272             gameSpeed = (int) (pot + 1) * 5;
00273             
00274             // if there is a collision, the player loses
00275             if (checkCollision()) {
00276                 gameState = LOSE; 
00277             }
00278             
00279             // if the player dodges a pipe, increase the score and create a new pipe
00280             if (pipe.getX() + pipe.getWidth() < 0) {
00281                 score++;
00282                 pipe = Pipe();   
00283             }
00284             
00285             // draw the current frame of the game on the lcd
00286             drawScreen();
00287             
00288             // move the pipe across the screen according the game speed
00289             pipe.move(gameSpeed);
00290             
00291             // wait for lcd to finish drawing images
00292             wait(0.25);
00293             break;
00294             
00295         case LOSE:
00296         
00297             // display game over screen on lcd
00298             uLCD.cls();
00299             uLCD.printf("YOU LOSE D:\n\n");
00300             
00301             // if the user beat the high score, save and display high score info
00302             if (score > highscore){
00303                 uLCD.printf("CONGRATZ THO NEW HIGH SCORE!");
00304                 uLCD.printf("           %i", score);
00305                 // overwrite previous high score
00306                 fp = fopen("/sd/highscore.txt", "w");
00307                 fprintf(fp, "%i", score);
00308                 fclose(fp);
00309             }
00310             
00311             // restart game after 5 seconds
00312             wait(5.0);
00313             gameState = START;
00314             break;
00315         case WAIT:
00316         
00317             i++; // Used to seed the rand() function so we don't get the same positions for the pipes every time
00318             break;
00319         }
00320     }
00321 }