a flappy bird game demo of the RA8875 display library.

Dependencies:   mbed RA8875

I came across some code for a flappy bird game at www.HowToMechatronics.com. It might entertain little kids for a while, so I did a quick port from arduino to mbed and for the RA8875 display library.

main.cpp

Committer:
WiredHome
Date:
2020-03-29
Revision:
7:39363a4f6255
Parent:
5:f1fe5ccc4d5b

File content as of revision 7:39363a4f6255:

/*  Arduino Game Proejct
 *  Program made by Dejan Nedelkovski,
 *  www.HowToMechatronics.com
 *
 * Adapted to the RA8875 and mbed by David Smart
 *
 * Possible Improvements:
 *  - create an in-ram image of the bird(s) and blit them.
 *  - use 2-layers and transparency. Put the bird and pillar in front of the sky
 *  - use block-move for the bird and the sky
 *  - tune the easy/hard configuration values a little more.
 */

#include "mbed.h"       // Last tested: v122
#include "RA8875.h"     // Last tested: v125

// Define this for 800x480 panel, undefine for 480x272
#define BIG_SCREEN

// Define this for Cap touch panel, undefine for resistive
#define CAP_TOUCH

#ifdef CAP_TOUCH
RA8875 myGLCD(p5, p6, p7, p12, NC, p9,p10,p13, "tft"); // SPI:{MOSI,MISO,SCK,/ChipSelect,/reset}, I2C:{SDA,SCL,/IRQ}, name
#else
RA8875 myGLCD(p5, p6, p7, p12, NC, "tft");             // SPI:{MOSI,MISO,SCK,/ChipSelect,/reset}, name
#endif

#define PC_BAUD 460800  // I like the serial communications to be very fast

// // // // // // // // // // // // // // // // // // // // // // // //
// End of Configuration Section
// // // // // // // // // // // // // // // // // // // // // // // //

LocalFileSystem local("local");                     // access to calibration file for resistive touch.
Serial pc(USBTX, USBRX);    // Not required for display

#ifdef BIG_SCREEN
#define LCD_W 800
#define LCD_H 480
#define LCD_C 8         // color - bits per pixel
#define DEF_RADIUS 50   // default radius of the fingerprint
#define BL_NORM 25      // Backlight Normal setting (0 to 255)
#else
#define LCD_W 480
#define LCD_H 272
#define LCD_C 8         // color - bits per pixel
#define DEF_RADIUS 20   // default radius of the fingerprint
#define BL_NORM 25      // Backlight Normal setting (0 to 255)
#endif


//  +----------------------------------------------------------------+------+--------+
//  |                                                                |      |        |
//  |                                                                |      |        |
//  |      (x,y)                                                     |Pillar|        |
//  |          +------+           +-----------------------+          +------+        |
//  |          | Bird h           | Tap To Start Easy     |       (x,y)     ^        |
//  |          +---w--+           +-----------------------+              window      |
//  |                                                                       h        |
//  |                             +-----------------------+                 v        |
//  |                             | Tap To Start Hard     |          +---w--+        |
//  |                             +-----------------------+          |      |        |
//  |                                                                |      |        |
//  |                                                                |      |        |
//  |                                                                |      |        |
//  |                                                                |      |        |
//  |                                                                |      |        |
//  |                                                                |      |        |
//  +----------------------------------------------------------------+------+--------+ Ground
//  | From HowToMechatronics.com      Adapted by Smartware Computing                 |
//  +--------------------------------------------------------------------------------+ ScoreZone
//  | S C O R E :  # # #              Highest Score: #####              [Reset]      |
//  |                                 Highest Score: #####                           |
//  +--------------------------------------------------------------------------------+
//                                    +
//                                  INFO_X
#define INFO_X (300)
#define GROUND_Y (LCD_H - 60)
#define SCOREZONE_Y (LCD_H - 40)

#define BIRD_W 35
#define BIRD_H 30
#define BIRD_X 50
#define BIRD_Y 50

#define TAP_EASY_Y  (GROUND_Y/2 - 64)
#define TAP_EASY_MSG "TAP TO START EASY GAME"      // 22 chars
#define TAP_HARD_Y  (GROUND_Y/2 - 0)
#define TAP_HARD_MSG "TAP TO START HARD GAME"      // 22 chars
#define TAP_LEN 22      // 22 chars
#define TAP_W  16*TAP_LEN
#define TAP_H  32
#define TAP_X  (LCD_W/2 - 16*TAP_LEN/2)  // text centered on screen

#define RESET_X (LCD_W - 2 - 160)
#define RESET_Y SCOREZONE_Y
#define RESET_W (160)
#define RESET_H (LCD_H - 1 - SCOREZONE_Y)

#define PILLAR_W 50
#define WINDOW_H_EASY 120
#define WINDOW_H_HARD 80

#define COLOR_SKY RGB(114, 198, 206)
#define COLOR_GROUND RGB(64,248,48)
#define COLOR_SCOREZONE RGB(221, 216, 148)
#define COLOR_PILLAR RGB(0, 200, 20)

const rect_t r_easy = rect_t (
    TAP_X,TAP_EASY_Y, TAP_X+TAP_W, TAP_EASY_Y+TAP_H
);
const rect_t r_hard = rect_t (
    TAP_X,TAP_HARD_Y, TAP_X+TAP_W, TAP_HARD_Y+TAP_H
);


const rect_t r_Reset = rect_t (
    RESET_X,RESET_Y, RESET_X+RESET_W,RESET_Y+RESET_H
);
const rect_t r_Ground = rect_t (
    1,GROUND_Y, LCD_W-4,SCOREZONE_Y-1
);
const rect_t r_Score = rect_t (
    1,SCOREZONE_Y, LCD_W-4,LCD_H-3
);
const rect_t r_PlayField = rect_t (
    0,0, LCD_W-1, GROUND_Y-1
);

rect_t r_Bird = rect_t (
    BIRD_X,BIRD_Y, BIRD_X+BIRD_W,BIRD_Y+BIRD_H
);
rect_t r_PillarUp = rect_t (
    0,0, 0+PILLAR_W,GROUND_Y/4
);
rect_t r_PillarDn = rect_t (
    0,GROUND_Y/4, 0+PILLAR_W,GROUND_Y-1
);

int movingRate = 3;
int fallRateInt = 0;
float fallRate = 0;
int score = 0;
int lastSpeedUpScore = 0;
#define SPEED_UP_EASY 5
#define SPEED_UP_HARD 2
int speed_up_threshold = SPEED_UP_EASY;
int highestScore;
bool screenPressed = false;
bool gameStarted = false;

Timer timer;
int lastPillarTime;
int pillarStepTime = 30;     // 50 mSec time between pillar motion to start.
int lastBirdTime;
int birdStepTime = 60;

int window_h = WINDOW_H_EASY;

void initiateGame();
void SetPillarX(int x);
void SetWindowY(int y);
void SetBirdY(int y);
void DrawPillars();
void DrawBird();
void gameOver();

void setup() {
    myGLCD.cls();
    timer.start();
    highestScore = 0; //EEPROM.read(0); // Read the highest score from the EEPROM
    initiateGame(); // Initiate the game
}

void loop() {
    int nowTime = timer.read_ms();
    
    if ((nowTime - lastPillarTime) > pillarStepTime) {
        lastPillarTime = nowTime;
        SetPillarX(r_PillarUp.p1.x - movingRate);
        DrawPillars();    // Draws the pillars
    }
    
    if ((nowTime - lastBirdTime) > birdStepTime) {
        lastBirdTime = nowTime;
        SetBirdY(r_Bird.p1.y + fallRateInt);
        DrawBird();       // Draws the bird
        fallRate = fallRate+0.4;  // Each inetration the fall rate increase so that we can the effect of acceleration/ gravity
        fallRateInt = int(fallRate);
    }
    // Checks for collision
    if (r_Bird.p2.y >= (GROUND_Y - 1 ) || r_Bird.p1.y <= 0) { // top and bottom
        gameOver();
    }
    if (myGLCD.Intersect(r_Bird, r_PillarUp) 
    ||  myGLCD.Intersect(r_Bird, r_PillarDn)) {
        gameOver();
    }

    // After the pillar has passed through the screen
    if (r_PillarUp.p2.x <= 0) {
        SetPillarX(LCD_W - 2);
        SetWindowY(rand() % (GROUND_Y - window_h));        // Random number for the pillars height
        score++;                                    // Increase score by one
    }
    //==== Controlling the bird
    point_t p;
    if (myGLCD.TouchPanelReadable(&p) && !screenPressed) {
        fallRate=-6; // Setting the fallRate negative will make the bird jump
        screenPressed = true;
    }
    // Doesn't allow holding the screen / you must tap it
    else if ( !myGLCD.TouchPanelReadable() && screenPressed) {
        screenPressed = false;
    }
    // After each few points, increases the moving rate of the pillars
    if ((score - lastSpeedUpScore) == speed_up_threshold) {
        lastSpeedUpScore = score;
        movingRate++;
    }
}


void DrawScore(void) {
    myGLCD.foreground(Black);
    myGLCD.background(COLOR_SCOREZONE);
    myGLCD.SetTextFontSize(2);
    myGLCD.SetTextCursor(5,SCOREZONE_Y);
    myGLCD.printf("Score: %5d", score);
}

void DrawHighScore(void) {
    myGLCD.SetTextFontSize(2);
    myGLCD.SetTextCursor(INFO_X,SCOREZONE_Y);
    myGLCD.printf("Highest: %5d", highestScore);
}

void ShowReset(bool showit) {
    myGLCD.background(COLOR_SCOREZONE);
    myGLCD.rect(r_Reset, COLOR_SCOREZONE);
    if (showit) {
        myGLCD.foreground(Black);
        myGLCD.SetTextCursor(RESET_X,RESET_Y);
        myGLCD.SetTextFontSize(2);
        myGLCD.printf(">RESET<");
    }
}

// ===== initiateGame - Custom Function
void initiateGame() {
    SetPillarX(LCD_W - PILLAR_W - 30);
    SetWindowY(rand() % (GROUND_Y - window_h));
    SetBirdY(BIRD_Y);
    fallRate = 0;
    score = 0;
    lastSpeedUpScore = 0;
    movingRate = 3;

    // Blue sky
    myGLCD.foreground(White);
    myGLCD.background(COLOR_SKY);
    myGLCD.cls();

    // Ground
    myGLCD.fillrect(r_Ground, COLOR_GROUND);
    myGLCD.foreground(Black);
    myGLCD.background(COLOR_GROUND);
    myGLCD.SetTextFontSize(1);
    myGLCD.SetTextCursor(5,GROUND_Y);
    myGLCD.printf("From www.HowToMechatronics.com");
    myGLCD.SetTextCursor(INFO_X,GROUND_Y);
    myGLCD.printf("Adapted for RA8875 and mbed by Smartware Computing");

    // ScoreZone
    myGLCD.fillrect(r_Score, COLOR_SCOREZONE);
    DrawScore();
    DrawHighScore();
    ShowReset(true);

    // Start buttons
    myGLCD.SetTextCursor(r_easy.p1);
    myGLCD.SetTextFontSize(2);
    myGLCD.foreground(Black);
    myGLCD.printf(TAP_EASY_MSG);
    myGLCD.rect(r_easy, BrightRed);

    myGLCD.SetTextCursor(r_hard.p1);
    myGLCD.SetTextFontSize(2);
    myGLCD.foreground(Black);
    myGLCD.printf(TAP_HARD_MSG);
    myGLCD.rect(r_hard, BrightRed);

    wait(0.5);
    DrawBird(); // Draws the bird
    DrawPillars();

    // Wait until we tap the sreen
    while (!gameStarted) {
        point_t p;
        if (myGLCD.TouchPanelReadable(&p)) {
            if (myGLCD.Intersect(r_Reset, p)) {
                highestScore = 0;
                myGLCD.fillrect(r_Reset, COLOR_SCOREZONE);
                DrawHighScore();
            }
            if (myGLCD.Intersect(r_easy,p)) {
                movingRate = 3;
                window_h = WINDOW_H_EASY;
                speed_up_threshold = SPEED_UP_EASY;
                gameStarted = true;
            }
            if (myGLCD.Intersect(r_hard,p)) {
                movingRate = 8;
                window_h = WINDOW_H_HARD;
                speed_up_threshold = SPEED_UP_HARD;
                gameStarted = true;
            }
        }
    }
    ShowReset(false);
    myGLCD.fillrect(r_easy, COLOR_SKY);
    myGLCD.fillrect(r_hard, COLOR_SKY);
    wait(0.5);
    lastBirdTime = lastPillarTime = timer.read_ms();
}


// ===== DrawPillars - Custom Function
// (x,y) is the bottom left of the top pillar
//
// Set the left side of the pillar
void SetPillarX(int x) {
    r_PillarUp.p1.x = r_PillarDn.p1.x = x;
    r_PillarUp.p2.x = r_PillarDn.p2.x = x + PILLAR_W;
}

void SetWindowY(int y) {
    r_PillarUp.p2.y = y;
    r_PillarDn.p1.y = y + window_h;
}

void DrawPillars() {
    rect_t r;
    
    for (int i=0; i<2; i++) {
        r = (i == 0) ? r_PillarUp : r_PillarDn;

        myGLCD.Intersect(&r, &r_PlayField);     // Create a new rect that fits on the screen
        myGLCD.fillrect(r, COLOR_PILLAR);
        myGLCD.rect(r, Black);
        r.p1.x = r.p2.x + 1;                    // Sky-fill to the right of the pillar
        r.p2.x += movingRate;
        if (myGLCD.Intersect(&r, &r_PlayField)) {
            myGLCD.fillrect(r, COLOR_SKY);
        }
    }
    DrawScore();
}

void SetBirdY(int y) {
    r_Bird.p1.y = y;
    if (r_Bird.p1.y + BIRD_H >= GROUND_Y)
        r_Bird.p1.y = GROUND_Y - BIRD_H - 1;
    r_Bird.p2.y = y + BIRD_H;
}

void DrawBird() {
    #if 1
    point_t p;
    rect_t r;
    
    r = r_Bird;
    // Clear space above or below
    if (fallRateInt > 0) {                  // Falling
        r.p1.y -= fallRateInt;
        if (r.p1.y < 0)
            r.p1.y = 0;
    } else if (fallRateInt < 0) {           // Rising
        r.p2.y += -fallRateInt;
        if (r.p2.y >= GROUND_Y)
            r.p2.y = GROUND_Y - 1;        
    }
    myGLCD.fillrect(r, COLOR_SKY);     // erase old bird

    // center body
    p.x = r_Bird.p1.x + BIRD_W/2;
    p.y = r_Bird.p1.y + BIRD_H/2;
    myGLCD.fillcircle(p, 7*BIRD_H/16, (color_t)(RGB(255,255,0)));

    p.x = r_Bird.p1.x + 3*BIRD_W/4;
    p.y = r_Bird.p1.y + 1*BIRD_H/4;
    myGLCD.fillcircle(p, BIRD_H/5, White);
    myGLCD.circle(p, BIRD_H/5, Black);
    myGLCD.fillcircle(p, BIRD_H/8, Black);
    // Beak
    r.p1.x = r_Bird.p1.x + 9*BIRD_W/16;
    r.p1.y = r_Bird.p1.y + BIRD_H/2;
    r.p2.x = r_Bird.p1.x + BIRD_W;
    r.p2.y = r.p1.y + BIRD_H/5;
    myGLCD.fillroundrect(r, 3, 3, BrightRed);
    myGLCD.roundrect(r, 3, 3, Black);
    // Wing up/down
    if (fallRate > 0) {
        r.p1.y -= BIRD_H/6;
        r.p2.y = r.p1.y + BIRD_H/5;
    } else if (fallRate < 0) {
        r.p1.y += BIRD_H/6;
        r.p2.y = r.p1.y + BIRD_H/5;
    }
    r.p1.x = r_Bird.p1.x;
    r.p2.x = r.p1.x + 3*BIRD_W/8;
    myGLCD.fillroundrect(r, 3, 3, White);
    myGLCD.roundrect(r, 3, 3, Black);
    #else
    //printf("DrawBird(%d)\r\n", y);
    RetCode_t r = myGLCD.RenderImageFile(BIRD_X, y, "/local/BIRD_01.jpg");    // 35x30
    myGLCD.fillrect(BIRD_X,y,BIRD_X+BIRD_W,y-fallRateInt, COLOR_SKY);        // Draws blue rectangles above and below the bird 
    myGLCD.fillrect(BIRD_X,y+BIRD_H,BIRD_X+BIRD_W,y+BIRD_H+fallRateInt, COLOR_SKY);    // in order to clear its previous state
    #endif
}

//======== gameOver() - Custom Function
void gameOver() {
    wait(3.000);
    // Clears the screen and prints the text
    //myGLCD.cls();
    myGLCD.foreground(RGB(255, 255, 255));
    myGLCD.background(RGB(0, 0, 0));
    myGLCD.SetTextFontSize(2);
    myGLCD.SetTextCursor(INFO_X, 40);
    myGLCD.printf("GAME OVER");
    DrawScore();
    myGLCD.SetTextCursor(INFO_X,120);
    myGLCD.printf("Restarting...");
    myGLCD.SetTextFontSize(2);
    for (int i=5; i>0; i--) {
        myGLCD.SetTextCursor(INFO_X,150);
        myGLCD.printf("%d", i);
        wait(1.000);
    }

    // Writes the highest score in the EEPROM
    if (score > highestScore) {
        highestScore = score;
        //EEPROM.write(0,highestScore);
    }
    // Resets the variables to start position values
    gameStarted = false;
    initiateGame();
}


int main(void)
{
    pc.baud(PC_BAUD);    //I like a snappy terminal, so crank it up!
    pc.printf("\r\nFlappy Bird - Build " __DATE__ " " __TIME__ "\r\n");
    myGLCD.init((LCD_W-2),LCD_H,LCD_C);
    myGLCD.TouchPanelInit();
    myGLCD.Backlight_u8(BL_NORM);
    setup();
    
    while(1) {
        loop();
    }
}