Das Ziel dieses Projektes ist, ein eigenes Pong-Spiel umzusetzen.
Ein Punkt (Ball) bewegt sich auf dem Bildschirm hin und her.
Jeder der beiden Spieler steuert einen senkrechten Strich (Schläger), den er mit zwei Tastern nach oben und unten steuern kann.
Wenn die linke oder rechte Wand berührt wird, erhält der Gegner einen Punkt.
⇒ Für den Fall, dass kein Arduino zur Verfügung steht oder Materialien nicht vorhanden sind. Kann dieser webbasierter Arduino Emulator verwendet werden. [klicken]
Beim UNO R3 gibt es dafür oberhalb des Pin 13 einen SDA und SCL Pin, alternativ können auch die analogen Pins A4 (SDA) und A5 (SCL) verwendet werden.
Tabelle 2: OLED Display Pins
VCC
Pin für die Spannungsversorgung, anzuschließen an den 5V Pin des Mikrocontrollers
GND
Ground-Pin, anzuschließen an den GND Pin des Mikrocontrollers
SDA
Überträgt Daten oder Adressen
SCL
Überträgt den Takt
Aufbau Schaltung
Abb.4 Schaltung Pong Spiel
In Abbildung 4 wird die Schaltung für das Projekt "Arduino Pong Spiel" dargestellt.
Bevor mit der Programmierung begonnen werden kann, muss die Schaltung des Projekts aufgebaut werden.
1. Taster initialisieren
Zuerst müssen Sie allen vier Tastern einen Pin am Arduino zugewiesen werden. Diese können Sie beliebig an den digitalen Schnittstellen anschließen.
Danach werden die Pins mit pinMode(); und digitalWrite(); initialisiert.
[Quelltext 2: Pong.ino]
Lösung
/* Taster PINS */constcharUP_BUTTON=2;constcharDOWN_BUTTON=3;constcharUP_BUTTON_TWO=4;constcharDOWN_BUTTON_TWO=5;voidsetup(){/* Taster initialisieren */pinMode(UP_BUTTON,INPUT);pinMode(DOWN_BUTTON,INPUT);digitalWrite(UP_BUTTON,HIGH);digitalWrite(DOWN_BUTTON,HIGH);/* Taster zwei */pinMode(UP_BUTTON_TWO,INPUT);pinMode(DOWN_BUTTON_TWO,INPUT);digitalWrite(UP_BUTTON_TWO,HIGH);digitalWrite(DOWN_BUTTON_TWO,HIGH);voidloop(){}
Um später zu sehen, wo Sie welche Objekte platziert hat, muss das Display hochgefahren werden.
Zusätzlich wird beim Start vom Display ein Timer gesetzt, dieser wird für die Bewegung vom Ball benötigt. Benötigt wird: display.display(); und mills(); zum Zeitauslesen.
[Quelltext 6: Pong.ino]
Lösung Code
voidsetup(){Serial.begin(9600);/* Hochfahren vom Display */display.begin(SSD1306_SWITCHCAPVCC,0x3C);// Starte das Display display.display();// Zeigt das Startbild vom Arduino anunsignedlongstart=millis();// Anzahl von Ms zurück, seit Arduino-Board das aktuelle Programm gestartet hatdelay(1000);display.clearDisplay();// Löscht das Bild/* Ball Timer zuweisen*/ballUpdate=millis();delay(1000);}
Display Spielname anzeigen
Als optionale Zusatzaufgabe können Sie den Namen Pong Spiel auf dem Display anzeigen lassen.
Tipp: Hierfür werden die display.set(); und display.println(); Funktionen benötigt.
[Quelltext 7: Pong.ino]
Lösung
/* Pong Spiel Namen anzeigen */display.setTextSize(2);display.setTextColor(WHITE);display.setCursor(5,25);display.println("Pong Spiel");display.display();delay(1000);display.clearDisplay();
Display Punkteanzeige
Um die Punktanzeige ins Spiel mit einzubringen, werden zwei Methoden benötigt.
Die erste Methode soll die Zahlen auf dem Display anzeigen.
Die zweite Methode soll die erstellen Zahlen wieder löschen, um das nächste Ergebnis korrekt anzuzeigen.
Tipp: Hierfür werden die display.set(); und display.println(); Funktionen benötigt.
[Quelltext 8: Pong.ino]
Lösung
voiddrawScore(){display.setTextSize(2);display.setTextColor(WHITE);display.setCursor(43,0);display.println(playerOne);display.setCursor(73,0);display.println(playerTwo);}/* Punkte zurücksetzten */voideraseScore(){display.setTextSize(2);display.setTextColor(BLACK);display.setCursor(43,0);display.println(playerOne);display.setCursor(73,0);display.println(playerTwo);}
Bewegter Ball
Damit der Ball sich auf dem Display bewegt, benötigt Sie eine Rechenfunktion.
Tipp: Rechnen Sie den X und Y Wert + 1 und erstellen Sie eine neue Variable.
[Quelltext 9: Pong.ino]
Lösung
voidloop(){unsignedlongtime=millis();// Zeit-Variable/* Neue Ballposition */if(time>Ball_update){unsignedcharNew_x=Ball_x+Ball_dir_x;// (x+1)unsignedcharNew_y=Ball_y+Ball_dir_y;// (y+1)}
1. Wände erkennen
Damit der Ball wie gewünscht von den Wänden abprallt, müssen die Wände bei Berührung mit einer if-Bedingung() abgefragt werden.
Wenn der Ball auf eine Wand trifft, muss die Richtung vom Ball geändert werden.
Zusätzlich soll der Spieler einen Punkt bekommen, wenn die linke oder rechte Wand berührt wurde.
Tipp: Wenn die X oder Y Position von der Wand erreicht wurde, negieren Sie den Addierer.
[Quelltext 10: Pong.ino]
Lösung
/* Checkt ob die vertikalen Wände berührt werden */if(New_x==0)// Wand links{ballDir_x=-ballDir_x;// Wechselt die RichtungNew_x+=ballDir_x+ballDir_x;eraseScore();// Punkt für SpielerplayerOne+=1;drawScore();New_x=64;// Ball ResetNew_y=32;}if(New_x==127)// Wand rechts{ballDir_x=-ballDir_x;// Wechselt die RichtungNew_x+=ballDir_x+ballDir_x;eraseScore();// Punkt für Spieler 2playerTwo+=1;drawScore();New_x=64;// Ball ResetNew_y=32;}// Checkt ob die horizontalen Wände berührt werdenif(New_y==0||New_y==63){ballDir_y=-ballDir_y;// Wechselt die RichtungNew_y+=ballDir_y+ballDir_y;}
2. Paddel erkennen
Auch muss der Ball, wenn er den Schläger berührt, seine Richtung ändern.
Tipp: Die Position von den Paddeln muss kleiner sein als die Position vom Ball.
[Quelltext 11: Pong.ino]
Lösung
// Checkt, ob der Spieler 1 Paddel getroffen wurdeif(New_x==PLAYER_ONE_X&&New_y>=playerOne_y&&New_y<=playerOne_y+PADDLE_HEIGHT){ballDir_x=-ballDir_x;New_x+=ballDir_x+ballDir_x;}// Checkt, ob der Spieler 2 Paddel getroffen wurdeif(New_x==PLAYER_TWO_X&&New_y>=playerTwo_y&&New_y<=playerTwo_y+PADDLE_HEIGHT){ballDir_x=-ballDir_x;New_x+=ballDir_x+ballDir_x;}
3. Neue Ballposition anzeigen
Nun kann sich der Ball bewegen, muss aber noch auf dem Display als Pixel angezeigt und die alten Pixel wieder gelöscht werden.
Außerdem müssen die neuen Variablen übertragen werden auf die alten, um weitere Abläufe zu ermöglichen.
Um die Taster abzufragen und falsche Eingaben zu überprüfen, wird mit einem bool programmiert.
Tipp:
Neue Variablen für die Taster mit bool initialisieren.
Die alten und neuen Variablen auf LOW zu setzen, um die Taster schalten zu können.
Wenn die boolVariable = true ist, soll das Display mit display.display(); aktualisiert werden.
[Quelltext 13: Pong.ino]
Lösung
boolupdate=false;// Ein bool enthält einen von zwei Werten, true oder falseunsignedlongtime=millis();// Zeit-VariablestaticboolUP_STATE=false;staticboolDOWN_STATE=false;staticboolUP_STATE_TWO=false;staticboolDOWN_STATE_TWO=false;UP_STATE|=(digitalRead(UP_BUTTON)==LOW);// Taster auslesenDOWN_STATE|=(digitalRead(DOWN_BUTTON)==LOW);UP_STATE_TWO|=(digitalRead(UP_BUTTON_TWO)==LOW);DOWN_STATE_TWO|=(digitalRead(DOWN_BUTTON_TWO)==LOW);if(update)// Wenn Variable = true wird display.display(); ausgeführtdisplay.display();}
Paddel Spieler
Der letzte Schritt ist, die Schläger mit den Tastern steuerbar zu machen.
Diese dürfen aber nicht aus dem Display gelangen.
Tipp:
Der Ablauf ist ähnlich wie beim Ball, nur wird die if-Bedingung von den Tastern betätigt.
Zusätzlich müssen noch die Schläger auf dem Display mit der Funktion display.drawFastVLine(PLAYER_ONE_X, playerOne_y, PADDLE_HEIGHT, BLACK); angezeigt werden.
[Quelltext 14: Pong.ino]
Lösung
/* Spieler 1 Paddel */display.drawFastVLine(PLAYER_ONE_X,playerOne_y,PADDLE_HEIGHT,BLACK);if(UP_STATE){playerOne_y-=1;}if(DOWN_STATE){playerOne_y+=1;}UP_STATE=DOWN_STATE=false;if(playerOne_y<1)playerOne_y=1;// Um nicht aus dem Bildschirm zu gelangenif(playerOne_y+PADDLE_HEIGHT>63)playerOne_y=63-PADDLE_HEIGHT;display.drawFastVLine(PLAYER_ONE_X,playerOne_y,PADDLE_HEIGHT,WHITE);update=true;/* Spieler 2 Paddel */display.drawFastVLine(PLAYER_TWO_X,playerTwo_y,PADDLE_HEIGHT,BLACK);if(UP_STATE_TWO){playerTwo_y-=1;// Paddle 1 nach unten}if(DOWN_STATE_TWO){playerTwo_y+=1;// Paddle 1 nach oben}UP_STATE_TWO=DOWN_STATE_TWO=false;if(playerTwo_y<1)playerTwo_y=1;// Um nicht aus dem Bildschirm zugelangenif(playerTwo_y+PADDLE_HEIGHT>63)playerTwo_y=63-PADDLE_HEIGHT;display.drawFastVLine(PLAYER_TWO_X,playerTwo_y,PADDLE_HEIGHT,WHITE);if(update)//hierhin verschiebendisplay.display();}
Musterlösung
Lösung Code
/* Benötigte Bibliotheken */#include<SPI.h>#include<Wire.h>#include<Adafruit_GFX.h>#include<Adafruit_SSD1306.h>/* Taster PINS */constcharUP_BUTTON=2;constcharDOWN_BUTTON=3;constcharUP_BUTTON_TWO=4;constcharDOWN_BUTTON_TWO=5;/* Variablen */constunsignedcharBALL_RATE=16;constunsignedcharPADDLE_HEIGHT=25;constcharSCREEN_WIDTH=128;// OLED Display BreiteconstcharSCREEN_HEIGHT=64;// OLED Display HöheunsignedintplayerOne=0,playerTwo=0;/* Deklaration Display, verbindung zum I2C (SDA, SCL pins) */constcharOLED_RESET=0;// Reset pinAdafruit_SSD1306display(SCREEN_WIDTH,SCREEN_HEIGHT,&Wire,OLED_RESET);/* Position vom Ball, Paddle und Spieler auf dem Display */unsignedcharballX=64,ballY=32;unsignedcharballDir_x=1,ballDir_y=1;constunsignedcharPLAYER_TWO_X=12;unsignedcharplayerTwo_y=16;constunsignedcharPLAYER_ONE_X=115;unsignedcharplayerOne_y=16;unsignedlongballUpdate;voidsetup(){Serial.begin(9600);/* Hochfahren vom Display */display.begin(SSD1306_SWITCHCAPVCC,0x3C);// Starte das Displaydisplay.display();// Zeigt das Startbild vom Arduino anunsignedlongstart=millis();// Anzahl von Ms zurück, seit Arduino-Board das aktuelle Programm gestartet hatdelay(1000);display.clearDisplay();// Löscht das Bild/* Pong Spiel Namen anzeigen */display.setTextSize(2);display.setTextColor(WHITE);display.setCursor(5,25);display.println("Pong Spiel");display.display();delay(1000);display.clearDisplay();/* Ball Timer zuweisen*/ballUpdate=millis();delay(1000);/* Taster Initialisieren */pinMode(UP_BUTTON,INPUT);// Taster als INPUT festlegenpinMode(DOWN_BUTTON,INPUT);digitalWrite(UP_BUTTON,HIGH);// Taster anschaltendigitalWrite(DOWN_BUTTON,HIGH);/* Taster zwei */pinMode(UP_BUTTON_TWO,INPUT);pinMode(DOWN_BUTTON_TWO,INPUT);digitalWrite(UP_BUTTON_TWO,HIGH);digitalWrite(DOWN_BUTTON_TWO,HIGH);}/* Punkteanzeige */voiddrawScore(){display.setTextSize(2);display.setTextColor(WHITE);display.setCursor(43,0);display.println(playerOne);display.setCursor(73,0);display.println(playerTwo);}/* Punkte zurücksetzten */voideraseScore(){display.setTextSize(2);display.setTextColor(BLACK);display.setCursor(43,0);display.println(playerOne);display.setCursor(73,0);display.println(playerTwo);}voidloop(){drawScore();boolupdate=false;// Ein bool enthält einen von zwei Werten, true oder falseunsignedlongtime=millis();// Zeit-VariablestaticboolUP_STATE=false;staticboolDOWN_STATE=false;staticboolUP_STATE_TWO=false;staticboolDOWN_STATE_TWO=false;UP_STATE|=(digitalRead(UP_BUTTON)==LOW);// Taster auslesenDOWN_STATE|=(digitalRead(DOWN_BUTTON)==LOW);UP_STATE_TWO|=(digitalRead(UP_BUTTON_TWO)==LOW);DOWN_STATE_TWO|=(digitalRead(DOWN_BUTTON_TWO)==LOW);/* Neue Ballposition */if(time>ballUpdate){unsignedcharNew_x=ballX+ballDir_x;// (x+1)unsignedcharNew_y=ballY+ballDir_y;// (y+1)/* Checkt ob die vertikalen Wände berührt werden */if(New_x==0)// Wand links{ballDir_x=-ballDir_x;// Wechselt die RichtungNew_x+=ballDir_x+ballDir_x;eraseScore();// Punkt für SpielerplayerOne+=1;drawScore();New_x=64;// Ball ResetNew_y=32;}if(New_x==127)// Wand rechts{ballDir_x=-ballDir_x;// Wechselt die RichtungNew_x+=ballDir_x+ballDir_x;eraseScore();// Punkt für Spieler 2playerTwo+=1;drawScore();New_x=64;// Ball ResetNew_y=32;}// Checkt ob die horizontalen Wände berührt werdenif(New_y==0||New_y==63){ballDir_y=-ballDir_y;// Wechselt die RichtungNew_y+=ballDir_y+ballDir_y;}// Checkt, ob der Spieler 1 Paddel getroffen wurdeif(New_x==PLAYER_ONE_X&&New_y>=playerOne_y&&New_y<=playerOne_y+PADDLE_HEIGHT){ballDir_x=-ballDir_x;New_x+=ballDir_x+ballDir_x;}// Checkt, ob der Spieler 2 Paddel getroffen wurdeif(New_x==PLAYER_TWO_X&&New_y>=playerTwo_y&&New_y<=playerTwo_y+PADDLE_HEIGHT){ballDir_x=-ballDir_x;New_x+=ballDir_x+ballDir_x;}display.drawPixel(ballX,ballY,BLACK);display.drawPixel(New_x,New_y,WHITE);ballX=New_x;ballY=New_y;ballUpdate+=BALL_RATE;update=true;// true ~ display.display();}/* Spieler 1 Paddel */display.drawFastVLine(PLAYER_ONE_X,playerOne_y,PADDLE_HEIGHT,BLACK);if(UP_STATE){playerOne_y-=1;}if(DOWN_STATE){playerOne_y+=1;}UP_STATE=DOWN_STATE=false;if(playerOne_y<1)playerOne_y=1;// Um nicht aus dem Bildschirm zu gelangenif(playerOne_y+PADDLE_HEIGHT>63)playerOne_y=63-PADDLE_HEIGHT;display.drawFastVLine(PLAYER_ONE_X,playerOne_y,PADDLE_HEIGHT,WHITE);update=true;/* Spieler 2 Paddel */display.drawFastVLine(PLAYER_TWO_X,playerTwo_y,PADDLE_HEIGHT,BLACK);if(UP_STATE_TWO){playerTwo_y-=1;// Paddle 1 nach unten}if(DOWN_STATE_TWO){playerTwo_y+=1;// Paddle 1 nach oben}UP_STATE_TWO=DOWN_STATE_TWO=false;if(playerTwo_y<1)playerTwo_y=1;// Um nicht aus dem Bildschirm zugelangenif(playerTwo_y+PADDLE_HEIGHT>63)playerTwo_y=63-PADDLE_HEIGHT;display.drawFastVLine(PLAYER_TWO_X,playerTwo_y,PADDLE_HEIGHT,WHITE);if(update)display.display();}