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

Revision:
0:cd1d2540aaf4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Tue Mar 15 01:11:07 2016 +0000
@@ -0,0 +1,321 @@
+#include "mbed.h"
+#include "rtos.h"
+#include "PinDetect.h"
+#include "uLCD_4DGL.h"
+#include "Speaker.h"
+#include "SDFileSystem.h"
+#include "pipe.h"
+#include "bird.h"
+#include "soundBuilder.h"
+#include <stdlib.h>
+
+// image sectors on ulcd sd card
+#define BACKGROUND 0x0000, 0x0000   // background
+#define FLAPPY     0x0000, 0x0041   // bird
+#define PIPEUP     0x0000, 0x0043   // pipe pointing up
+#define PIPEDOWN16 0x0000, 0x0054   // pipe pointing down with a height of 16
+#define PIPEDOWN32 0x0000, 0x0057   // pipe pointing down with a height of 32
+#define PIPEDOWN48 0x0000, 0x005C   // pipe pointing down with a height of 48
+#define PIPEDOWN64 0x0000, 0x0063   // pipe pointing down with a height of 64
+#define PIPEDOWN80 0x0000, 0x006C   // pipe pointing down with a height of 80
+#define PIPEDOWN96 0x0000, 0x0077   // pipe pointing down with a height of 96
+
+// Nav Switch
+PinDetect nsUp(p16);        // up button
+PinDetect nsCenter(p15);    // center button
+PinDetect nsDown(p13);      // down button
+
+// uLCD
+uLCD_4DGL uLCD(p28, p27, p30);
+
+// Speaker
+Speaker mySpeaker(p25);
+
+// SD File System
+SDFileSystem sd(p5, p6, p7, p8, "sd");
+
+// Potentiometer
+AnalogIn pot(p20);
+
+// Bird
+Bird flappyBird;
+
+// Pipe
+Pipe pipe;
+
+int score = 0;
+int oldScore = 0;
+int gameSpeed = 0;
+ 
+// State machine definitions
+enum gameStateType {START, WAIT, GAME_SETUP, GAME, LOSE};
+/* State Definitions:
+ * START -- Creates the start screen
+ * WAIT -- After the start screen, goes into wait where mbed waits for player to start the game
+ * GAME_SETUP -- Initializes game objects such as the bird and pipe
+ * GAME -- When the user actually gets to play
+ * LOSE -- clears the screen, prints you lose and if you get a high score, stores it, then goes back to start
+ */
+ 
+// Global state machine variable
+gameStateType gameState = START;
+
+// function that draws each frame of the game on the lcd
+void drawScreen() {
+    
+    // clears the pipe from its previous location
+    uLCD.filled_rectangle(                  // draws a rectangle the size of the pipe
+        pipe.getOldX(),                     // the pipe's old x position
+        0,                                  // the top of the screen
+        pipe.getOldX() + pipe.getWidth(),   // the pipe's old x position + the pipe's width
+        127,                                // the bottom of the screen
+        0x4EC0CA                            // the color of the background
+    );
+    
+    // draws the lower pipe at its current location
+    uLCD.set_sector_address(PIPEUP);
+    int x = pipe.getX();
+    int y = pipe.getY() + 32;
+    uLCD.display_image(x,y);
+    
+    // draws the upper pipe at its current location
+    switch (pipe.getType()) {
+        case PIPE16:
+            uLCD.set_sector_address(PIPEDOWN16);
+            break;
+        case PIPE32:
+            uLCD.set_sector_address(PIPEDOWN32);
+            break;
+        case PIPE48:
+            uLCD.set_sector_address(PIPEDOWN48);
+            break;
+        case PIPE64:
+            uLCD.set_sector_address(PIPEDOWN64);
+            break;
+        case PIPE80:
+            uLCD.set_sector_address(PIPEDOWN80);
+            break;
+        case PIPE96:
+            uLCD.set_sector_address(PIPEDOWN96);
+            break;
+    }
+    x = pipe.getX();
+    y = 0;
+    uLCD.display_image(x,y);
+    
+    // clears the bird from its previous location
+    uLCD.filled_rectangle(                              // draws a rectangle the size of the bird
+        flappyBird.getX(),                              // the bird's current x position
+        flappyBird.getOldY(),                           // the bird's old y position
+        flappyBird.getX() + flappyBird.getWidth(),      // the bird's x position + the bird's width
+        flappyBird.getOldY() + flappyBird.getHeight(),  // the bird's old y position + the bird's height
+        0x4EC0CA                                        // the color of the background
+    );
+    
+    // draws the bird at its current location
+    uLCD.set_sector_address(FLAPPY);
+    x = flappyBird.getX();
+    y = flappyBird.getY();
+    uLCD.display_image(x,y);
+    
+    // prints the player's current score
+    uLCD.locate(1,1);
+    uLCD.printf("%d", score);
+}
+
+// function that determines if the bird has collided with a pipe
+bool checkCollision() {
+
+    if (pipe.getX() <= flappyBird.getX() + flappyBird.getWidth()
+        && flappyBird.getX() <= pipe.getX() + pipe.getWidth()) {
+        return (flappyBird.getY() < pipe.getY() || flappyBird.getY() >= (pipe.getY() + 32));
+    } else {
+        return false;   
+    }
+}
+
+// Nav switch callbacks
+
+void nsUp_hit_callback() // the up button is pressed
+{
+    switch (gameState)
+    {
+    case GAME: // if game is running, then move the bird upwards
+        flappyBird.move(DIRECTION_UP);
+        break;
+    }
+}
+ 
+void nsDown_hit_callback() // the down button is pressed
+{
+    switch (gameState)
+    {
+    case GAME: // if game is running, then move the bird downwards
+        flappyBird.move(DIRECTION_DOWN);
+        break;
+    }
+}
+
+void nsCenter_hit_callback() // the center button is pressed
+{
+    switch (gameState)
+    {
+    case WAIT: // if game is waiting to start, set up the game
+        gameState = GAME_SETUP;
+        break;
+    }
+}
+
+// thread that plays game sounds through the speaker
+void speaker_thread(void const *argument) {
+
+    Speaker *player = &mySpeaker;
+
+    // Start Song
+    float sFreq[] = {550,750,550,750};
+    float sDur[] = {.3,.3,.3,.3};
+    float sVol[] = {.5,.5,.5,.5};
+    SoundBuilder startSong(sFreq, sDur, sVol, sizeof(sFreq)/sizeof(*sFreq), player);
+    
+    // End Song
+    float eFreq[] = {300,200,250,225,200,150,150,100};
+    float eDur[] = {.3,.3,.3,.3,.3,.3,.3,.3};
+    float eVol[] = {.5,.5,.5,.5,.5,.5,.5,.5};
+    SoundBuilder endSong(eFreq, eDur, eVol, sizeof(eFreq)/sizeof(*eFreq), player);
+
+    while (true) {
+        switch (gameState) {
+        case GAME: // if game is running and user dodges a pipe, play a note
+            if (oldScore < score) {
+                mySpeaker.PlayNote(440, 0.1, 0.5);
+                oldScore = score;
+            }
+            break;
+        case START: // play a song at the start of the game
+            startSong.playSong();
+            break;
+        case LOSE: // play a song when the player loses the game
+            endSong.playSong();
+            wait(5);
+            break;
+        }
+    }
+}
+
+int main() {
+    
+    // Setup internal pullup resistors for nav switch input pins
+    nsUp.mode(PullUp);
+    nsDown.mode(PullUp);
+    nsCenter.mode(PullUp);
+    // Wait for pullup to take effect
+    wait(0.1);
+    // Attaches nav switch inputs to the callback functions
+    nsUp.attach_deasserted(&nsUp_hit_callback);
+    nsDown.attach_deasserted(&nsDown_hit_callback);
+    nsCenter.attach_deasserted(&nsCenter_hit_callback);
+    // Set sample frequency for nav switch interrupts
+    nsUp.setSampleFrequency();
+    nsDown.setSampleFrequency();
+    nsCenter.setSampleFrequency();
+    
+    // initialize the lcd
+    uLCD.media_init();
+    uLCD.cls();
+    uLCD.background_color(0x4EC0CA);
+    uLCD.textbackground_color(0x4EC0CA);
+    uLCD.color(WHITE);
+    
+    int highscore = 0;  // variable to store high score
+    int i = 0;          // variable for seeding random pipe generation
+    
+    Thread thread1(speaker_thread); // start speaker thread
+    
+    while (1) 
+    {   
+        switch (gameState)
+        {
+        case START:
+        
+            // read current high score from a text file on the sd card
+            FILE *fp = fopen("/sd/highscore.txt", "r");
+            if(fp == NULL) {
+                error("Could not open file for read\n");
+            }
+            fscanf(fp, "%i", &highscore);
+            fclose(fp);
+            
+            // display start screen on lcd
+            uLCD.cls();
+            uLCD.locate(0,0);
+            uLCD.printf("Flappy Bird(ish)!\n\n");
+            uLCD.printf("Press Fire\nto Start\n\n");
+            uLCD.printf("High Score: %i", highscore);
+            gameState = WAIT;
+            break;
+            
+        case GAME_SETUP:
+        
+            // initialize game objects and draw initial frame on the lcd
+            uLCD.cls();
+            pipe = Pipe();
+            drawScreen();
+            srand(i);
+            score = 0;
+            oldScore = 0;
+            gameState = GAME;
+            break;
+            
+        case GAME:
+        
+            // read the game speed from the potentiometer
+            gameSpeed = (int) (pot + 1) * 5;
+            
+            // if there is a collision, the player loses
+            if (checkCollision()) {
+                gameState = LOSE; 
+            }
+            
+            // if the player dodges a pipe, increase the score and create a new pipe
+            if (pipe.getX() + pipe.getWidth() < 0) {
+                score++;
+                pipe = Pipe();   
+            }
+            
+            // draw the current frame of the game on the lcd
+            drawScreen();
+            
+            // move the pipe across the screen according the game speed
+            pipe.move(gameSpeed);
+            
+            // wait for lcd to finish drawing images
+            wait(0.25);
+            break;
+            
+        case LOSE:
+        
+            // display game over screen on lcd
+            uLCD.cls();
+            uLCD.printf("YOU LOSE D:\n\n");
+            
+            // if the user beat the high score, save and display high score info
+            if (score > highscore){
+                uLCD.printf("CONGRATZ THO NEW HIGH SCORE!");
+                uLCD.printf("           %i", score);
+                // overwrite previous high score
+                fp = fopen("/sd/highscore.txt", "w");
+                fprintf(fp, "%i", score);
+                fclose(fp);
+            }
+            
+            // restart game after 5 seconds
+            wait(5.0);
+            gameState = START;
+            break;
+        case WAIT:
+        
+            i++; // Used to seed the rand() function so we don't get the same positions for the pipes every time
+            break;
+        }
+    }
+}