![](/media/cache/img/default_profile.jpg.50x50_q85.jpg)
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, committed 2016-03-15
- Comitter:
- Mpmart08
- Date:
- Tue Mar 15 01:11:07 2016 +0000
- Commit message:
- added comments
Changed in this revision
diff -r 000000000000 -r cd1d2540aaf4 4DGL-uLCD-SE.lib --- /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
diff -r 000000000000 -r cd1d2540aaf4 PinDetect.lib --- /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
diff -r 000000000000 -r cd1d2540aaf4 SDFileSystem.lib --- /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
diff -r 000000000000 -r cd1d2540aaf4 Speaker.h --- /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
diff -r 000000000000 -r cd1d2540aaf4 bird.cpp --- /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
diff -r 000000000000 -r cd1d2540aaf4 bird.h --- /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
diff -r 000000000000 -r cd1d2540aaf4 main.cpp --- /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; + } + } +}
diff -r 000000000000 -r cd1d2540aaf4 mbed-rtos.lib --- /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
diff -r 000000000000 -r cd1d2540aaf4 mbed.bld --- /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
diff -r 000000000000 -r cd1d2540aaf4 pipe.cpp --- /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
diff -r 000000000000 -r cd1d2540aaf4 pipe.h --- /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
diff -r 000000000000 -r cd1d2540aaf4 soundBuilder.cpp --- /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
diff -r 000000000000 -r cd1d2540aaf4 soundBuilder.h --- /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