#include "SevSeg.h"
#include "ssd1306.h"
#include "nano_engine.h"
const int BUTTON = 13;
const int PLAYER_POS = 15;
const int PLAYER_SIZE = 4;
const int PIPE_WIDTH = 10;
const int PIPE_START = 60;
const int PIPE_GAP = 20;
const int SLOT_MIN_GAP = PLAYER_SIZE * 8;
const int SLOT_MAX_GAP = SLOT_MIN_GAP + 10;
const int OFFSET_MIN = 10;
const int OFFSET_MAX = 30;
const float PIPE_SPEED = 0.6;
const float PIPE_SPEEDUP = 0.0007;
const float GRAVITY = 0.4;
const float JUMP_FORCE = 2.8;
const float TERMINAL_VELOCITY = 3.6;
uint8_t buffer[128 * 64 / 8];
NanoCanvas1 canvas(128, 64, buffer);
SevSeg seg;
struct Pipe {
int top, bottom;
float xpos;
};
float vy = 0, y;
float pv = PIPE_SPEED;
int w, h;
int pipeCount;
int score = 0;
bool playing = true;
bool insidePipe = false;
Pipe* pipes;
int getRandomSlotGap() {
return random(SLOT_MIN_GAP, SLOT_MAX_GAP + 1);
}
int getRandomOffset() {
return random(OFFSET_MIN, OFFSET_MAX + 1);
}
Pipe newRandomPipe(int xpos) {
int gap = getRandomSlotGap();
int top = getRandomOffset();
int bottom = top + gap;
return Pipe {
.top = top,
.bottom = bottom,
.xpos = xpos
};
}
void setupScreen() {
ssd1306_128x64_i2c_init();
ssd1306_clearScreen();
ssd1306_setFixedFont(ssd1306xled_font6x8);
w = ssd1306_displayWidth();
h = ssd1306_displayHeight();
y = h / 2;
}
void setupSevSeg() {
byte digitPins[] = {2, 3, 4, 5};
byte segmentPins[] = {6, 7, 8, 9, 10, 11, 12, 13};
seg.begin(COMMON_ANODE, 4, digitPins, segmentPins, false, false, false, true);
seg.setBrightness(90);
}
void initPipes() {
for (int i = 0; i < pipeCount; i++) {
pipes[i] = newRandomPipe(i * (PIPE_WIDTH + PIPE_GAP) + PIPE_START);
}
}
void setupPipes() {
pipeCount = w / (PIPE_WIDTH + PIPE_GAP) + 2;
pipes = malloc(pipeCount * sizeof(Pipe));
initPipes();
}
void setup() {
randomSeed(analogRead(0));
pinMode(BUTTON, INPUT_PULLUP);
setupScreen();
setupSevSeg();
setupPipes();
}
bool isButtonPressed() {
static bool pressedLastFrame = false;
bool pressed = digitalRead(BUTTON) == LOW;
if (!pressed)
return pressedLastFrame = false;
if (pressedLastFrame)
return false;
return pressedLastFrame = true;
}
void updatePlayer() {
if (y <= PLAYER_SIZE) {
y = PLAYER_SIZE + 1;
vy = 0;
} else if (isButtonPressed()) {
vy = -JUMP_FORCE;
y += vy;
} else if (y >= h - PLAYER_SIZE - 1) {
vy = 0;
y = h - PLAYER_SIZE - 1;
} else {
y += vy;
vy += GRAVITY;
vy = min(vy, TERMINAL_VELOCITY);
}
}
void updatePipes() {
bool needsShifting = false;
for (int i = 0; i < pipeCount; i++) {
pipes[i].xpos -= pv;
if (pipes[i].xpos < -PIPE_WIDTH)
needsShifting = true;
}
if (!needsShifting)
return;
for (int i = 1; i < pipeCount; i++)
pipes[i - 1] = pipes[i];
pipes[pipeCount - 1] = newRandomPipe(pipes[pipeCount - 2].xpos + PIPE_WIDTH + PIPE_GAP);
}
bool isInsidePipe() {
for (int i = 0; i < pipeCount; i++) {
int px = pipes[i].xpos;
if (px + PIPE_WIDTH >= PLAYER_POS && px <= PLAYER_POS + PLAYER_SIZE)
return true;
}
return false;
}
void updateScore() {
bool prev = insidePipe;
bool curr = insidePipe = isInsidePipe();
if (prev && !curr)
score++;
}
bool isColliding() {
for (int i = 0; insidePipe && i < pipeCount; i++) {
auto pipe = pipes[i];
float px = pipe.xpos;
if (px + PIPE_WIDTH < PLAYER_POS || px > PLAYER_POS + PLAYER_SIZE)
continue;
int py1 = pipe.top;
int py2 = pipe.bottom;
if (y <= py1 || y + PLAYER_SIZE >= py2) {
return true;
}
}
return false;
}
void drawScore() {
char txt[8];
sprintf(txt, "%d", score);
canvas.printFixed(0, 0, txt);
}
void drawPipe(Pipe pipe) {
if (pipe.xpos <= -PIPE_WIDTH || pipe.xpos >= w)
return;
int startx = pipe.xpos;
int endx = startx + PIPE_WIDTH;
canvas.fillRect(startx, 0, endx, pipe.top);
canvas.fillRect(startx, pipe.bottom, endx, h);
}
void drawPipes() {
for (int i = 0; i < pipeCount; i++) {
drawPipe(pipes[i]);
}
}
int frames = 0;
long long lastms = 0;
int fps = 0;
void loop() {
if (playing) {
pv += PIPE_SPEEDUP;
seg.blank();
updatePlayer();
updatePipes();
updateScore();
canvas.clear();
drawPipes();
drawScore();
canvas.fillRect(PLAYER_POS, y, PLAYER_POS + PLAYER_SIZE, y + PLAYER_SIZE);
canvas.fillRect(0, h-1, w, h-1);
if (isColliding() || y + PLAYER_SIZE + 1 >= h) {
playing = false;
canvas.printFixed(55, 28, "END");
}
long long ms = millis() / 1000;
frames++;
if (lastms != ms) {
fps = frames;
frames = 0;
}
char fpsstr[12];
sprintf(fpsstr, "FPS %d", fps);
canvas.printFixed(92, 0, fpsstr);
lastms = ms;
canvas.blt(0,0);
} else {
seg.setNumber(score, 0);
seg.refreshDisplay();
}
}