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

Files at this revision

API Documentation at this revision

Comitter:
Mpmart08
Date:
Tue Mar 15 01:11:07 2016 +0000
Commit message:
added comments

Changed in this revision

4DGL-uLCD-SE.lib Show annotated file Show diff for this revision Revisions of this file
PinDetect.lib Show annotated file Show diff for this revision Revisions of this file
SDFileSystem.lib Show annotated file Show diff for this revision Revisions of this file
Speaker.h Show annotated file Show diff for this revision Revisions of this file
bird.cpp Show annotated file Show diff for this revision Revisions of this file
bird.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed-rtos.lib Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
pipe.cpp Show annotated file Show diff for this revision Revisions of this file
pipe.h Show annotated file Show diff for this revision Revisions of this file
soundBuilder.cpp Show annotated file Show diff for this revision Revisions of this file
soundBuilder.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/4DGL-uLCD-SE.lib	Tue Mar 15 01:11:07 2016 +0000
@@ -0,0 +1,1 @@
+http://developer.mbed.org/users/4180_1/code/4DGL-uLCD-SE/#2cb1845d7681
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/PinDetect.lib	Tue Mar 15 01:11:07 2016 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/AjK/code/PinDetect/#cb3afc45028b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SDFileSystem.lib	Tue Mar 15 01:11:07 2016 +0000
@@ -0,0 +1,1 @@
+http://developer.mbed.org/users/neilt6/code/SDFileSystem/#3fa5eaf48e81
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Speaker.h	Tue Mar 15 01:11:07 2016 +0000
@@ -0,0 +1,24 @@
+#include "mbed.h"
+
+// class to play a note on Speaker based on PwmOut class
+#ifndef _SPEAKER_H
+#define _SPEAKER_H
+
+class Speaker
+{
+public:
+    // Constructor
+    Speaker(PinName pin) : _pin(pin) {}
+    // member function to play a given note
+    void PlayNote(float frequency, float duration, float volume) {
+        _pin.period(1.0/frequency);
+        _pin = volume/2.0;
+        wait(duration);
+        _pin = 0.0;
+    }
+
+private:
+    PwmOut _pin;
+};
+
+#endif // _SPEAKER_H
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bird.cpp	Tue Mar 15 01:11:07 2016 +0000
@@ -0,0 +1,38 @@
+#include "bird.h"
+
+Bird::Bird(){
+    x = 16; // the bird starts 16 pixels from the left side of the screen
+    y = 64; // the bird starts 64 pixels from the top of the screen
+}
+
+int Bird::getOldY() {
+    return oldY;   
+}
+
+int Bird::getX(){
+    return x;
+}
+
+int Bird::getY(){
+    return y;
+}
+
+int Bird::getWidth(){
+    return width;
+}
+
+int Bird::getHeight(){
+    return height;
+}
+
+void Bird::move(directionType direction){
+    oldY = y;               // store old y position
+    switch (direction){
+    case DIRECTION_UP:      // the player pressed up
+        y -= moveDistance;  // the bird moves up by a constant distance
+        break;
+    case DIRECTION_DOWN:    // the player pressed down
+        y += moveDistance;  // the bird moves down by a constant distance
+        break;
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bird.h	Tue Mar 15 01:11:07 2016 +0000
@@ -0,0 +1,33 @@
+#ifndef BIRD_H
+#define BIRD_H
+
+// enumerated type that represents the direction that the bird moves
+enum directionType {DIRECTION_UP, DIRECTION_DOWN};
+
+// class that represents the bird in the Flappy Bird game
+class Bird 
+{
+public:
+    // Constructor
+    Bird();
+    // Get Functions
+    int getX();                 // return the bird's current x position
+    int getY();                 // return the bird's current y position
+    int getOldY();              // return the bird's previous y position
+    int getWidth();             // return the bird's width
+    int getHeight();            // return the bird's height
+    // Member Functions
+    void move(directionType);   // moves the bird in a given direction
+
+private:
+    // variables
+    int x;                              // the bird's x position
+    int y;                              // the bird's y position
+    int oldY;                           // the bird's previous y position
+    // constants
+    static const int width = 16;        // the bird's width
+    static const int height = 16;       // the bird's height
+    static const int moveDistance = 8;  // the distance that the bird moves
+};
+
+#endif // BIRD_H
\ No newline at end of file
--- /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;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-rtos.lib	Tue Mar 15 01:11:07 2016 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed-rtos/#d3d0e710b443
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Tue Mar 15 01:11:07 2016 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/87f2f5183dfb
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pipe.cpp	Tue Mar 15 01:11:07 2016 +0000
@@ -0,0 +1,34 @@
+#include "pipe.h"
+#include <stdlib.h>
+
+Pipe::Pipe(){
+    x = 128;                                // the pipe starts at the right side of the screen
+    int random = rand() % 6;                // generates a random number between 0 and 5
+    y = (random + 1) * 16;                  // converts random number to pipe size
+    type = static_cast<pipeType>(random);   // cast int to pipeType
+}
+
+int Pipe::getWidth() {
+    return width;
+}
+
+int Pipe::getOldX() {
+    return oldX;   
+}
+
+int Pipe::getY() {
+    return y;   
+}
+
+int Pipe::getX() {
+    return x;
+}
+
+pipeType Pipe::getType() {
+    return type;
+}
+
+void Pipe::move(int gameSpeed) {
+    oldX = x;       // store old x position
+    x -= gameSpeed; // move the pipe left (distance ranges from 1 to 5)
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pipe.h	Tue Mar 15 01:11:07 2016 +0000
@@ -0,0 +1,32 @@
+#ifndef PIPE_H
+#define PIPE_H
+
+// enumerated type that represents the 6 possible pipe sizes
+enum pipeType {PIPE16, PIPE32, PIPE48, PIPE64, PIPE80, PIPE96};
+
+// class that represents a pair of pipes in the Flappy Bird game
+class Pipe
+{
+public:
+    // Constructor
+    Pipe();
+    // Get Functions
+    int getWidth();             // returns the pipe's width
+    int getX();                 // returns the pipe's current x position
+    int getY();                 // returns the pipe's current y position
+    int getOldX();              // returns the pipe's previous x position
+    pipeType getType();         // returns the pipe's size
+    // Member Functions
+    void move(int gameSpeed);   // moves the pipe given the current game speed
+    
+private:
+    // variables
+    int x;                          // the pipe's current x position
+    int y;                          // the height of the top pipe in the pair of pipes
+    int oldX;                       // the pipe's previous x position
+    pipeType type;                  // the pipe's size
+    // constants
+    static const int width = 32;    // the pipe's width
+};
+
+#endif // PIPE_H
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/soundBuilder.cpp	Tue Mar 15 01:11:07 2016 +0000
@@ -0,0 +1,75 @@
+#include "Speaker.h"
+#include "soundBuilder.h"
+
+Note::Note(){
+    setFreq(0);
+    setLength(0);
+    setVolume(0);
+}
+
+Note::Note(float freq, float dur, float vol){
+    setFreq(freq);
+    setLength(dur);
+    setVolume(vol);
+}
+
+void Note::setFreq(float nfreq){
+    freq = nfreq;
+}
+
+void Note::setLength(float dur){
+    length = dur;
+}
+
+void Note::setVolume(float vol){
+    volume = vol;
+}
+
+float Note::getFreq(){
+    return freq;
+}
+
+float Note::getLength(){
+    return length;
+}
+
+float Note::getVolume(){
+    return volume;
+}
+
+SoundBuilder::SoundBuilder(float freq[], float dur[], float vol[], int numNotes, Speaker *mainspeaker){
+    // add each note to the song
+    for (int i = 0; i < numNotes; i++){
+        song[i].setFreq(freq[i]);
+        song[i].setLength(dur[i]);
+        song[i].setVolume(vol[i]);
+    }
+    speaker = &(*mainspeaker);
+}
+
+void SoundBuilder::setNote(float freq, float dur, float vol, int noteNum){
+    // set frequency, length, and volume of the specified note
+    song[noteNum].setFreq(freq);
+    song[noteNum].setLength(dur);
+    song[noteNum].setVolume(vol);
+}
+
+void SoundBuilder::playNotes(int start, int stop){
+    // play each note in the song successively from given start and stop positions
+    for (int i = start; i <= stop; i++)
+        speaker->PlayNote(song[i].getFreq(), song[i].getLength(), song[i].getVolume());
+}
+
+void SoundBuilder::playSong(){
+    // play each note in the song successively from the beginning to the end
+    playNotes(0, sizeof(song)/sizeof(*song)-1);
+}
+
+void SoundBuilder::clearSong(){
+    // clear all note data from the song
+    for (int i = 0; i < sizeof(song)/sizeof(*song); i++){
+        song[i].setFreq(0);
+        song[i].setLength(0);
+        song[i].setVolume(0);
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/soundBuilder.h	Tue Mar 15 01:11:07 2016 +0000
@@ -0,0 +1,47 @@
+#include "Speaker.h"
+
+#ifndef SOUNDBUILDER_H
+#define SOUNDBUILDER_H
+
+// class that represents a note played by a speaker
+class Note
+{
+public:
+    // Constructor
+    Note();
+    Note(float, float, float);
+    // Set Functions
+    void setFreq(float);        // set the note's frequency
+    void setLength(float);      // set the note's length
+    void setVolume(float);      // set the note's volume
+    // Get Functions
+    float getFreq();            // return the note's frequency
+    float getLength();          // return the note's length
+    float getVolume();          // return the note's volume
+private:
+    float freq;                 // the note's frequency
+    float length;               // the note's length
+    float volume;               // the note's volume
+};
+
+class SoundBuilder
+{
+public:
+    // Set Song
+    SoundBuilder (float [], float [], float [], int, Speaker *);
+    // Set sound, where int is the note number starting from 0
+    void setNote(float, float, float, int);
+    // Play Song from given start and stop positions
+    void playNotes(int, int);
+    // Play Song
+    void playSong();
+    // Clear Songs
+    void clearSong();
+
+private:
+    Note song[20];      // a song of up to 20 notes
+    Speaker *speaker;   // speaker that plays the song
+    
+};
+
+#endif // SOUNDBUILDER_H
\ No newline at end of file