#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// I2C LCD
LiquidCrystal_I2C lcd(0x27, 16, 2);
// Buttons
const int buttonPin = 2;
const int pauseButtonPin = 3;
// Buzzer
const int buzzerPin = 11;
// Custom Character (Player)
byte customChar[] = {
B01110,
B01110,
B00100,
B01110,
B10101,
B00100,
B01010,
B01010
};
// ===============================
// START SCREEN
// ===============================
bool gameStarted = false;
// Player
int playerRow = 1;
int lastPlayerRow = 1;
bool isJumping = false;
unsigned long jumpStart = 0;
const int jumpDuration = 400;
// Pause
bool paused = false;
// Game
bool gameOver = false;
bool gameOverScreenShown = false;
unsigned long gameOverTime = 0;
unsigned long lastMove = 0;
int gameSpeed = 500;
int score = 0;
int lastScore = -1;
// Highscore
int highscore = 0;
// Obstacles
const int maxObstacles = 3;
int obstacleX[maxObstacles];
int obstacleRow[maxObstacles];
int lastObstacleX[maxObstacles];
int lastObstacleRow[maxObstacles];
unsigned long lastSpawn = 0;
int spawnInterval = 1500;
// Button debounce
bool buttonState = HIGH;
bool lastStableState = HIGH;
bool lastReading = HIGH;
unsigned long lastDebounceTime = 0;
int debounceDelay = 50;
void setup() {
pinMode(buttonPin, INPUT_PULLUP);
pinMode(pauseButtonPin, INPUT_PULLUP);
// Buzzer
pinMode(buzzerPin, OUTPUT);
lcd.init();
lcd.backlight();
lcd.createChar(0, customChar);
lcd.clear();
}
void loop() {
// ===============================
// START SCREEN
// ===============================
if (!gameStarted) {
showStartScreen();
if (digitalRead(buttonPin) == LOW) {
gameStarted = true;
resetGame();
lcd.clear();
delay(300);
}
return;
}
handleInput();
if (paused && !gameOver) {
showPauseScreen();
return;
}
if (!gameOver) {
updateJump();
updateGame();
if (!gameOver) {
drawGame();
}
}
else {
showGameOver();
}
}
void resetGame() {
lcd.clear();
gameOver = false;
gameOverScreenShown = false;
paused = false;
playerRow = 1;
lastPlayerRow = 1;
isJumping = false;
score = 0;
lastScore = -1;
gameSpeed = 500;
for (int i = 0; i < maxObstacles; i++) {
obstacleX[i] = -1;
obstacleRow[i] = 0;
lastObstacleX[i] = -1;
lastObstacleRow[i] = 0;
}
}
void handleInput() {
int reading = digitalRead(buttonPin);
if (reading != lastReading) {
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay) {
buttonState = reading;
}
if (lastStableState == HIGH && buttonState == LOW) {
if (!isJumping && !gameOver && !paused) {
startJump();
}
if (gameOver &&
millis() - gameOverTime > 1000) {
resetGame();
}
}
lastStableState = buttonState;
lastReading = reading;
if (digitalRead(pauseButtonPin) == LOW && !gameOver) {
paused = !paused;
lcd.clear();
delay(250);
}
}
void startJump() {
isJumping = true;
jumpStart = millis();
playerRow = 0;
}
void updateJump() {
if (isJumping && millis() - jumpStart > jumpDuration) {
isJumping = false;
playerRow = 1;
}
}
void updateGame() {
if (millis() - lastMove > gameSpeed) {
lastMove = millis();
moveObstacles();
spawnObstacle();
checkCollision();
if (gameOver) {
if (score > highscore) {
highscore = score;
}
return;
}
score++;
if (gameSpeed > 150) {
gameSpeed -= 5;
}
}
}
void spawnObstacle() {
if (millis() - lastSpawn > spawnInterval) {
lastSpawn = millis();
for (int i = 0; i < maxObstacles; i++) {
if (obstacleX[i] < 0) {
obstacleX[i] = 15;
obstacleRow[i] = random(0, 2);
break;
}
}
spawnInterval = random(1000, 2000);
}
}
void moveObstacles() {
for (int i = 0; i < maxObstacles; i++) {
if (obstacleX[i] >= 0) {
obstacleX[i]--;
if (obstacleX[i] < 0) {
obstacleX[i] = -1;
}
}
}
}
// ===============================
// CHECK COLLISION (EINGEBAUT)
// ===============================
void checkCollision() {
for (int i = 0; i < maxObstacles; i++) {
if (obstacleX[i] == 0 &&
obstacleRow[i] == playerRow) {
gameOver = true;
gameOverScreenShown = false;
gameOverTime = millis();
// ===============================
// WAMP WAMP WAMP SOUND (AKTIVER BUZZER)
// ===============================
digitalWrite(buzzerPin, HIGH);
delay(120);
digitalWrite(buzzerPin, LOW);
delay(150);
digitalWrite(buzzerPin, HIGH);
delay(150);
digitalWrite(buzzerPin, LOW);
delay(180);
digitalWrite(buzzerPin, HIGH);
delay(200);
digitalWrite(buzzerPin, LOW);
delay(220);
digitalWrite(buzzerPin, HIGH);
delay(500);
digitalWrite(buzzerPin, LOW);
}
}
}
// ===============================
// DRAW
// ===============================
void drawGame() {
lcd.setCursor(0, lastPlayerRow);
lcd.print(" ");
lcd.setCursor(0, playerRow);
lcd.write(0);
lastPlayerRow = playerRow;
for (int i = 0; i < maxObstacles; i++) {
if (lastObstacleX[i] >= 0) {
lcd.setCursor(lastObstacleX[i],
lastObstacleRow[i]);
lcd.print(" ");
}
if (obstacleX[i] >= 0) {
lcd.setCursor(obstacleX[i],
obstacleRow[i]);
lcd.print("X");
}
lastObstacleX[i] = obstacleX[i];
lastObstacleRow[i] = obstacleRow[i];
}
if (score != lastScore) {
lcd.setCursor(10, 0);
lcd.print(" ");
lcd.setCursor(10, 0);
lcd.print(score);
lastScore = score;
}
}
// ===============================
// SCREENS
// ===============================
void showStartScreen() {
lcd.setCursor(0, 0);
lcd.print(" DINO RUNNER");
lcd.setCursor(0, 1);
lcd.print("Press Button");
}
void showPauseScreen() {
lcd.setCursor(4, 0);
lcd.print("PAUSED");
lcd.setCursor(0, 1);
lcd.print("Press Btn...");
}
void showGameOver() {
if (gameOverScreenShown) return;
gameOverScreenShown = true;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("GAME OVER");
lcd.setCursor(0, 1);
lcd.print("HS:");
lcd.print(highscore);
lcd.setCursor(9, 1);
lcd.print("SC:");
lcd.print(score);
}