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
Diff: main.cpp
- 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; + } + } +}