intro flappy
This commit is contained in:
@@ -14,6 +14,7 @@ board = esp32doit-devkit-v1
|
||||
framework = arduino
|
||||
monitor_speed = 115200
|
||||
upload_protocol = esptool
|
||||
#monitor_port = /dev/cu.usbserial-2120
|
||||
#upload_flags = -b 115200
|
||||
|
||||
monitor_flags =
|
||||
@@ -46,6 +47,7 @@ build_flags =
|
||||
-DLOAD_GLCD=1
|
||||
-DLOAD_GFXFF=1
|
||||
-DSPI_FREQUENCY=40000000
|
||||
-DDISABLE_ALL_LIBRARY_WARNINGS=1
|
||||
|
||||
[env:mainOTA]
|
||||
extends = env:main
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <json11.hpp>
|
||||
|
||||
#include "gif_player.h"
|
||||
#include "flappy.h"
|
||||
|
||||
using namespace json11;
|
||||
|
||||
@@ -311,7 +312,12 @@ void DisplayTask::run() {
|
||||
tft_.fillScreen(TFT_BLACK);
|
||||
tft_.setTextSize(2);
|
||||
tft_.setTextDatum(TC_DATUM);
|
||||
if (isChristmas()){
|
||||
tft_.drawString("Merry Christmas!", center, 10);
|
||||
}
|
||||
else {
|
||||
tft_.drawString("Credits", center, 10);
|
||||
}
|
||||
tft_.setTextSize(1);
|
||||
tft_.drawString("Designed and handmade", center, 50);
|
||||
tft_.drawString("by Scott Bezek", center, 60);
|
||||
@@ -364,6 +370,28 @@ void DisplayTask::run() {
|
||||
tft_.fillScreen(TFT_BLACK);
|
||||
delay(200);
|
||||
}
|
||||
if (left_button){
|
||||
main_task_.setOtaEnabled(false);
|
||||
state = State::PLAY_FLAPPY;
|
||||
tft_.fillScreen(TFT_BLACK);
|
||||
tft_.setTextSize(2);
|
||||
tft_.setTextDatum(TC_DATUM);
|
||||
tft_.drawString("Lets Flap!", tft_.width()/2, 10);
|
||||
tft_.setTextSize(1);
|
||||
delay(200);
|
||||
|
||||
}
|
||||
break;
|
||||
case State::PLAY_FLAPPY:
|
||||
if (right_button) {
|
||||
// Exit credits
|
||||
main_task_.setOtaEnabled(false);
|
||||
state = State::CHOOSE_GIF;
|
||||
tft_.fillScreen(TFT_BLACK);
|
||||
delay(200);
|
||||
return;
|
||||
}
|
||||
game_loop(left_button);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ enum class State {
|
||||
CHOOSE_GIF,
|
||||
PLAY_GIF,
|
||||
SHOW_CREDITS,
|
||||
PLAY_FLAPPY,
|
||||
};
|
||||
|
||||
class DisplayTask : public Task<DisplayTask>, public Logger {
|
||||
|
||||
351
Firmware/src/flappy.cpp
Normal file
351
Firmware/src/flappy.cpp
Normal file
@@ -0,0 +1,351 @@
|
||||
#include "flappy.h"
|
||||
|
||||
// By Ponticelli Domenico.
|
||||
// 12NOV2020 EEPROM Working now, Modified by Zontex
|
||||
// https://github.com/pcelli85/M5Stack_FlappyBird_game
|
||||
|
||||
// #include <EEPROM.h>
|
||||
// #include <M5StickC.h>
|
||||
TFT_eSPI tft_;
|
||||
int address = 0;
|
||||
int maxScore = 0; // EEPROM.readInt(address);
|
||||
const int buttonPin = 2;
|
||||
// background
|
||||
const unsigned int BCKGRDCOL = tft_.color565(138, 235, 244);
|
||||
// bird
|
||||
const unsigned int BIRDCOL = tft_.color565(255, 254, 174);
|
||||
// pipe
|
||||
const unsigned int PIPECOL = tft_.color565(99, 255, 78);
|
||||
// pipe highlight
|
||||
const unsigned int PIPEHIGHCOL = tft_.color565(250, 255, 250);
|
||||
// pipe seam
|
||||
const unsigned int PIPESEAMCOL = tft_.color565(0, 0, 0);
|
||||
// floor
|
||||
const unsigned int FLOORCOL = tft_.color565(246, 240, 163);
|
||||
// grass (col2 is the stripe color)
|
||||
const unsigned int GRASSCOL = tft_.color565(141, 225, 87);
|
||||
const unsigned int GRASSCOL2 = tft_.color565(156, 239, 88);
|
||||
|
||||
// bird sprite
|
||||
// bird sprite colors (Cx name for values to keep the array readable)
|
||||
#define C0 BCKGRDCOL
|
||||
#define C1 tft_.color565(195, 165, 75)
|
||||
#define C2 BIRDCOL
|
||||
#define C3 TFT_WHITE
|
||||
#define C4 TFT_RED
|
||||
#define C5 tft_.color565(251, 216, 114)
|
||||
|
||||
static unsigned int birdcol[] = {
|
||||
C0, C0, C1, C1, C1, C1, C1, C0, C0, C0, C1, C1, C1, C1, C1, C0, C0, C1, C2,
|
||||
C2, C2, C1, C3, C1, C0, C1, C2, C2, C2, C1, C3, C1, C0, C2, C2, C2, C2, C1,
|
||||
C3, C1, C0, C2, C2, C2, C2, C1, C3, C1, C1, C1, C1, C2, C2, C3, C1, C1, C1,
|
||||
C1, C1, C2, C2, C3, C1, C1, C1, C2, C2, C2, C2, C2, C4, C4, C1, C2, C2, C2,
|
||||
C2, C2, C4, C4, C1, C2, C2, C2, C1, C5, C4, C0, C1, C2, C2, C2, C1, C5, C4,
|
||||
C0, C0, C1, C2, C1, C5, C5, C5, C0, C0, C1, C2, C1, C5, C5, C5, C0, C0, C0,
|
||||
C1, C5, C5, C5, C0, C0, C0, C0, C1, C5, C5, C5, C0, C0};
|
||||
|
||||
// bird structure
|
||||
static struct BIRD
|
||||
{
|
||||
long x, y, old_y;
|
||||
long col;
|
||||
float vel_y;
|
||||
} bird;
|
||||
|
||||
// pipe structure
|
||||
static struct PIPES
|
||||
{
|
||||
long x, gap_y;
|
||||
long col;
|
||||
} pipes;
|
||||
|
||||
// score
|
||||
int score;
|
||||
// temporary x and y var
|
||||
static short tmpx, tmpy;
|
||||
|
||||
// ---------------
|
||||
// draw pixel
|
||||
// ---------------
|
||||
// faster drawPixel method by inlining calls and using setAddrWindow and
|
||||
// pushColor using macro to force inlining
|
||||
#define drawPixel(a, b, c) \
|
||||
tft_.setAddrWindow(a, b, a, b); \
|
||||
tft_.pushColor(c)
|
||||
// ---------------
|
||||
// game loop
|
||||
// ---------------
|
||||
double delta, old_time, next_game_tick, current_time;
|
||||
FlpState state;
|
||||
unsigned char GAMEH = TFTH - FLOORH;
|
||||
long grassx = TFTW;
|
||||
int loops;
|
||||
// passed pipe flag to count score
|
||||
bool passed_pipe = false;
|
||||
// temp var for setAddrWindow
|
||||
unsigned char px;
|
||||
|
||||
void game_loop(bool button)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case FlpState::START_FLAPPY:
|
||||
{
|
||||
tft_.fillScreen(TFT_BLACK);
|
||||
tft_.fillRect(0, TFTH2 - 10, TFTW, 1, TFT_WHITE);
|
||||
tft_.fillRect(0, TFTH2 + 15, TFTW, 1, TFT_WHITE);
|
||||
tft_.setTextColor(TFT_WHITE);
|
||||
tft_.setTextSize(1);
|
||||
// half width - num char * char width in pixels
|
||||
tft_.setCursor(TFTW2 - 15, TFTH2 - 6);
|
||||
tft_.println("FLAPPY");
|
||||
tft_.setTextSize(1);
|
||||
tft_.setCursor(TFTW2 - 15, TFTH2 + 6);
|
||||
tft_.println("-BIRD-");
|
||||
tft_.setTextSize(1);
|
||||
tft_.setCursor(TFTW2 - 40, TFTH2 + 21);
|
||||
tft_.println("please press home");
|
||||
if (button)
|
||||
{
|
||||
state = FlpState::INIT_FLAPPY;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case FlpState::INIT_FLAPPY:
|
||||
{
|
||||
// clear screen
|
||||
tft_.fillScreen(BCKGRDCOL);
|
||||
// reset score
|
||||
score = 0;
|
||||
// init bird
|
||||
bird.x = 30;
|
||||
bird.y = bird.old_y = TFTH2 - BIRDH;
|
||||
bird.vel_y = -JUMP_FORCE;
|
||||
tmpx = tmpy = 0;
|
||||
// generate new random seed for the pipe gape
|
||||
randomSeed(analogRead(0));
|
||||
// init pipe
|
||||
pipes.x = 0;
|
||||
pipes.gap_y = random(20, TFTH - 60);
|
||||
// ===============
|
||||
// prepare game variables
|
||||
// draw floor
|
||||
// ===============
|
||||
// instead of calculating the distance of the floor from the screen height
|
||||
// each time store it in a variable
|
||||
|
||||
// draw the floor once, we will not overwrite on this area in-game
|
||||
// black line
|
||||
tft_.drawFastHLine(0, GAMEH, TFTW, TFT_BLACK);
|
||||
// grass and stripe
|
||||
tft_.fillRect(0, GAMEH + 1, TFTW2, GRASSH, GRASSCOL);
|
||||
tft_.fillRect(TFTW2, GAMEH + 1, TFTW2, GRASSH, GRASSCOL2);
|
||||
// black line
|
||||
tft_.drawFastHLine(0, GAMEH + GRASSH, TFTW, TFT_BLACK);
|
||||
// mud
|
||||
tft_.fillRect(0, GAMEH + GRASSH + 1, TFTW, FLOORH - GRASSH, FLOORCOL);
|
||||
// grass x position (for stripe animation)
|
||||
// game loop time variables
|
||||
|
||||
next_game_tick = current_time = millis();
|
||||
|
||||
loops = 0;
|
||||
state = FlpState::RUN_FLAPPY;
|
||||
}
|
||||
break;
|
||||
|
||||
case FlpState::RUN_FLAPPY:
|
||||
{
|
||||
if (millis() > next_game_tick && loops < MAX_FRAMESKIP)
|
||||
{
|
||||
if (button)
|
||||
{
|
||||
// while(digitalRead(M5_BUTTON_HOME) == LOW);
|
||||
if (bird.y > BIRDH2 * 0.5)
|
||||
bird.vel_y = -JUMP_FORCE;
|
||||
// else zero velocity
|
||||
else
|
||||
bird.vel_y = 0;
|
||||
}
|
||||
|
||||
// ===============
|
||||
// update
|
||||
// ===============
|
||||
// calculate delta time
|
||||
// ---------------
|
||||
old_time = current_time;
|
||||
current_time = millis();
|
||||
delta = (current_time - old_time) / 1000;
|
||||
|
||||
// bird
|
||||
// ---------------
|
||||
bird.vel_y += GRAVITY * delta;
|
||||
bird.y += bird.vel_y;
|
||||
|
||||
// pipe
|
||||
// ---------------
|
||||
|
||||
pipes.x -= SPEED;
|
||||
// if pipe reached edge of the screen reset its position and gap
|
||||
if (pipes.x < -PIPEW)
|
||||
{
|
||||
pipes.x = TFTW;
|
||||
pipes.gap_y = random(10, GAMEH - (10 + GAPHEIGHT));
|
||||
}
|
||||
|
||||
// ---------------
|
||||
next_game_tick += SKIP_TICKS;
|
||||
loops++;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// ===============
|
||||
// draw
|
||||
// ===============
|
||||
// pipe
|
||||
// ---------------
|
||||
// we save cycles if we avoid drawing the pipe when outside the screen
|
||||
|
||||
if (pipes.x >= 0 && pipes.x < TFTW)
|
||||
{
|
||||
// pipe color
|
||||
tft_.drawFastVLine(pipes.x + 3, 0, pipes.gap_y, PIPECOL);
|
||||
tft_.drawFastVLine(pipes.x + 3, pipes.gap_y + GAPHEIGHT + 1,
|
||||
GAMEH - (pipes.gap_y + GAPHEIGHT + 1),
|
||||
PIPECOL);
|
||||
// highlight
|
||||
tft_.drawFastVLine(pipes.x, 0, pipes.gap_y, PIPEHIGHCOL);
|
||||
tft_.drawFastVLine(pipes.x, pipes.gap_y + GAPHEIGHT + 1,
|
||||
GAMEH - (pipes.gap_y + GAPHEIGHT + 1),
|
||||
PIPEHIGHCOL);
|
||||
// bottom and top border of pipe
|
||||
drawPixel(pipes.x, pipes.gap_y, PIPESEAMCOL);
|
||||
drawPixel(pipes.x, pipes.gap_y + GAPHEIGHT, PIPESEAMCOL);
|
||||
// pipe seam
|
||||
drawPixel(pipes.x, pipes.gap_y - 6, PIPESEAMCOL);
|
||||
drawPixel(pipes.x, pipes.gap_y + GAPHEIGHT + 6, PIPESEAMCOL);
|
||||
drawPixel(pipes.x + 3, pipes.gap_y - 6, PIPESEAMCOL);
|
||||
drawPixel(pipes.x + 3, pipes.gap_y + GAPHEIGHT + 6, PIPESEAMCOL);
|
||||
}
|
||||
#if 1
|
||||
// erase behind pipe
|
||||
if (pipes.x <= TFTW)
|
||||
tft_.drawFastVLine(pipes.x + PIPEW, 0, GAMEH, BCKGRDCOL);
|
||||
// tft_.drawFastVLine(pipes.x, 0, GAMEH, BCKGRDCOL);
|
||||
// PIPECOL
|
||||
#endif
|
||||
// bird
|
||||
// ---------------
|
||||
tmpx = BIRDW - 1;
|
||||
do
|
||||
{
|
||||
px = bird.x + tmpx + BIRDW;
|
||||
// clear bird at previous position stored in old_y
|
||||
// we can't just erase the pixels before and after current position
|
||||
// because of the non-linear bird movement (it would leave 'dirty'
|
||||
// pixels)
|
||||
tmpy = BIRDH - 1;
|
||||
do
|
||||
{
|
||||
drawPixel(px, bird.old_y + tmpy, BCKGRDCOL);
|
||||
} while (tmpy--);
|
||||
// draw bird sprite at new position
|
||||
tmpy = BIRDH - 1;
|
||||
do
|
||||
{
|
||||
drawPixel(px, bird.y + tmpy, birdcol[tmpx + (tmpy * BIRDW)]);
|
||||
} while (tmpy--);
|
||||
} while (tmpx--);
|
||||
// save position to erase bird on next draw
|
||||
bird.old_y = bird.y;
|
||||
|
||||
// grass stripes
|
||||
// ---------------
|
||||
grassx -= SPEED;
|
||||
if (grassx < 0)
|
||||
grassx = TFTW;
|
||||
tft_.drawFastVLine(grassx % TFTW, GAMEH + 1, GRASSH - 1, GRASSCOL);
|
||||
tft_.drawFastVLine((grassx + 64) % TFTW, GAMEH + 1, GRASSH - 1,
|
||||
GRASSCOL2);
|
||||
|
||||
// ===============
|
||||
// collision
|
||||
// ===============
|
||||
// if the bird hit the ground game over
|
||||
if (bird.y > GAMEH - BIRDH)
|
||||
break;
|
||||
// checking for bird collision with pipe
|
||||
if (bird.x + BIRDW >= pipes.x - BIRDW2 &&
|
||||
bird.x <= pipes.x + PIPEW - BIRDW)
|
||||
{
|
||||
// bird entered a pipe, check for collision
|
||||
if (bird.y < pipes.gap_y ||
|
||||
bird.y + BIRDH > pipes.gap_y + GAPHEIGHT)
|
||||
break;
|
||||
else
|
||||
passed_pipe = true;
|
||||
}
|
||||
// if bird has passed the pipe increase score
|
||||
else if (bird.x > pipes.x + PIPEW - BIRDW && passed_pipe)
|
||||
{
|
||||
passed_pipe = false;
|
||||
// erase score with background color
|
||||
tft_.setTextColor(BCKGRDCOL);
|
||||
tft_.setCursor(TFTW2, 4);
|
||||
tft_.print(score);
|
||||
// set text color back to white for new score
|
||||
tft_.setTextColor(TFT_WHITE);
|
||||
// increase score since we successfully passed a pipe
|
||||
score++;
|
||||
}
|
||||
|
||||
// update score
|
||||
// ---------------
|
||||
tft_.setCursor(2, 4);
|
||||
tft_.print(score);
|
||||
delay(2000);
|
||||
state = FlpState::GAME_OVER;
|
||||
}
|
||||
break;
|
||||
|
||||
case FlpState::GAME_OVER:
|
||||
{
|
||||
tft_.fillScreen(TFT_BLACK);
|
||||
// maxScore = EEPROM.readInt(address);
|
||||
|
||||
if (score > maxScore)
|
||||
{
|
||||
// EEPROM.writeInt(address, score);
|
||||
// EEPROM.commit();
|
||||
maxScore = score;
|
||||
tft_.setTextColor(TFT_RED);
|
||||
tft_.setTextSize(1);
|
||||
tft_.setCursor(0, TFTH2 - 16);
|
||||
tft_.println("NEW HIGHSCORE");
|
||||
}
|
||||
|
||||
tft_.setTextColor(TFT_WHITE);
|
||||
tft_.setTextSize(1);
|
||||
// half width - num char * char width in pixels
|
||||
tft_.setCursor(TFTW2 - 25, TFTH2 - 6);
|
||||
tft_.println("GAME OVER");
|
||||
tft_.setTextSize(1);
|
||||
tft_.setCursor(1, 10);
|
||||
tft_.print("score: ");
|
||||
tft_.print(score);
|
||||
tft_.setCursor(5, TFTH2 + 6);
|
||||
tft_.println("press button");
|
||||
tft_.setCursor(1, 21);
|
||||
tft_.print("Max Score:");
|
||||
tft_.print(maxScore);
|
||||
if (button)
|
||||
{
|
||||
state = FlpState::INIT_FLAPPY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
39
Firmware/src/flappy.h
Normal file
39
Firmware/src/flappy.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <TFT_eSPI.h>
|
||||
|
||||
|
||||
#define TFTW 240 // screen width
|
||||
#define TFTH 135 // screen height
|
||||
#define TFTW2 120 // half screen width
|
||||
#define TFTH2 67 // half screen height
|
||||
// game constant
|
||||
#define SPEED 1
|
||||
#define GRAVITY 9.8
|
||||
#define JUMP_FORCE 2.15
|
||||
#define SKIP_TICKS 20.0 // 1000 / 50fps
|
||||
#define MAX_FRAMESKIP 5
|
||||
// bird size
|
||||
#define BIRDW 8 // bird width
|
||||
#define BIRDH 8 // bird height
|
||||
#define BIRDW2 4 // half width
|
||||
#define BIRDH2 4 // half height
|
||||
// pipe size
|
||||
#define PIPEW 15 // pipe width
|
||||
#define GAPHEIGHT 30 // pipe gap height
|
||||
// floor size
|
||||
#define FLOORH 20 // floor height (from bottom of the screen)
|
||||
// grass size
|
||||
#define GRASSH 4 // grass height (inside floor, starts at floor y)
|
||||
|
||||
|
||||
|
||||
|
||||
enum class FlpState {
|
||||
START_FLAPPY,
|
||||
INIT_FLAPPY,
|
||||
RUN_FLAPPY,
|
||||
GAME_OVER,
|
||||
};
|
||||
|
||||
void game_loop(bool button);
|
||||
Reference in New Issue
Block a user