#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH110X.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SH1107 display = Adafruit_SH1107(64, 128, &Wire);
#define JUMP_PIN 7
#define BUZZER_PIN 21
#define BUZZER_CHANNEL 0
int attempt = 1;
bool gameStarted = false;
int cubeY = 40;
int cubeVel = 0;
bool jumping = false;
struct Spike {
int x;
bool active;
};
#define MAX_SPIKES 6
Spike spikes[MAX_SPIKES];
// ----------------------
// MUSIC (original tune)
// ----------------------
int melody[] = {
523, 659, 784, 659, 523, 392, 523, 659,
784, 880, 784, 659, 523, 659, 784, 659,
523, 392, 330, 392, 523, 659, 523, 392
};
int noteDurations[] = {
150,150,150,150,150,150,150,150,
150,200,150,150,150,150,150,150,
150,150,150,150,150,200,150,200
};
int melodyLength = sizeof(melody) / sizeof(melody[0]);
void playSong() {
for (int i = 0; i < melodyLength; i++) {
ledcWriteTone(BUZZER_CHANNEL, melody[i]);
delay(noteDurations[i]);
ledcWriteTone(BUZZER_CHANNEL, 0);
delay(20);
}
}
// ----------------------
// Attempt Screen
// ----------------------
void showAttemptScreen() {
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(SH110X_WHITE);
display.setCursor(10, 20);
display.print("Attempt");
display.setCursor(30, 40);
display.print(attempt);
display.display();
}
// ----------------------
// Game Start
// ----------------------
void startGame() {
showAttemptScreen();
playSong();
gameStarted = true;
cubeY = 40;
cubeVel = 0;
jumping = false;
for (int i = 0; i < MAX_SPIKES; i++) {
spikes[i].active = false;
}
}
// ----------------------
// Spike Logic
// ----------------------
void spawnSpike() {
for (int i = 0; i < MAX_SPIKES; i++) {
if (!spikes[i].active) {
spikes[i].x = SCREEN_WIDTH + random(20, 80);
spikes[i].active = true;
return;
}
}
}
void updateSpikes() {
for (int i = 0; i < MAX_SPIKES; i++) {
if (spikes[i].active) {
spikes[i].x -= 3;
if (spikes[i].x < -10) {
spikes[i].active = false;
}
}
}
if (random(100) < 5) {
spawnSpike();
}
}
void drawSpikes() {
for (int i = 0; i < MAX_SPIKES; i++) {
if (spikes[i].active) {
int x = spikes[i].x;
display.fillTriangle(x, 40, x + 8, 40, x + 4, 28, SH110X_WHITE);
}
}
}
// ----------------------
// Cube Jump Logic
// ----------------------
void updateCube() {
bool buttonHeld = (digitalRead(JUMP_PIN) == LOW);
if (buttonHeld && cubeY >= 40 && !jumping) {
jumping = true;
cubeVel = -6;
ledcWriteTone(BUZZER_CHANNEL, 880);
}
if (jumping) {
cubeY += cubeVel;
cubeVel += 1;
if (cubeY >= 40) {
cubeY = 40;
jumping = false;
ledcWriteTone(BUZZER_CHANNEL, 0);
}
}
}
// ----------------------
// Collision Detection
// ----------------------
bool checkCollision() {
for (int i = 0; i < MAX_SPIKES; i++) {
if (!spikes[i].active) continue;
int sx = spikes[i].x;
int sy = 28;
if (sx < 28 && sx + 8 > 20) {
if (cubeY >= sy - 8) {
return true;
}
}
}
return false;
}
// ----------------------
// Setup
// ----------------------
void setup() {
Wire.begin();
display.begin(0x3C, true);
display.clearDisplay();
display.display();
pinMode(JUMP_PIN, INPUT_PULLUP);
// Correct ESP32-S3 LEDC setup
ledcSetup(BUZZER_CHANNEL, 2000, 8);
ledcAttachPin(BUZZER_PIN, BUZZER_CHANNEL);
ledcWriteTone(BUZZER_CHANNEL, 0);
randomSeed(analogRead(0));
startGame();
}
// ----------------------
// Main Loop
// ----------------------
void loop() {
if (!gameStarted) return;
updateCube();
updateSpikes();
if (checkCollision()) {
gameStarted = false;
attempt++;
startGame();
return;
}
display.clearDisplay();
display.drawLine(0, 40, SCREEN_WIDTH, 40, SH110X_WHITE);
display.fillRect(20, cubeY - 8, 8, 8, SH110X_WHITE);
drawSpikes();
display.display();
delay(30);
}
Loading
esp32-s3-devkitc-1
esp32-s3-devkitc-1
Loading
grove-oled-sh1107
grove-oled-sh1107