mikrocontroller.net

Forum: Projekte & Code PixelPipe_Game Arduino UNO mit OLED Display und Joystick


Autor: Manuel B. (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,

habe gestern ein kleines Projekt umgesetzt, das an eine Art 
FlappyBird-Klon erinnert. Dabei geht es darum, einen per Joystick 
bedienbaren Pixel auf dem 128x32 OLED Display durch die sich annähernden 
Rohröffnungen zu bewegen. Bei einer Kollision ist das Spiel vorbei.

Bevor hier die Diskussion wieder anfängt, wieso ich denn Arduino gewählt 
habe, gleich vorab schon mal die Antwort. Obwohl ich mich in der Welt 
der 8-Bit Atmels und STM32-Controller sehr gut zurecht finde, so ging es 
mir hauptsächlich darum, neue Peripherie, wie beispielsweise das OLED 
Display und den Joystick ohne großen Aufwand in Betrieb zu nehmen. Da es 
Bibliotheken sowohl für Arduino, als auch für die blanken Controller 
gibt, macht es somit kaum noch Unterschied, welche Plattform ich wähle.

Der Sketch ist hier im Anhang und der Quellcode nachfolgend zu finden.

Ebenso ist ein Überblicksvideo auf YouTube auf meinem Kanal zu finden:
https://youtu.be/SqoWV4F428M

Der im Video enthaltene Schaltplan zu dem Ganzen hier noch zusätzlich im 
Anhang unter Schaltplan.png zu finden.

Bei Fragen, Erklärungen oder sonstiger Kritik, bin ich gerne Bereit mit 
Euch darüber zu diskutieren und Hilfestellung zu geben, falls jemand 
dieses Projekt nachstellen oder erweitern möchte.
Auch eine Erweiterung mit Highscore, alternativer Spielersymbole anstatt 
dem Pixel und optische Verbesserungen sind derzeit in Arbeit.

Hoffe Euch gefällt, was ich letzten Nachmittag zusammengebastelt habe 
und freue mich auf Antworten

/*  PixelPipe v0.5
 *  07.09.2016
 *  Arduino UNO
 */
/*
 *  OLED 128x32 
 *    5V->5V, GND->GND, SDA->A4(SDA), SCL->A5(SCL)
 *  Joysick 4duino (2 Potentiometer, 1 Button)
 *    5V->5V, GND->GND, VRX->A0, VRY->A1, SW->2 mit PULL-UP
 */
#include <Adafruit_SSD1306.h>
#include <Adafruit_GFX.h>
#include <SPI.h>
#include <Wire.h>
#define OLED_RESET  4

//Settings
//  actual pipe speed = PIPESPEED * REFRESH_MS
#define PIPESPEED   2
#define REFRESH_MS  50

//State Overview
#define STATE_START               0
#define STATE_START_PRESS_BUTTON  1
#define STATE_PLAY                2
#define STATE_LOOSE               3
#define STATE_RESET_PRESS_BUTTON  4

//Define Variables
Adafruit_SSD1306 display(OLED_RESET);
int x_in,y_in,x,y;
char pipeCnt[10]; //Variable for the pipes moving from right to left on the screen
char pipeSpeed=0; //counter variable for the pipeSpeed
char startState=0;
bool hitState=false;
typedef struct sPipe{ //Variable for creating pipes
  bool active;        //active=true means show pipe on display
  unsigned x_o, x_u;  //coordinates for upper and lower pipe
  unsigned y_o, y_u;
  unsigned w_o, w_u;
  unsigned h_o, h_u;
  unsigned gap;       //size of the gap between the uper and lower pipe
  unsigned nextPipe;  //pixel-distance between the pipe and the next one
};

sPipe pipe[10];       //amount of available pipes

void setup() {
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  pinMode(2,INPUT);
}

void loop() {
  if(startState==STATE_START){    //Show Start Screen,if Button not pressed, go to the Wait for Press Button Screen
    display.clearDisplay();
    display.setTextColor(WHITE);
    display.setTextSize(2);
    display.setCursor(15,10);
    display.println("Start");
    if(digitalRead(2)==HIGH)
      startState++;
  }
  else if(startState==STATE_START_PRESS_BUTTON){  //Wait for the button being pressed, then start the game
    if(digitalRead(2)==LOW){
      createPipe(&pipe[0]); //Create a new pipe mit ID0
      startState++; 
    } 
  }
  else if(startState==STATE_PLAY){  //Loop for the actual game. Ends when pixel hits a pipe
    display.clearDisplay();
    getInput();//Get the coordinates for the pixel from the ADC's
    displayPlayer();//Show the pixel on the Screen
    displayPipes();//Show pipes on the Screen
    hitState=calculateHit();//Calculate if pixel hits a pipe
    if(hitState==true)//if pixel hit a pipe, then go to the loose state
      startState++;
  }
  else if(startState==STATE_LOOSE){   //Clear als pipes and show Verloren on the screen
    display.clearDisplay();
    display.setTextColor(WHITE);
    display.setTextSize(2);
    display.setCursor(13,10);
    display.println("Verloren");
    display.setCursor(1,0);
    display.setTextSize(1);
    for(int i=0; i<10;i++){
      pipe[i].active=false; //Set all pipes to active=false, so they are not displayed if a new game starts
      pipeCnt[i]=0; //set the pipe-Counter to 0
    }
    startState++;
  }
  else if(startState==STATE_RESET_PRESS_BUTTON){//Wait for Button to be pressed for starting a new game
    if(digitalRead(2)==LOW)
      startState=STATE_START;  
  }
  display.display();
  delay(REFRESH_MS);
}
//This function creates a new pipe witch uper and lower part of the pipe
//User can customize the values for more challenging pipe-spawns
void createPipe(sPipe *nPipe){
  nPipe->active=true;
  nPipe->x_o=nPipe->x_u=0;
  nPipe->y_o=0;
  nPipe->w_o=nPipe->w_u=random(7,30);
  nPipe->h_o=random(3,20);
  nPipe->gap=random(5,7);
  nPipe->y_u=nPipe->h_o+nPipe->gap;
  nPipe->h_u=31-nPipe->y_u;
  nPipe->nextPipe=random(nPipe->w_o+20,nPipe->w_o+50);
}
//Read the ADC to get information about the Joystick and convert the information to
//useable coordinates on the 128x32 OLED
void getInput(){
  y_in = 1023-analogRead(A0);
  x_in = analogRead(A1); 
  x=(float)x_in*127/1023;
  y=(float)y_in*31/1023;  
}
//show pixel player on the Screen with the coordinates from getInput()
void displayPlayer(){
  display.drawPixel(x,y,WHITE);
}
//Show the pipes
//for the exact algorithm study this code for studying purposes or ask in the thread
void displayPipes(){
  pipeSpeed++;
  if(pipeSpeed>=PIPESPEED){
    for(int i=0;i<10;i++){
      if(pipe[i].active){
        pipeCnt[i]++; 
        if(pipeCnt[i]==pipe[i].nextPipe){
          if((i>=9)&&(pipe[0].active==false))
            createPipe(&pipe[0]);
          else
            createPipe(&pipe[i+1]);
        }
        else if(pipeCnt[i]>=127){
          pipeCnt[i]=0; 
          pipe[i].active=false; 
        }      
      }
    }   
    pipeSpeed=0; 
  } 
  for(int i=0; i<10;i++){
    if(pipe[i].active){
      display.fillRect((127-pipeCnt[i]),pipe[i].y_o,pipe[i].w_o,pipe[i].h_o,WHITE);
      display.fillRect((127-pipeCnt[i]),pipe[i].y_u,pipe[i].w_u,pipe[i].h_u,WHITE);
      pipe[i].x_o=pipe[i].x_u=127-pipeCnt[i];
    }
  }
}
//Calculate if the player hits a pipe with its pixel Player
//for the exact algorithm study this code for studying purposes or ask in the thread
bool calculateHit(){
  for(int i=0;i<10;i++){
    if(pipe[i].active){
      if((x>=pipe[i].x_o)&&(x<=pipe[i].x_o+pipe[i].w_o)&&((y<=pipe[i].h_o)||(y>=(pipe[i].h_o+pipe[i].gap))))
        return true;
    }
  }
  return false; 
}

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.