Arduino Projekt: Pong Spiel: Unterschied zwischen den Versionen
(83 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt) | |||
Zeile 3: | Zeile 3: | ||
'''Autor:''' Justin Frommberger<br> | '''Autor:''' Justin Frommberger<br> | ||
== | == Aufgabenstellung== | ||
Das Ziel dieses Projektes ist, ein eigenes Pong-Spiel umzusetzen. | |||
* Ein Punkt (Ball) bewegt sich auf dem Bildschirm hin und her. | * 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. | * 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.<br> | ||
⇒ Für den Fall, dass '''kein Arduino''' zur Verfügung steht oder '''Materialien''' nicht vorhanden sind. Kann dieser '''webbasierter Arduino Emulator''' verwendet werden. [https://wokwi.com/projects/new/arduino-uno [klicken]] | |||
<br><br> | |||
[[Datei:PongGameUML.png|600px]]<br> | [[Datei:PongGameUML.png|600px]]<br> | ||
[Abb. 2: UML] | [Abb. 2: UML] | ||
== | ==Benötigte Materiallien== | ||
{| class="wikitable" | {| class="wikitable" | ||
|+ style = "text-align: left"| | |+ style = "text-align: left"| Tabelle 1: Materialliste | ||
|- | |- | ||
Zeile 21: | Zeile 23: | ||
|<big><big>①</big></big> || 1 || [[Arduino|Funduino Arduino UNO R3]] ||[[Datei:Arduino Uno R3.jpg|ohne|100px|]] | |<big><big>①</big></big> || 1 || [[Arduino|Funduino Arduino UNO R3]] ||[[Datei:Arduino Uno R3.jpg|ohne|100px|]] | ||
|- | |- | ||
|<big><big>②</big></big> || 1 || | |<big><big>②</big></big> || 1 || Typ 2 ||[[Datei:Arduino_Kabel.png|ohne|100px|]] | ||
|- | |- | ||
|<big><big>③</big></big> || 14+ || Jumperkabel, männlich/männlich||[[Datei:R19-F-2-2.jpg|ohne|100px|]] | |<big><big>③</big></big> || 14+ || Jumperkabel, männlich/männlich||[[Datei:R19-F-2-2.jpg|ohne|100px|]] | ||
Zeile 32: | Zeile 34: | ||
|} | |} | ||
== | == Vorab wichtig zu wissen!== | ||
[[Datei:OLED 0.96.png|thumb|rigth|200px|Abb. 3: OLED Display]] | [[Datei:OLED 0.96.png|thumb|rigth|200px|Abb. 3: OLED Display]] | ||
===OLED Display: | ===OLED Display:=== | ||
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. | 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. | ||
Zeile 45: | Zeile 47: | ||
| '''GND''' || Ground-Pin, anzuschließen an den GND Pin des Mikrocontrollers | | '''GND''' || Ground-Pin, anzuschließen an den GND Pin des Mikrocontrollers | ||
|- | |- | ||
| '''SDA ''' || Überträgt Daten oder Adressen | | '''SDA ''' || Überträgt Daten oder Adressen | ||
|- | |- | ||
| '''SCL''' || Überträgt den Takt | | '''SCL''' || Überträgt den Takt | ||
Zeile 51: | Zeile 53: | ||
== Aufbau Schaltung == | == Aufbau Schaltung == | ||
[[Datei:Schaltung_Pong.png|500px|thumb|right|Abb.4 Schaltung Pong Spiel]] | [[Datei:Schaltung_Pong.png|500px|thumb|right|Abb.4 Schaltung Pong Spiel]] | ||
In | In Abbildung 4 wird die Schaltung für das Projekt "Arduino Pong Spiel" dargestellt.<br> | ||
Bevor mit der Programmierung begonnen werden kann, muss die Schaltung des Projekts aufgebaut werden. | Bevor mit der Programmierung begonnen werden kann, muss die Schaltung des Projekts aufgebaut werden. | ||
==Programmierung== | ==Programmierung== | ||
Es ist wichtig, die [[Programmierrichtlinien Einsteiger|['''Programmierrichtlinien''']]] beim Programmieren einzuhalten.<br><br> | Es ist wichtig, die [[Programmierrichtlinien Einsteiger|['''Programmierrichtlinien''']]] beim Programmieren einzuhalten.<br><br> | ||
Wenn Sie Fragen zur Programmierung haben, finden Sie die Antworten in den [https://wiki.hshl.de/wiki/index.php/Kategorie:Arduino:_Projekt_Grundkenntnisse ['''Grundkenntnissen''']]. | |||
=== | <br> | ||
---- | |||
=== Bibliotheken einfügen=== | |||
Downloaden Sie die Bibliotheken für das OLED Display[https://github.com/adafruit/Adafruit_SSD1306 ''' [SSD10306]'''] und [https://github.com/adafruit/Adafruit-GFX-Library '''[GFX]'''].<br> | |||
'''Benötigt wird: ''' | '''Benötigt wird: ''' | ||
# Kommunikation zum I2C/TWI Gerät | # Kommunikation zum I2C/TWI Gerät: <code>'''Wire.h'''</code> | ||
# Kommunikation mit dem SPI Gerät | # Kommunikation mit dem SPI Gerät: <code>'''SPI.h'''</code> | ||
# Adafruit GFX Grafik | # Adafruit GFX Grafik: <code>'''Adafruit_GFX.h'''</code> | ||
# 128x64 and 128x32 OLEDs Displays | # 128x64 and 128x32 OLEDs Displays: <code>'''Adafruit_SSD1306.h'''</code> | ||
['''Quelltext 1: ''' <code>Pong.ino</code>] | ['''Quelltext 1: ''' <code>Pong.ino</code>] | ||
Zeile 73: | Zeile 76: | ||
|- | |- | ||
| | | | ||
<syntaxhighlight lang="C" style="border: none; background-color: #EFF1C1; font-size: | <syntaxhighlight lang="C" style="border: none; background-color: #EFF1C1; font-size:small"> | ||
// Benötigte Bibliotheken | // Benötigte Bibliotheken | ||
#include <SPI.h> | #include <SPI.h> | ||
Zeile 81: | Zeile 84: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
|} | |} | ||
---- | |||
=== | === Initialisierung Arduino=== | ||
''' | '''1. Taster initialisieren'''<br> | ||
Zuerst müssen allen vier Tastern einen Pin am Arduino zugewiesen werden | Zuerst müssen Sie allen vier Tastern einen Pin am Arduino zugewiesen werden. Diese können Sie beliebig an den digitalen Schnittstellen anschließen.<br> | ||
Danach werden die Pins mit [https://wiki.hshl.de/wiki/index.php/Grundkenntnisse_Programmierung_(Pulsierende_LED)#pinMode() '''<code>pinMode();</code>'''] und [https://wiki.hshl.de/wiki/index.php/Grundkenntnisse_Programmierung_(Arduino_LED_W%C3%BCrfel)#digitalWrite() '''<code>digitalWrite();'''</code>] initialisiert. | Danach werden die Pins mit [https://wiki.hshl.de/wiki/index.php/Grundkenntnisse_Programmierung_(Pulsierende_LED)#pinMode() '''<code>pinMode();</code>'''] und [https://wiki.hshl.de/wiki/index.php/Grundkenntnisse_Programmierung_(Arduino_LED_W%C3%BCrfel)#digitalWrite() '''<code>digitalWrite();'''</code>] initialisiert. | ||
Zeile 92: | Zeile 96: | ||
|- | |- | ||
| | | | ||
<syntaxhighlight lang="C" style="border: none; background-color: #EFF1C1; font-size: | <syntaxhighlight lang="C" style="border: none; background-color: #EFF1C1; font-size:small"> | ||
/* Taster PINS */ | /* Taster PINS */ | ||
const char UP_BUTTON = 2; | const char UP_BUTTON = 2; | ||
Zeile 99: | Zeile 103: | ||
const char DOWN_BUTTON_TWO = 5; | const char DOWN_BUTTON_TWO = 5; | ||
void setup() { | void setup() | ||
/* Taster | { | ||
pinMode(UP_BUTTON, INPUT); | /* Taster initialisieren */ | ||
pinMode(DOWN_BUTTON, INPUT); | pinMode(UP_BUTTON, INPUT); | ||
digitalWrite(UP_BUTTON, HIGH); | pinMode(DOWN_BUTTON, INPUT); | ||
digitalWrite(DOWN_BUTTON, HIGH); | digitalWrite(UP_BUTTON, HIGH); | ||
digitalWrite(DOWN_BUTTON, HIGH); | |||
/* Taster zwei */ | /* Taster zwei */ | ||
pinMode(UP_BUTTON_TWO, INPUT); | pinMode(UP_BUTTON_TWO, INPUT); | ||
pinMode(DOWN_BUTTON_TWO, INPUT); | pinMode(DOWN_BUTTON_TWO, INPUT); | ||
digitalWrite(UP_BUTTON_TWO, HIGH); | digitalWrite(UP_BUTTON_TWO, HIGH); | ||
digitalWrite(DOWN_BUTTON_TWO, HIGH); | digitalWrite(DOWN_BUTTON_TWO, HIGH); | ||
void loop() | |||
{ | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
|} | |} | ||
'''2. | '''2. OLED Display initialisieren''' <br> | ||
Nutzen Sie die [https://wiki.hshl.de/wiki/index.php/Grundkenntnisse_Programmierung_(Arduino_Pong_Spiel)#OLED_Display <code>'''Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);'''</code>] Funktion. | |||
['''Quelltext 3: ''' <code>Pong.ino</code>] | ['''Quelltext 3: ''' <code>Pong.ino</code>] | ||
Zeile 127: | Zeile 132: | ||
|- | |- | ||
| | | | ||
<syntaxhighlight lang="C" style="border: none; background-color: #EFF1C1; font-size: | <syntaxhighlight lang="C" style="border: none; background-color: #EFF1C1; font-size:small"> | ||
/* Deklaration Display, Verbindung zum I2C (SDA, SCL pins) */ | /* Deklaration Display, Verbindung zum I2C (SDA, SCL pins) */ | ||
const char OLED_RESET; // Reset Pin | const char OLED_RESET = 0; // Reset Pin | ||
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); | Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
|} | |} | ||
''' | '''3. Variablen initialisieren''' <br> | ||
Auch können vorab Variablen bestimmt werden, die für die Programmierung benötigt werden.<br> | Auch können vorab Variablen bestimmt werden, die für die Programmierung benötigt werden.<br> | ||
* Die '''Größe des Displays''' und die '''Ball Rate'''<br> | * Die '''Größe des Displays''' und die '''Ball Rate'''<br> | ||
* Zwei Variablen für eine '''Punkteanzeige''' | * Zwei Variablen für eine '''Punkteanzeige''' | ||
* BALL_RATE ist die Rate in Millisekunden, mit der der Ball aktualisiert werden soll. | * BALL_RATE ist die Rate in Millisekunden, mit der der Ball aktualisiert werden soll. | ||
** Höhere Zahl | ** Höhere Zahl: langsamerer Ball | ||
** Niedrigere Zahl | ** Niedrigere Zahl: schnellerer Ball | ||
['''Quelltext 4: ''' <code>Pong.ino</code>] | ['''Quelltext 4: ''' <code>Pong.ino</code>] | ||
Zeile 147: | Zeile 152: | ||
|- | |- | ||
| | | | ||
<syntaxhighlight lang="C" style="border: none; background-color: #EFF1C1; font-size: | <syntaxhighlight lang="C" style="border: none; background-color: #EFF1C1; font-size:small"> | ||
const unsigned char BALL_RATE = 16; | const unsigned char BALL_RATE = 16; | ||
const unsigned char PADDLE_HEIGHT = 25; | const unsigned char PADDLE_HEIGHT = 25; | ||
Zeile 154: | Zeile 159: | ||
const char SCREEN_HEIGHT = 64; // OLED Display Höhe | const char SCREEN_HEIGHT = 64; // OLED Display Höhe | ||
unsigned int | unsigned int playerOne = 0, playerTwo = 0; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
|} | |} | ||
''' | '''4. Position vom Ball, Paddle und Spieler auf dem Display festlegen''' <br> | ||
* Benötigte Postionen sind: '''Ball, Spieler 1 ( | * Benötigte Postionen sind: '''Ball, Spieler 1 (playerOne) und Spieler 2 (playerTwo)'''.<br> | ||
* Benötigt wird eine | * Benötigt wird eine Update-Variable, um den Ball zu bewegen.<br> | ||
* Dieser Werte können nach Geschmack angepasst werden. | * Dieser Werte können nach Geschmack angepasst werden. | ||
Zeile 168: | Zeile 173: | ||
|- | |- | ||
| | | | ||
<syntaxhighlight lang="C" style="border: none; background-color: #EFF1C1; font-size: | <syntaxhighlight lang="C" style="border: none; background-color: #EFF1C1; font-size:small"> | ||
unsigned char | unsigned char ballX = 64, ballY = 32; | ||
unsigned char | unsigned char ballDir_x = 1, ballDir_y = 1; | ||
const unsigned char PLAYER_TWO_X = 12; | const unsigned char PLAYER_TWO_X = 12; | ||
unsigned char | unsigned char playerTwo_y = 16; | ||
const unsigned char | const unsigned char PLAYER_ONE_X = 115; | ||
unsigned char | unsigned char playerOne_y = 16; | ||
unsigned long | unsigned long ballUpdate; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
|} | |} | ||
---- | |||
=== | === Display hochfahren=== | ||
Um später zu sehen, wo | Um später zu sehen, wo Sie welche Objekte platziert hat, muss das Display hochgefahren werden.<br> | ||
Zusätzlich wird beim Start vom Display ein Timer gesetzt, dieser wird für die Bewegung vom Ball benötigt.<br> | Zusätzlich wird beim Start vom Display ein Timer gesetzt, dieser wird für die Bewegung vom Ball benötigt.<br> | ||
'''Benötigt wird: ''' [https://wiki.hshl.de/wiki/index.php/Grundkenntnisse_Programmierung_(Arduino_Pong_Spiel)#OLED_Display <code>'''display.display();'''</code>] und [https://wiki.hshl.de/wiki/index.php/Grundkenntnisse_Programmierung_(Arduino_Pong_Spiel)#millis()<code>'''mills();'''</code>] zum Zeitauslesen. | '''Benötigt wird: ''' [https://wiki.hshl.de/wiki/index.php/Grundkenntnisse_Programmierung_(Arduino_Pong_Spiel)#OLED_Display <code>'''display.display();'''</code>] und [https://wiki.hshl.de/wiki/index.php/Grundkenntnisse_Programmierung_(Arduino_Pong_Spiel)#millis()<code>'''mills();'''</code>] zum Zeitauslesen. | ||
Zeile 193: | Zeile 199: | ||
|- | |- | ||
| | | | ||
<syntaxhighlight lang="C" style="border: none; background-color: #EFF1C1; font-size: | <syntaxhighlight lang="C" style="border: none; background-color: #EFF1C1; font-size:small"> | ||
void setup() { | void setup() | ||
{ | |||
Serial.begin(9600); | |||
/* Hochfahren vom Display */ | /* Hochfahren vom Display */ | ||
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // Starte das Display | |||
display.display(); // Zeigt das Startbild vom Arduino an | |||
unsigned long start = millis(); // Anzahl von Ms zurück, seit Arduino-Board das aktuelle Programm gestartet hat | |||
delay(1000); | |||
display.clearDisplay(); // Löscht das Bild | |||
/* Ball Timer zuweisen*/ | /* Ball Timer zuweisen*/ | ||
ballUpdate = millis(); | |||
delay(1000); | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
|} | |} | ||
''' | ''' Display Spielname anzeigen''' <br> | ||
Als Zusatzaufgabe | Als optionale Zusatzaufgabe können Sie den Namen '''Pong Spiel''' auf dem Display anzeigen lassen.<br> | ||
'''Tipp: ''' Hierfür werden die <code>'''display.set();'''</code> und <code>'''display.println'''();</code> Funktionen benötigt. | '''Tipp: ''' Hierfür werden die <code>'''display.set();'''</code> und <code>'''display.println'''();</code> Funktionen benötigt. | ||
Zeile 221: | Zeile 228: | ||
|- | |- | ||
| | | | ||
<syntaxhighlight lang="C" style="border: none; background-color: #EFF1C1; font-size: | <syntaxhighlight lang="C" style="border: none; background-color: #EFF1C1; font-size:small"> | ||
/* Pong Spiel Namen anzeigen */ | /* Pong Spiel Namen anzeigen */ | ||
display.setTextSize(2); | display.setTextSize(2); | ||
Zeile 232: | Zeile 239: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
|} | |} | ||
---- | |||
=== | === Display Punkteanzeige=== | ||
Um die Punktanzeige ins Spiel mit einzubringen, werden zwei Methoden benötigt.<br> | Um die Punktanzeige ins Spiel mit einzubringen, werden zwei Methoden benötigt.<br> | ||
* Die erste Methode soll die Zahlen auf dem Display anzeigen.<br> | * Die erste Methode soll die Zahlen auf dem Display anzeigen.<br> | ||
Zeile 245: | Zeile 253: | ||
|- | |- | ||
| | | | ||
<syntaxhighlight lang="C" style="border: none; background-color: #EFF1C1; font-size: | <syntaxhighlight lang="C" style="border: none; background-color: #EFF1C1; font-size:small"> | ||
void drawScore() | |||
void drawScore() { | { | ||
display.setTextSize(2); | |||
display.setTextColor(WHITE); | |||
display.setCursor(43, 0); | |||
display.println(playerOne); | |||
display.setCursor(73, 0); | |||
display.println(playerTwo); | |||
} | } | ||
/* Punkte zurücksetzten */ | /* Punkte zurücksetzten */ | ||
void eraseScore() { | void eraseScore() | ||
{ | |||
display.setTextSize(2); | |||
display.setTextColor(BLACK); | |||
display.setCursor(43, 0); | |||
display.println(playerOne); | |||
display.setCursor(73, 0); | |||
display.println(playerTwo); | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
|} | |} | ||
=== | ---- | ||
Damit der Ball sich auf dem Display bewegt, benötigt | |||
=== Bewegter Ball=== | |||
Damit der Ball sich auf dem Display bewegt, benötigt Sie eine '''Rechenfunktion'''.<br> | |||
'''Tipp: ''' | '''Tipp: ''' Rechnen Sie den X und Y Wert + 1 und erstellen Sie eine neue Variable. | ||
['''Quelltext 9: ''' <code>Pong.ino</code>] | ['''Quelltext 9: ''' <code>Pong.ino</code>] | ||
Zeile 280: | Zeile 292: | ||
|- | |- | ||
| | | | ||
<syntaxhighlight lang="C" style="border: none; background-color: #EFF1C1; font-size: | <syntaxhighlight lang="C" style="border: none; background-color: #EFF1C1; font-size:small"> | ||
void loop() { | void loop() { | ||
unsigned long time = millis(); // Zeit-Variable | unsigned long time = millis(); // Zeit-Variable | ||
Zeile 292: | Zeile 304: | ||
|} | |} | ||
''' | '''1. Wände erkennen''' <br> | ||
Damit der Ball wie gewünscht von den Wänden abprallt, müssen die Wände mit einer [https://wiki.hshl.de/wiki/index.php/Grundkenntnisse_Programmierung_(Pulsierende_LED)#if-Bedingung <code>'''if-Bedingung()'''</code>] | Damit der Ball wie gewünscht von den Wänden abprallt, müssen die Wände bei Berührung mit einer [https://wiki.hshl.de/wiki/index.php/Grundkenntnisse_Programmierung_(Pulsierende_LED)#if-Bedingung <code>'''if-Bedingung()'''</code>] abgefragt werden.<br> | ||
* Wenn der Ball auf eine Wand trifft, muss die Richtung vom Ball geändert werden.<br> | * Wenn der Ball auf eine Wand trifft, muss die Richtung vom Ball geändert werden.<br> | ||
* Zusätzlich soll der Spieler | * Zusätzlich soll der Spieler einen Punkt bekommen, wenn die linke oder rechte Wand berührt wurde.<br> | ||
'''Tipp: ''' Wenn die X oder Y Position von der Wand erreicht wurde, | '''Tipp: ''' Wenn die X oder Y Position von der Wand erreicht wurde, negieren Sie den Addierer. | ||
['''Quelltext 10: ''' <code>Pong.ino</code>] | ['''Quelltext 10: ''' <code>Pong.ino</code>] | ||
Zeile 304: | Zeile 316: | ||
|- | |- | ||
| | | | ||
<syntaxhighlight lang="C" style="border: none; background-color: #EFF1C1; font-size: | <syntaxhighlight lang="C" style="border: none; background-color: #EFF1C1; font-size:small"> | ||
/* Checkt ob die vertikalen Wände berührt werden */ | |||
if (New_x == 0) // Wand links | |||
{ | |||
ballDir_x = -ballDir_x; // Wechselt die Richtung | |||
New_x += ballDir_x + ballDir_x; | |||
eraseScore(); // Punkt für Spieler | |||
playerOne += 1; | |||
drawScore(); | |||
New_x = 64; // Ball Reset | |||
New_y = 32; | |||
} | |||
if (New_x == 127) // Wand rechts | |||
{ | |||
ballDir_x = -ballDir_x; // Wechselt die Richtung | |||
New_x += | New_x += ballDir_x + ballDir_x; | ||
eraseScore(); | eraseScore(); // Punkt für Spieler 2 | ||
playerTwo += 1; | |||
drawScore(); | drawScore(); | ||
New_x = 64; // Ball Reset | |||
New_y = 32; | |||
New_x | |||
} | } | ||
// Checkt ob die horizontalen Wände berührt werden | |||
if (New_y == 0 || New_y == 63) | |||
{ | |||
ballDir_y = -ballDir_y; // Wechselt die Richtung | |||
New_y += ballDir_y + ballDir_y; | |||
} | |||
</syntaxhighlight> | </syntaxhighlight> | ||
|} | |} | ||
''' | '''2. Paddel erkennen''' <br> | ||
Auch muss der Ball, wenn er den Schläger berührt, seine Richtung ändern.<br> | Auch muss der Ball, wenn er den Schläger berührt, seine Richtung ändern.<br> | ||
'''Tipp: ''' | '''Tipp: ''' Die Position von den Paddeln muss kleiner sein als die Position vom Ball. | ||
['''Quelltext 11: ''' <code>Pong.ino</code>] | ['''Quelltext 11: ''' <code>Pong.ino</code>] | ||
Zeile 348: | Zeile 359: | ||
|- | |- | ||
| | | | ||
<syntaxhighlight lang="C" style="border: none; background-color: #EFF1C1; font-size: | <syntaxhighlight lang="C" style="border: none; background-color: #EFF1C1; font-size:small"> | ||
/ | // Checkt, ob der Spieler 1 Paddel getroffen wurde | ||
if (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 wurde | ||
if (New_x == | if (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; | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
|} | |} | ||
''' | '''3. Neue Ballposition anzeigen''' <br> | ||
Nun kann sich der Ball bewegen, muss aber noch auf dem Display als Pixel angezeigt | Nun kann sich der Ball bewegen, muss aber noch auf dem Display als Pixel angezeigt und die alten Pixel wieder gelöscht werden.<br> | ||
Außerdem müssen die neuen Variablen übertragen werden auf die alten, um weitere Abläufe zu ermöglichen.<br> | |||
'''Tipp: '''Nutze <code>'''display.drawPixel(Name, Name, BLACK/WHITE);'''</code>. | '''Tipp: '''Nutze <code>'''display.drawPixel(Name, Name, BLACK/WHITE);'''</code>. | ||
Zeile 374: | Zeile 386: | ||
|- | |- | ||
| | | | ||
<syntaxhighlight lang="C" style="border: none; background-color: #EFF1C1; font-size: | <syntaxhighlight lang="C" style="border: none; background-color: #EFF1C1; font-size:small"> | ||
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(); | |||
} | |||
</syntaxhighlight> | </syntaxhighlight> | ||
|} | |} | ||
=== | ----- | ||
=== Boolean Taster=== | |||
Um die Taster abzufragen und falsche Eingaben zu überprüfen, wird mit einem [https://wiki.hshl.de/wiki/index.php/Grundkenntnisse_Programmierung_(Arduino_Pong_Spiel)#bool <code>'''bool'''</code>] programmiert.<br> | |||
'''Tipp: ''' | '''Tipp: ''' | ||
# Neue Variablen für die Taster mit <code>'''bool'''</code> initialisieren. | # Neue Variablen für die Taster mit <code>'''bool'''</code> initialisieren. | ||
# Die alten und neuen Variablen auf <code>'''LOW'''</code> setzen, um die Taster schalten zu können. | # Die alten und neuen Variablen auf <code>'''LOW'''</code> zu setzen, um die Taster schalten zu können. | ||
# Wenn die <code>'''bool'''</code> <code>'''Variable = true'''</code> ist, soll das Display | # Wenn die <code>'''bool'''</code> <code>'''Variable = true'''</code> ist, soll das Display mit <code>'''display.display();'''</code> aktualisiert werden. | ||
['''Quelltext 13: ''' <code>Pong.ino</code>] | ['''Quelltext 13: ''' <code>Pong.ino</code>] | ||
Zeile 399: | Zeile 413: | ||
|- | |- | ||
| | | | ||
<syntaxhighlight lang="C" style="border: none; background-color: #EFF1C1; font-size: | <syntaxhighlight lang="C" style="border: none; background-color: #EFF1C1; font-size:small"> | ||
bool update = false; // Ein bool enthält einen von zwei Werten, true oder false | |||
bool update = false; // Ein bool enthält einen von zwei Werten, true oder false | unsigned long time = millis(); // Zeit-Variable | ||
static bool UP_STATE = false; | |||
static bool DOWN_STATE = false; | |||
static bool UP_STATE_TWO = false; | |||
static bool DOWN_STATE_TWO = false; | |||
UP_STATE |= (digitalRead(UP_BUTTON) == LOW); // Taster auslesen | |||
DOWN_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ührt | if (update) // Wenn Variable = true wird display.display(); ausgeführt | ||
display.display(); | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
|} | |} | ||
=== | ---- | ||
=== Paddel Spieler=== | |||
Der letzte Schritt ist, die Schläger mit den Tastern steuerbar zu machen.<br> | Der letzte Schritt ist, die Schläger mit den Tastern steuerbar zu machen.<br> | ||
Diese dürfen aber nicht aus dem Display gelangen.<br> | Diese dürfen aber nicht aus dem Display gelangen.<br> | ||
'''Tipp: '''<br> | '''Tipp: '''<br> | ||
# Der Ablauf ist ähnlich wie beim Ball, nur | # 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 | # Zusätzlich müssen noch die Schläger auf dem Display mit der Funktion <br> <code>'''display.drawFastVLine(PLAYER_ONE_X, playerOne_y, PADDLE_HEIGHT, BLACK);'''</code> angezeigt werden. | ||
['''Quelltext 14: ''' <code>Pong.ino</code>] | ['''Quelltext 14: ''' <code>Pong.ino</code>] | ||
Zeile 433: | Zeile 448: | ||
|- | |- | ||
| | | | ||
<syntaxhighlight lang="C" style="border: none; background-color: #EFF1C1; font-size: | <syntaxhighlight lang="C" style="border: none; background-color: #EFF1C1; font-size:small"> | ||
/* Spieler 1 Paddel */ | /* Spieler 1 Paddel */ | ||
display.drawFastVLine( | display.drawFastVLine(PLAYER_ONE_X, playerOne_y, PADDLE_HEIGHT, BLACK); | ||
if ( | if (UP_STATE) | ||
{ | |||
playerOne_y -= 1; | |||
} | } | ||
if ( | if (DOWN_STATE) | ||
{ | |||
playerOne_y += 1; | |||
} | } | ||
UP_STATE = DOWN_STATE = false; | |||
if (playerOne_y < 1) playerOne_y = 1; // Um nicht aus dem Bildschirm zu gelangen | |||
if (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 */ | /* Spieler 2 Paddel */ | ||
display.drawFastVLine( | display.drawFastVLine(PLAYER_TWO_X, playerTwo_y, PADDLE_HEIGHT, BLACK); | ||
if ( | if (UP_STATE_TWO) | ||
{ | |||
playerTwo_y -= 1; // Paddle 1 nach unten | |||
} | } | ||
if ( | if (DOWN_STATE_TWO) | ||
{ | |||
playerTwo_y += 1; // Paddle 1 nach oben | |||
} | } | ||
UP_STATE_TWO = DOWN_STATE_TWO = false; | |||
if ( | if (playerTwo_y < 1) playerTwo_y = 1; // Um nicht aus dem Bildschirm zugelangen | ||
if ( | if (playerTwo_y + PADDLE_HEIGHT > 63) playerTwo_y = 63 - PADDLE_HEIGHT; | ||
display.drawFastVLine( | display.drawFastVLine(PLAYER_TWO_X, playerTwo_y, PADDLE_HEIGHT, WHITE); | ||
if (update) //hierhin verschieben | |||
display.display(); | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
|} | |} | ||
==Musterlösung== | == Musterlösung == | ||
{| role="presentation" class="wikitable mw-collapsible mw-collapsed" | {| role="presentation" class="wikitable mw-collapsible mw-collapsed" | ||
| <strong>Lösung Code  </strong> | | <strong>Lösung Code  </strong> | ||
|- | |- | ||
| | | | ||
<syntaxhighlight lang="C" style="border: none; background-color: #EFF1C1; font-size: | <syntaxhighlight lang="C" style="border: none; background-color: #EFF1C1; font-size:small"> | ||
/* Benötigte Bibliotheken */ | /* Benötigte Bibliotheken */ | ||
#include <SPI.h> | #include <SPI.h> | ||
Zeile 493: | Zeile 514: | ||
const char SCREEN_HEIGHT = 64; // OLED Display Höhe | const char SCREEN_HEIGHT = 64; // OLED Display Höhe | ||
unsigned int | unsigned int playerOne = 0, playerTwo = 0; | ||
/* Deklaration Display, verbindung zum I2C (SDA, SCL pins) */ | /* Deklaration Display, verbindung zum I2C (SDA, SCL pins) */ | ||
Zeile 502: | Zeile 523: | ||
/* Position vom Ball, Paddle und Spieler auf dem Display */ | /* Position vom Ball, Paddle und Spieler auf dem Display */ | ||
unsigned char | unsigned char ballX = 64, ballY = 32; | ||
unsigned char | unsigned char ballDir_x = 1, ballDir_y = 1; | ||
const unsigned char PLAYER_TWO_X = 12; | const unsigned char PLAYER_TWO_X = 12; | ||
unsigned char | unsigned char playerTwo_y = 16; | ||
const unsigned char | const unsigned char PLAYER_ONE_X = 115; | ||
unsigned char | unsigned char playerOne_y = 16; | ||
unsigned long | unsigned long ballUpdate; | ||
void setup() { | void setup() | ||
{ | |||
Serial.begin(9600); | |||
/* Hochfahren vom Display */ | |||
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // Starte das Display | |||
display.display(); // Zeigt das Startbild vom Arduino an | |||
unsigned long start = millis(); // Anzahl von Ms zurück, seit Arduino-Board das aktuelle Programm gestartet hat | |||
delay(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 festlegen | |||
pinMode(DOWN_BUTTON, INPUT); | |||
digitalWrite(UP_BUTTON, HIGH); // Taster anschalten | |||
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); | |||
} | } | ||
/* Punkteanzeige */ | /* Punkteanzeige */ | ||
void drawScore() { | void drawScore() | ||
{ | |||
display.setTextSize(2); | |||
display.setTextColor(WHITE); | |||
display.setCursor(43, 0); | |||
display.println(playerOne); | |||
display.setCursor(73, 0); | |||
display.println(playerTwo); | |||
} | } | ||
/* Punkte zurücksetzten */ | /* Punkte zurücksetzten */ | ||
void eraseScore() { | void eraseScore() | ||
{ | |||
display.setTextSize(2); | |||
display.setTextColor(BLACK); | |||
display.setCursor(43, 0); | |||
display.println(playerOne); | |||
display.setCursor(73, 0); | |||
display.println(playerTwo); | |||
} | } | ||
void loop() | |||
{ | |||
drawScore(); | |||
bool update = false; // Ein bool enthält einen von zwei Werten, true oder false | |||
unsigned long time = millis(); // Zeit-Variable | |||
static bool UP_STATE = false; | |||
static bool DOWN_STATE = false; | |||
static bool UP_STATE_TWO = false; | |||
static bool DOWN_STATE_TWO = false; | |||
UP_STATE |= (digitalRead(UP_BUTTON) == LOW); // Taster auslesen | |||
DOWN_STATE |= (digitalRead(DOWN_BUTTON) == LOW); | |||
UP_STATE_TWO |= (digitalRead(UP_BUTTON_TWO) == LOW); | |||
DOWN_STATE_TWO |= (digitalRead(DOWN_BUTTON_TWO) == LOW); | |||
/* Neue Ballposition */ | /* Neue Ballposition */ | ||
if (time > | if (time > ballUpdate) | ||
{ | |||
unsigned char New_x = ballX + ballDir_x; // (x+1) | |||
unsigned char New_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 Richtung | |||
New_x += ballDir_x + ballDir_x; | |||
eraseScore(); // Punkt für Spieler | |||
playerOne += 1; | |||
drawScore(); | |||
New_x = 64; // Ball Reset | |||
New_y = 32; | |||
} | |||
if (New_x == 127) // Wand rechts | |||
{ | |||
ballDir_x = -ballDir_x; // Wechselt die Richtung | |||
New_x += | New_x += ballDir_x + ballDir_x; | ||
eraseScore(); // Punkt für | eraseScore(); // Punkt für Spieler 2 | ||
playerTwo += 1; | |||
drawScore(); | drawScore(); | ||
New_x = 64; // Ball Reset | |||
New_y = 32; | |||
} | } | ||
// Checkt ob die horizontalen Wände berührt werden | |||
if (New_y == 0 || New_y == 63) | |||
{ | |||
ballDir_y = -ballDir_y; // Wechselt die Richtung | |||
New_y += ballDir_y + ballDir_y; | |||
} | |||
// Checkt, ob der Spieler 1 Paddel getroffen wurde | |||
if (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; | |||
} | |||
display.drawPixel( | // Checkt, ob der Spieler 2 Paddel getroffen wurde | ||
if (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); | display.drawPixel(New_x, New_y, WHITE); | ||
ballX = New_x; | |||
ballY = New_y; | |||
ballUpdate += BALL_RATE; | |||
update = true; // true ~ display.display(); | update = true; // true ~ display.display(); | ||
} | } | ||
/* Spieler 1 Paddel */ | /* Spieler 1 Paddel */ | ||
display.drawFastVLine( | display.drawFastVLine(PLAYER_ONE_X, playerOne_y, PADDLE_HEIGHT, BLACK); | ||
if ( | if (UP_STATE) | ||
{ | |||
playerOne_y -= 1; | |||
} | } | ||
if ( | if (DOWN_STATE) | ||
{ | |||
playerOne_y += 1; | |||
} | } | ||
UP_STATE = DOWN_STATE = false; | |||
if (playerOne_y < 1) playerOne_y = 1; // Um nicht aus dem Bildschirm zu gelangen | |||
if (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 */ | /* Spieler 2 Paddel */ | ||
display.drawFastVLine(PLAYER_TWO_X, | display.drawFastVLine(PLAYER_TWO_X, playerTwo_y, PADDLE_HEIGHT, BLACK); | ||
if ( | if (UP_STATE_TWO) | ||
{ | |||
playerTwo_y -= 1; // Paddle 1 nach unten | |||
} | } | ||
if ( | if (DOWN_STATE_TWO) | ||
{ | |||
playerTwo_y += 1; // Paddle 1 nach oben | |||
} | } | ||
UP_STATE_TWO = DOWN_STATE_TWO = false; | |||
if ( | if (playerTwo_y < 1) playerTwo_y = 1; // Um nicht aus dem Bildschirm zugelangen | ||
if ( | if (playerTwo_y + PADDLE_HEIGHT > 63) playerTwo_y = 63 - PADDLE_HEIGHT; | ||
display.drawFastVLine(PLAYER_TWO_X, | display.drawFastVLine(PLAYER_TWO_X, playerTwo_y, PADDLE_HEIGHT, WHITE); | ||
if (update) | if (update) | ||
display.display(); | |||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
|} | |} | ||
[https://github.com/shveytank/Arduino_Pong_Game Quelle] | |||
<br> | <br> | ||
---- | ---- | ||
'''→ zurück zum Hauptartikel: [[Konzipierung_und_Evaluierung_von_Arduino-Projekten_verschiedener_Schwierigkeitsgrade_für_die_Lehre | BA: Arduino-Projekte für die Lehre]]''' | '''→ zurück zum Hauptartikel: [[Konzipierung_und_Evaluierung_von_Arduino-Projekten_verschiedener_Schwierigkeitsgrade_für_die_Lehre | BA: Arduino-Projekte für die Lehre]]''' |
Aktuelle Version vom 12. September 2023, 07:10 Uhr
Autor: Justin Frommberger
Aufgabenstellung
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]
[Abb. 2: UML]
Benötigte Materiallien
Nr. | Anz. | Beschreibung | Bild |
---|---|---|---|
① | 1 | Funduino Arduino UNO R3 | |
② | 1 | Typ 2 | |
③ | 14+ | Jumperkabel, männlich/männlich | |
④ | 1 | Steckbrett | |
⑤ | 4 | Taster | |
⑥ | 1 | 0.96 I2C OLED Display |
Vorab wichtig zu wissen!
OLED Display:
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.
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
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.
Programmierung
Es ist wichtig, die [Programmierrichtlinien] beim Programmieren einzuhalten.
Wenn Sie Fragen zur Programmierung haben, finden Sie die Antworten in den [Grundkenntnissen].
Bibliotheken einfügen
Downloaden Sie die Bibliotheken für das OLED Display [SSD10306] und [GFX].
Benötigt wird:
- Kommunikation zum I2C/TWI Gerät:
Wire.h
- Kommunikation mit dem SPI Gerät:
SPI.h
- Adafruit GFX Grafik:
Adafruit_GFX.h
- 128x64 and 128x32 OLEDs Displays:
Adafruit_SSD1306.h
[Quelltext 1: Pong.ino
]
Lösung |
// Benötigte Bibliotheken
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
|
Initialisierung Arduino
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 */
const char UP_BUTTON = 2;
const char DOWN_BUTTON = 3;
const char UP_BUTTON_TWO = 4;
const char DOWN_BUTTON_TWO = 5;
void setup()
{
/* 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);
void loop()
{
}
|
2. OLED Display initialisieren
Nutzen Sie die Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
Funktion.
[Quelltext 3: Pong.ino
]
Lösung |
/* Deklaration Display, Verbindung zum I2C (SDA, SCL pins) */
const char OLED_RESET = 0; // Reset Pin
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
|
3. Variablen initialisieren
Auch können vorab Variablen bestimmt werden, die für die Programmierung benötigt werden.
- Die Größe des Displays und die Ball Rate
- Zwei Variablen für eine Punkteanzeige
- BALL_RATE ist die Rate in Millisekunden, mit der der Ball aktualisiert werden soll.
- Höhere Zahl: langsamerer Ball
- Niedrigere Zahl: schnellerer Ball
[Quelltext 4: Pong.ino
]
Lösung |
const unsigned char BALL_RATE = 16;
const unsigned char PADDLE_HEIGHT = 25;
const char SCREEN_WIDTH = 128; // OLED Display Breite
const char SCREEN_HEIGHT = 64; // OLED Display Höhe
unsigned int playerOne = 0, playerTwo = 0;
|
4. Position vom Ball, Paddle und Spieler auf dem Display festlegen
- Benötigte Postionen sind: Ball, Spieler 1 (playerOne) und Spieler 2 (playerTwo).
- Benötigt wird eine Update-Variable, um den Ball zu bewegen.
- Dieser Werte können nach Geschmack angepasst werden.
[Quelltext 5: Pong.ino
]
Lösung |
unsigned char ballX = 64, ballY = 32;
unsigned char ballDir_x = 1, ballDir_y = 1;
const unsigned char PLAYER_TWO_X = 12;
unsigned char playerTwo_y = 16;
const unsigned char PLAYER_ONE_X = 115;
unsigned char playerOne_y = 16;
unsigned long ballUpdate;
|
Display hochfahren
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 |
void setup()
{
Serial.begin(9600);
/* Hochfahren vom Display */
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // Starte das Display
display.display(); // Zeigt das Startbild vom Arduino an
unsigned long start = millis(); // Anzahl von Ms zurück, seit Arduino-Board das aktuelle Programm gestartet hat
delay(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 |
void drawScore()
{
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(43, 0);
display.println(playerOne);
display.setCursor(73, 0);
display.println(playerTwo);
}
/* Punkte zurücksetzten */
void eraseScore()
{
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 |
void loop() {
unsigned long time = millis(); // Zeit-Variable
/* Neue Ballposition */
if (time > Ball_update) {
unsigned char New_x = Ball_x + Ball_dir_x; // (x+1)
unsigned char New_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 Richtung
New_x += ballDir_x + ballDir_x;
eraseScore(); // Punkt für Spieler
playerOne += 1;
drawScore();
New_x = 64; // Ball Reset
New_y = 32;
}
if (New_x == 127) // Wand rechts
{
ballDir_x = -ballDir_x; // Wechselt die Richtung
New_x += ballDir_x + ballDir_x;
eraseScore(); // Punkt für Spieler 2
playerTwo += 1;
drawScore();
New_x = 64; // Ball Reset
New_y = 32;
}
// Checkt ob die horizontalen Wände berührt werden
if (New_y == 0 || New_y == 63)
{
ballDir_y = -ballDir_y; // Wechselt die Richtung
New_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 wurde
if (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 wurde
if (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.
Tipp: Nutze display.drawPixel(Name, Name, BLACK/WHITE);
.
[Quelltext 12: Pong.ino
]
Lösung |
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();
}
|
Boolean Taster
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
bool
Variable = true
ist, soll das Display mitdisplay.display();
aktualisiert werden.
[Quelltext 13: Pong.ino
]
Lösung |
bool update = false; // Ein bool enthält einen von zwei Werten, true oder false
unsigned long time = millis(); // Zeit-Variable
static bool UP_STATE = false;
static bool DOWN_STATE = false;
static bool UP_STATE_TWO = false;
static bool DOWN_STATE_TWO = false;
UP_STATE |= (digitalRead(UP_BUTTON) == LOW); // Taster auslesen
DOWN_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ührt
display.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 gelangen
if (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 zugelangen
if (playerTwo_y + PADDLE_HEIGHT > 63) playerTwo_y = 63 - PADDLE_HEIGHT;
display.drawFastVLine(PLAYER_TWO_X, playerTwo_y, PADDLE_HEIGHT, WHITE);
if (update) //hierhin verschieben
display.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 */
const char UP_BUTTON = 2;
const char DOWN_BUTTON = 3;
const char UP_BUTTON_TWO = 4;
const char DOWN_BUTTON_TWO = 5;
/* Variablen */
const unsigned char BALL_RATE = 16;
const unsigned char PADDLE_HEIGHT = 25;
const char SCREEN_WIDTH = 128; // OLED Display Breite
const char SCREEN_HEIGHT = 64; // OLED Display Höhe
unsigned int playerOne = 0, playerTwo = 0;
/* Deklaration Display, verbindung zum I2C (SDA, SCL pins) */
const char OLED_RESET = 0; // Reset pin
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
/* Position vom Ball, Paddle und Spieler auf dem Display */
unsigned char ballX = 64, ballY = 32;
unsigned char ballDir_x = 1, ballDir_y = 1;
const unsigned char PLAYER_TWO_X = 12;
unsigned char playerTwo_y = 16;
const unsigned char PLAYER_ONE_X = 115;
unsigned char playerOne_y = 16;
unsigned long ballUpdate;
void setup()
{
Serial.begin(9600);
/* Hochfahren vom Display */
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // Starte das Display
display.display(); // Zeigt das Startbild vom Arduino an
unsigned long start = millis(); // Anzahl von Ms zurück, seit Arduino-Board das aktuelle Programm gestartet hat
delay(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 festlegen
pinMode(DOWN_BUTTON, INPUT);
digitalWrite(UP_BUTTON, HIGH); // Taster anschalten
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);
}
/* Punkteanzeige */
void drawScore()
{
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(43, 0);
display.println(playerOne);
display.setCursor(73, 0);
display.println(playerTwo);
}
/* Punkte zurücksetzten */
void eraseScore()
{
display.setTextSize(2);
display.setTextColor(BLACK);
display.setCursor(43, 0);
display.println(playerOne);
display.setCursor(73, 0);
display.println(playerTwo);
}
void loop()
{
drawScore();
bool update = false; // Ein bool enthält einen von zwei Werten, true oder false
unsigned long time = millis(); // Zeit-Variable
static bool UP_STATE = false;
static bool DOWN_STATE = false;
static bool UP_STATE_TWO = false;
static bool DOWN_STATE_TWO = false;
UP_STATE |= (digitalRead(UP_BUTTON) == LOW); // Taster auslesen
DOWN_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)
{
unsigned char New_x = ballX + ballDir_x; // (x+1)
unsigned char New_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 Richtung
New_x += ballDir_x + ballDir_x;
eraseScore(); // Punkt für Spieler
playerOne += 1;
drawScore();
New_x = 64; // Ball Reset
New_y = 32;
}
if (New_x == 127) // Wand rechts
{
ballDir_x = -ballDir_x; // Wechselt die Richtung
New_x += ballDir_x + ballDir_x;
eraseScore(); // Punkt für Spieler 2
playerTwo += 1;
drawScore();
New_x = 64; // Ball Reset
New_y = 32;
}
// Checkt ob die horizontalen Wände berührt werden
if (New_y == 0 || New_y == 63)
{
ballDir_y = -ballDir_y; // Wechselt die Richtung
New_y += ballDir_y + ballDir_y;
}
// Checkt, ob der Spieler 1 Paddel getroffen wurde
if (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 wurde
if (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 gelangen
if (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 zugelangen
if (playerTwo_y + PADDLE_HEIGHT > 63) playerTwo_y = 63 - PADDLE_HEIGHT;
display.drawFastVLine(PLAYER_TWO_X, playerTwo_y, PADDLE_HEIGHT, WHITE);
if (update)
display.display();
}
|
→ zurück zum Hauptartikel: BA: Arduino-Projekte für die Lehre