Industrielle Fertigung
Industrielles Internet der Dinge | Industrielle Materialien | Gerätewartung und Reparatur | Industrielle Programmierung |
home  MfgRobots >> Industrielle Fertigung >  >> Manufacturing Technology >> Herstellungsprozess

Arduino Flappy Bird Klon bauen – Schritt-für-Schritt Spielprojekt mit TFT-Touchscreen

In diesem Arduino-Projekt erstellen wir ein cooles Arduino-Spiel – einen Nachbau des beliebten Flappy Bird für Smartphones – mit einem Arduino und einem TFT-Touchscreen. Lernen Sie die Funktionsweise im Video oder lesen Sie den Text unten.

Überblick

Das Spiel ist einfach, aber fesselnd: Per Touchscreen steuern wir den Vogel und versuchen, den sich bewegenden Säulen auszuweichen, deren Geschwindigkeit mit dem Fortschritt zunimmt. Der Highscore wird im EEPROM gespeichert und bleibt auch nach Stromausfall erhalten.

Arduino Flappy Bird Klon bauen – Schritt-für-Schritt Spielprojekt mit TFT-Touchscreen

Im vorherigen Tutorial (Arduino TFT Tutorial) haben wir detailliert gelernt, wie man TFT-Touchscreens mit Arduino nutzt – das Spielbeispiel wurde dort ausgeklammert und wird hier erklärt. Wir gehen Schritt für Schritt den Code durch.

Quellcode

Da der Code etwas länger ist, poste ich ihn in Abschnitten mit Erklärungen. Am Ende finden Sie den vollständigen Code.

Wir nutzen die UTFT- und URTouch-Bibliotheken von Henning Karlsen (www.RinkyDinkElectronics.com) sowie die EEPROM-Bibliothek für den Highscore-Speicher. Der EEPROM behält Daten auch ohne Strom.

Nach dem Einbinden der Bibliotheken erstellen wir die UTFT- und URTouch-Objekte und definieren die Spielvariablen. Im Setup initialisieren wir Display und Touch, lesen den Highscore aus dem EEPROM und starten das Spiel mit der benutzerdefinierten Funktion initiateGame().

#include <UTFT.h>
#include <URTouch.h>
#include <EEPROM.h>

//==== Objekte erstellen
UTFT myGLCD(SSD1289,38,39,40,41); // Parameter an Ihr Display/Shield anpassen
URTouch myTouch(6, 5, 4, 3, 2);

//==== Fonts definieren
extern uint8_t SmallFont[];
extern uint8_t BigFont[];
extern uint8_t SevenSegNumFont[];
extern unsigned int bird01[0x41A]; // Vogel-Bitmap

int x, y; // Koordinaten des Touch-Drucks
// Floppy Bird Variablen
int xP = 319;
int yP = 100;
int yB = 50;
int movingRate = 3;
int fallRateInt = 0;
float fallRate = 0;
int score = 0;
int lastSpeedUpScore = 0;
int highestScore;
boolean screenPressed = false;
boolean gameStarted = false;

void setup() {
  myGLCD.InitLCD();
  myGLCD.clrScr();
  myTouch.InitTouch();
  myTouch.setPrecision(PREC_MEDIUM);
  highestScore = EEPROM.read(0); // Highscore aus EEPROM lesen
  initiateGame(); // Spiel initialisieren
}

Die Funktion initiateGame() zeichnet den Initialzustand: Bildschirm leeren, blauen Hintergrund zeichnen, Bodenbereich, Texte und den Vogel per drawBird(). Eine while-Schleife wartet auf Bildschirmberührung. Drückt man oben rechts, wird der Highscore auf 0 zurückgesetzt; jede andere Berührung startet das Spiel.

Arduino Flappy Bird Klon bauen – Schritt-für-Schritt Spielprojekt mit TFT-Touchscreen
// ===== initiateGame - Benutzerdefinierte Funktion
void initiateGame() {
  myGLCD.clrScr();
  // Blauer Hintergrund
  myGLCD.setColor(114, 198, 206);
  myGLCD.fillRect(0,0,319,239);
  // Boden
  myGLCD.setColor(221,216,148);
  myGLCD.fillRect(0, 215, 319, 239);
  myGLCD.setColor(47,175,68);
  myGLCD.fillRect(0, 205, 319, 214);
  // Texte
  myGLCD.setColor(0, 0, 0);
  myGLCD.setBackColor(221, 216, 148);
  myGLCD.setFont(BigFont);
  myGLCD.print("Score:",5,220);
  myGLCD.setFont(SmallFont);
  myGLCD.print("HowToMechatronics.com", 140, 220);
  myGLCD.setColor(0, 0, 0);
  myGLCD.setBackColor(114, 198, 206);
  myGLCD.print("Highest Score: ",5,5);
  myGLCD.printNumI(highestScore, 120, 6);
  myGLCD.print(">RESET<",255,5);
  myGLCD.drawLine(0,23,319,23);
  myGLCD.print("TAP TO START",CENTER,100);
  drawBird(yB); // Vogel zeichnen

  // Warten bis Bildschirm berührt wird
  while (!gameStarted) {
    if (myTouch.dataAvailable()) {
      myTouch.read();
      x=myTouch.getX();
      y=myTouch.getY();
      // Highscore zurücksetzen
      if ((x>=250) && (x<=319) &&(y>=0) && (y<=28)) {
        highestScore = 0;
        myGLCD.setColor(114, 198, 206);
        myGLCD.fillRect(120, 0, 150, 22);
        myGLCD.setColor(0, 0, 0);
        myGLCD.printNumI(highestScore, 120, 5);
      }
      if ((x>=0) && (x<=319) &&(y>=30) && (y<=239)) {
        gameStarted = true;
        myGLCD.setColor(114, 198, 206);
        myGLCD.fillRect(0, 0, 319, 32);
      }
    }
  }
  // "TAP TO START" Text löschen
  myGLCD.setColor(114, 198, 206);
  myGLCD.fillRect(85, 100, 235, 116);
}

Im Haupt-Loop wird xP (Säulen-Position) bei jedem Durchlauf um movingRate verringert (Startwert 319, Geschwindigkeit anfangs 3). yP ist die Höhe der ersten Säule (Start 100).

// Haupt-Loop
void loop() {
  xP = xP - movingRate; // xP: x-Koordinate der Säulen; Bereich: 319 bis -51
  drawPilars(xP, yP); // Säulen zeichnen

  // yB: y-Koordinate des Vogels, abhängt von fallRate
  yB += fallRateInt;
  fallRate = fallRate + 0.4; // Beschleunigung/Gravitation
  fallRateInt = int(fallRate);

  // Kollision prüfen
  if(yB >= 180 || yB <= 0) { gameOver(); } // oben/unten
  if((xP <= 85) && (xP >= 5) && (yB <= yP-2)) { gameOver(); } // obere Säule
  if((xP <= 85) && (xP >= 5) && (yB >= yP+60)) { gameOver(); } // untere Säule

  drawBird(yB); // Vogel zeichnen

  // Säule hat Bildschirm passiert
  if (xP <= -51) {
    xP = 319; // xP zurücksetzen
    yP = rand() % 100 + 20; // Zufällige Säulenhöhe
    score++; // Score erhöhen
  }

  // Vogel steuern
  if (myTouch.dataAvailable() && !screenPressed) {
    fallRate = -6; // Negativer Fallrate = Sprung
    screenPressed = true;
  }
  // Loslassen erforderlich – Halten funktioniert nicht
  else if (!myTouch.dataAvailable() && screenPressed) {
    screenPressed = false;
  }

  // Alle 5 Punkte Geschwindigkeit erhöhen
  if ((score - lastSpeedUpScore) == 5) {
    lastSpeedUpScore = score;
    movingRate++;
  }
}

Funktionsweise: 50 Pixel breite Säulen bewegen sich von rechts nach links, jede mit zufälliger Höhe. Wegen der niedrigen Bildwiederholrate des Displays können wir den gesamten Bildschirm nicht neu zeichnen (Flackern). Stattdessen zeichnen wir nur die bewegten Objekte neu.

Arduino Flappy Bird Klon bauen – Schritt-für-Schritt Spielprojekt mit TFT-Touchscreen

Die Funktion drawPilars() nutzt fillRect(), um Säulen an neuer Position zu zeichnen, mit zusätzlichen blauen Rechtecken links/rechts, die die vorherige Position überdecken. if-Anweisungen verhindern Fehler, wenn Parameter außerhalb der Bildschirmgrenzen liegen. Am Ende wird der Score ausgegeben.

// ===== drawPilars - Benutzerdefinierte Funktion
void drawPilars(int x, int y) {
  if (x >= 270) {
    myGLCD.setColor(0, 200, 20);
    myGLCD.fillRect(318, 0, x, y-1);
    myGLCD.setColor(0, 0, 0);
    myGLCD.drawRect(319, 0, x-1, y);
    myGLCD.setColor(0, 200, 20);
    myGLCD.fillRect(318, y+81, x, 203);
    myGLCD.setColor(0, 0, 0);
    myGLCD.drawRect(319, y+80, x-1, 204);
  } else if (x <= 268) {
    // Blaues Rechteck rechts der Säule
    myGLCD.setColor(114, 198, 206);
    myGLCD.fillRect(x+51, 0, x+60, y);
    // Säule zeichnen
    myGLCD.setColor(0, 200, 20);
    myGLCD.fillRect(x+49, 1, x+1, y-1);
    // Schwarzer Rahmen
    myGLCD.setColor(0, 0, 0);
    myGLCD.drawRect(x+50, 0, x, y);
    // Blaues Rechteck links der Säule
    myGLCD.setColor(114, 198, 206);
    myGLCD.fillRect(x-1, 0, x-3, y);
    // Untere Säule
    myGLCD.setColor(114, 198, 206);
    myGLCD.fillRect(x+51, y+80, x+60, 204);
    myGLCD.setColor(0, 200, 20);
    myGLCD.fillRect(x+49, y+81, x+1, 203);
    myGLCD.setColor(0, 0, 0);
    myGLCD.drawRect(x+50, y+80, x, 204);
    myGLCD.setColor(114, 198, 206);
    myGLCD.fillRect(x-1, y+80, x-3, 204);
  }
  // Score anzeigen
  myGLCD.setColor(0, 0, 0);
  myGLCD.setBackColor(221, 216, 148);
  myGLCD.setFont(BigFont);
  myGLCD.printNumI(score, 100, 220);
}

Im Loop hängt yB (Vogel-Position) von der Fallrate ab, die pro Iteration zunimmt (Gravitation). Kollisionen mit Rand und Säulen werden geprüft.

Arduino Flappy Bird Klon bauen – Schritt-für-Schritt Spielprojekt mit TFT-Touchscreen

Die Funktion drawBird() zeichnet den Vogel als Bitmap (konvertiert mit ImageConverter565 von Henning Karlsen). Die Bitmap-Datei muss im Sketch-Ordner liegen. Der Vogel hat feste X-Koordinate (50), Y-Koordinate ist yB. Wie bei den Säulen löschen wir den vorherigen Zustand mit blauen Rechtecken über und unter dem Vogel.

//====== drawBird() - Benutzerdefinierte Funktion
void drawBird(int y) {
  // Vogel-Bitmap zeichnen
  myGLCD.drawBitmap(50, y, 35, 30, bird01);
  // Blaue Rechtecke oben/unten zum Löschen des vorherigen Zustands
  myGLCD.setColor(114, 198, 206);
  myGLCD.fillRoundRect(50, y, 85, y-6);
  myGLCD.fillRoundRect(50, y+30, 85, y+36);
}

Nachdem eine Säule den Bildschirm passiert hat, wird xP auf 319 zurückgesetzt, yP erhält einen neuen Zufallswert (20–100), Score erhöht sich. Per Touch wird fallRate auf -6 gesetzt (Sprung), else if verhindert Dauerfeuer. Alle 5 Punkte steigt movingRate (Schwierigkeit).

Die gameOver()-Funktion: Nach 1 Sekunde Verzögerung Bildschirm leeren, Score und Text anzeigen. Wenn neuer Highscore, in EEPROM speichern. Variablen zurücksetzen, initiateGame() aufrufen für Neustart.

Arduino Flappy Bird Klon bauen – Schritt-für-Schritt Spielprojekt mit TFT-Touchscreen
//======== gameOver() - Benutzerdefinierte Funktion
void gameOver() {
  delay(1000); // 1 Sekunde
  myGLCD.clrScr();
  myGLCD.setColor(255, 255, 255);
  myGLCD.setBackColor(0, 0, 0);
  myGLCD.setFont(BigFont);
  myGLCD.print("GAME OVER", CENTER, 40);
  myGLCD.print("Score:", 100, 80);
  myGLCD.printNumI(score, 200, 80);
  myGLCD.print("Restarting...", CENTER, 120);
  myGLCD.setFont(SevenSegNumFont);
  myGLCD.printNumI(2, CENTER, 150);
  delay(1000);
  myGLCD.printNumI(1, CENTER, 150);
  delay(1000);
  // Highscore in EEPROM speichern
  if (score > highestScore) {
    highestScore = score;
    EEPROM.write(0, highestScore);
  }
  // Variablen zurücksetzen
  xP = 319;
  yB = 50;
  fallRate = 0;
  score = 0;
  lastSpeedUpScore = 0;
  movingRate = 3;
  gameStarted = false;
  // Spiel neu starten
  initiateGame();
}

Das war die Erklärung. Bei Fragen nutzen Sie die Kommentare.

Kompletter Spielcode:

/* Arduino Game Projekt
 * Program made by Dejan Nedelkovski,
 * www.HowToMechatronics.com
 */
/* Verwendet UTFT und URTouch Bibliotheken von Henning Karlsen.
 * Download: www.RinkyDinkElectronics.com
 */
#include <UTFT.h>
#include <URTouch.h>
#include <EEPROM.h>

//==== Objekte
UTFT myGLCD(SSD1289,38,39,40,41);
URTouch myTouch(6, 5, 4, 3, 2);

//==== Fonts
extern uint8_t SmallFont[];
extern uint8_t BigFont[];
extern uint8_t SevenSegNumFont[];
extern unsigned int bird01[0x41A];

int x, y;
int xP = 319;
int yP = 100;
int yB = 50;
int movingRate = 3;
int fallRateInt = 0;
float fallRate = 0;
int score = 0;
int lastSpeedUpScore = 0;
int highestScore;
boolean screenPressed = false;
boolean gameStarted = false;

void setup() {
  myGLCD.InitLCD();
  myGLCD.clrScr();
  myTouch.InitTouch();
  myTouch.setPrecision(PREC_MEDIUM);
  highestScore = EEPROM.read(0);
  initiateGame();
}

void loop() {
  xP = xP - movingRate;
  drawPilars(xP, yP);
  yB += fallRateInt;
  fallRate = fallRate + 0.4;
  fallRateInt = int(fallRate);
  if(yB >= 180 || yB <= 0) { gameOver(); }
  if((xP <= 85) && (xP >= 5) && (yB <= yP-2)) { gameOver(); }
  if((xP <= 85) && (xP >= 5) && (yB >= yP+60)) { gameOver(); }
  drawBird(yB);
  if (xP <= -51) {
    xP = 319;
    yP = rand() % 100 + 20;
    score++;
  }
  if (myTouch.dataAvailable() && !screenPressed) {
    fallRate = -6;
    screenPressed = true;
  }
  else if (!myTouch.dataAvailable() && screenPressed) {
    screenPressed = false;
  }
  if ((score - lastSpeedUpScore) == 5) {
    lastSpeedUpScore = score;
    movingRate++;
  }
}

// ===== initiateGame
void initiateGame() {
  myGLCD.clrScr();
  myGLCD.setColor(114, 198, 206);
  myGLCD.fillRect(0,0,319,239);
  myGLCD.setColor(221,216,148);
  myGLCD.fillRect(0, 215, 319, 239);
  myGLCD.setColor(47,175,68);
  myGLCD.fillRect(0, 205, 319, 214);
  myGLCD.setColor(0, 0, 0);
  myGLCD.setBackColor(221, 216, 148);
  myGLCD.setFont(BigFont);
  myGLCD.print("Score:",5,220);
  myGLCD.setFont(SmallFont);
  myGLCD.print("HowToMechatronics.com", 140, 220);
  myGLCD.setColor(0, 0, 0);
  myGLCD.setBackColor(114, 198, 206);
  myGLCD.print("Highest Score: ",5,5);
  myGLCD.printNumI(highestScore, 120, 6);
  myGLCD.print(">RESET<",255,5);
  myGLCD.drawLine(0,23,319,23);
  myGLCD.print("TAP TO START",CENTER,100);
  drawBird(yB);
  while (!gameStarted) {
    if (myTouch.dataAvailable()) {
      myTouch.read();
      x=myTouch.getX();
      y=myTouch.getY();
      if ((x>=250) && (x<=319) &&(y>=0) && (y<=28)) {
        highestScore = 0;
        myGLCD.setColor(114, 198, 206);
        myGLCD.fillRect(120, 0, 150, 22);
        myGLCD.setColor(0, 0, 0);
        myGLCD.printNumI(highestScore, 120, 5);
      }
      if ((x>=0) && (x<=319) &&(y>=30) && (y<=239)) {
        gameStarted = true;
        myGLCD.setColor(114, 198, 206);
        myGLCD.fillRect(0, 0, 319, 32);
      }
    }
  }
  myGLCD.setColor(114, 198, 206);
  myGLCD.fillRect(85, 100, 235, 116);
}

// ===== drawPilars
void drawPilars(int x, int y) {
  if (x >= 270) {
    myGLCD.setColor(0, 200, 20);
    myGLCD.fillRect(318, 0, x, y-1);
    myGLCD.setColor(0, 0, 0);
    myGLCD.drawRect(319, 0, x-1, y);
    myGLCD.setColor(0, 200, 20);
    myGLCD.fillRect(318, y+81, x, 203);
    myGLCD.setColor(0, 0, 0);
    myGLCD.drawRect(319, y+80, x-1, 204);
  } else if (x <= 268) {
    myGLCD.setColor(114, 198, 206);
    myGLCD.fillRect(x+51, 0, x+60, y);
    myGLCD.setColor(0, 200, 20);
    myGLCD.fillRect(x+49, 1, x+1, y-1);
    myGLCD.setColor(0, 0, 0);
    myGLCD.drawRect(x+50, 0, x, y);
    myGLCD.setColor(114, 198, 206);
    myGLCD.fillRect(x-1, 0, x-3, y);
    myGLCD.setColor(114, 198, 206);
    myGLCD.fillRect(x+51, y+80, x+60, 204);
    myGLCD.setColor(0, 200, 20);
    myGLCD.fillRect(x+49, y+81, x+1, 203);
    myGLCD.setColor(0, 0, 0);
    myGLCD.drawRect(x+50, y+80, x, 204);
    myGLCD.setColor(114, 198, 206);
    myGLCD.fillRect(x-1, y+80, x-3, 204);
  }
  myGLCD.setColor(0, 0, 0);
  myGLCD.setBackColor(221, 216, 148);
  myGLCD.setFont(BigFont);
  myGLCD.printNumI(score, 100, 220);
}

//====== drawBird()
void drawBird(int y) {
  myGLCD.drawBitmap(50, y, 35, 30, bird01);
  myGLCD.setColor(114, 198, 206);
  myGLCD.fillRoundRect(50, y, 85, y-6);
  myGLCD.fillRoundRect(50, y+30, 85, y+36);
}

//======== gameOver()
void gameOver() {
  delay(3000);
  myGLCD.clrScr();
  myGLCD.setColor(255, 255, 255);
  myGLCD.setBackColor(0, 0, 0);
  myGLCD.setFont(BigFont);
  myGLCD.print("GAME OVER", CENTER, 40);
  myGLCD.print("Score:", 100, 80);
  myGLCD.printNumI(score, 200, 80);
  myGLCD.print("Restarting...", CENTER, 120);
  myGLCD.setFont(SevenSegNumFont);
  myGLCD.printNumI(2, CENTER, 150);
  delay(1000);
  myGLCD.printNumI(1, CENTER, 150);
  delay(1000);
  if (score > highestScore) {
    highestScore = score;
    EEPROM.write(0, highestScore);
  }
  xP = 319;
  yB = 50;
  fallRate = 0;
  score = 0;
  lastSpeedUpScore = 0;
  movingRate = 3;
  gameStarted = false;
  initiateGame();
}

Hier gibt es eine Download-Datei mit Arduino-Sketch, Vogelbild und Bitmap-Datei.

Herstellungsprozess

  1. Monitor-Ambilight selbst bauen mit Arduino Nano & WS2812B LEDs
  2. Klassisches Pong-Spiel auf Arduino UNO mit OLED-Display bauen – Schritt-für-Schritt-Tutorial
  3. DIY Arduino USB Gaming Controller – Bauen Sie Ihren eigenen High-Performance Gamepad
  4. Arduino Touch Breakout-Spiel: Klassisches Arcade auf 2,4″ TFT LCD
  5. Riesige Lego-Minifigur als Animatronic-Operation-Game mit Arduino bauen
  6. Arduino Audio-Frequenz-Detektor – Lauteste Schallpeaks mit hochsensiblem Modul messen
  7. Arduino Tic Tac Toe mit MAX7219 LED-Matrix und Karton-Gehäuse
  8. Zuverlässigen Arduino-Countdown-Timer mit SparkFun 7-Segment-Display bauen
  9. Pixel Chaser: Interaktives One-Tap-LED-Spiel mit Arduino Nano
  10. RC Porsche selbst bauen: Arduino-Schritt-für-Schritt-Anleitung