Steuerpult


Google-Suche auf MEINE-SCHALTUNG.de :





Online Rechner

Chronik

Dauerkalender


Pong für zwei Spieler
mit Arduino Mega und 64x64-Matrix

Das Spiel Pong ist keine Neuheit, man könnte auf die Beschreibung der Regeln praktisch verzichten. Das Spiel ähnelt dem Tennis-Spiel und heißt hier „für zwei Spieler“, da wir mit einer 1-Spieler-Version bereits experimentiert haben. Damals ging es speziell um Joystick-Ansteuerung, die mit Arduino Uno realisiert wurde. Das Beispiel ist hier zu finden:

Pong (1 Spieler)


Startbild

In dem folgenden Experiment kommt eine größere Matrix (64x64 Pixel) zum Einsatz. Die Matrix-Steuerung übernimmt Arduino Mega. Die dazugehörige Schaltung ist äußerst einfach. Neben den zwei bereits erwähnten Komponenten nehmen an der Schaltung noch zwei Potis und ein Taster teil. Das ist schon alles.
Für diejenigen, die mit Pong doch noch nicht in Berührung gekommen sind, kurz die Regeln. Über das Spielfeld bewegt sich ein Ball, der durch einen einzelnen Pixel symbolisiert wird. Jeder Spieler verfügt über einen Schläger, den er rauf und runter bewegen kann. Die Schläger werden durch 6-Pixel Linien symbolisiert und befinden sich auf den gegenüberliegenden Spielfeldseiten. Immer wenn der Ball die Spielfeldseite eines Spielers erreicht, muss er versuchen, den Ball mit dem Schläger zurückzuschlagen. Gelingt ihm das nicht, bekommt der Gegner einen Punkt. In unserem Fall gewinnt der Spieler, der als erster 50 Punkte einsammelt.

Schaltplan

Schaltplan



Testschaltung

Testschaltung



Programm (Sketch)

// ************************************************************************************************
// Pong für zwei Spieler
// Eine Schaltung mit Arduino Mega und Matrix 64x64
// Arduino IDE 2.3.5
// ************************************************************************************************

#include "RGBmatrixPanel.h"                                     // Bibliothek Einbindung

#define CLK 11 
#define OE   9
#define LAT 10
#define A   A0
#define B   A1
#define C   A2
#define D   A3
#define E   A4

RGBmatrixPanel matrix(A, B, C, D, E, CLK, LAT, OE, false, 64);

unsigned long Speed_Time = 30;                                  // Tempo Bewegung Ball
unsigned long Millis_Alt;                                       // Zeit festhalten
unsigned long Millis_Aktuell;                                   // Zeit aktuell
int Schlaeger_Soll_Pos [2];                                     // Schläger Y-Pos
int Pos_Alt [2];                                                // Alte Schläger Y-Pos
int Richtung = 2;                                               // Bewegungsrichtung des Balles
                                                                // Nächste X/Y-Position  
int X_Datensatz [14] = {  1,  1,  2,  1,  2,  1,  1, -1, -1, -2, -1, -2, -1, -1 };
int Y_Datensatz [14] = { -2, -1, -1,  0,  1,  1,  2,  2,  1,  1,  0, -1, -1, -2 };
int X = 32,Y = 20;                                              // Ball Koordinaten
int Analoge_Werte [2][10];                                      // Poti Werte
int Mittelwert [2];                                             // Poti Mittelwerte
int Zufall_Richtung;                                            // Zufallszahl -1/0/1
int Start_Stop = 8;                                             // Taster Start / Stop
int Punkte [2];                                                 // Punkte aktuell
int Punkte_Alt [2];                                             // Alte Punktzahlen
bool Spiel_laeuft;                                              // Merker
 
// ************************************************************************************************

void setup() {

    matrix.begin();
    delay(500);
    matrix.fillRect(10, 57, 40, 7, matrix.Color333(0, 0, 0));   // Matrix löschen
    matrix.drawRect(0, 0, 64, 57, matrix.Color333(0, 0, 7));    // Spielfeld markieren
    matrix.setTextSize(1);                                      // Textgröße Punkteanzeige
    pinMode(Start_Stop, INPUT_PULLUP);                          // Taster Spiel Start / Stop
    Pos_Alt [0] = 25;                                           // Y Vorbelegung
    Pos_Alt [1] = 25;
}
// ************************************************************************************************

void loop()  {  
                                                                // Spiel starten oder stoppen
    if (digitalRead (Start_Stop) == LOW or Punkte [0] == 50 or Punkte [1] == 50) {
        Spiel_laeuft = not Spiel_laeuft;
        if (Spiel_laeuft) {
            matrix.fillRect(0, 57, 64, 7, matrix.Color333(0, 0, 0));
            matrix.fillRect(0, 57, 2, 7, matrix.Color333(7, 0, 0));
            Punkte_Anzeige ();            
        } else {
            matrix.fillRect(0, 57, 2, 7, matrix.Color333(0, 0, 0));
        }
        Punkte [0] = 0;
        Punkte [1] = 0;
        delay (1000);
    }
    
    Analoges_Mittelwert ();                                     // Potiwerte ermitteln
    Schlaeger_bewegen ();                                       // balken bewegen

    Millis_Aktuell = millis();                                  // Zeit für Tempo der Ballbewegung
    if ((Millis_Aktuell - Millis_Alt) > Speed_Time) {
        Millis_Alt = Millis_Aktuell;
                                                                // Ball Bewegung anzeigen
        if ((Y > 0 && Y < 56 && X < 61 && X > 2)) {             // Rand nicht bechreiben
           matrix.drawPixel(X, Y, matrix.Color333(0, 0, 0));    // Alte Position löschen
        }
        X = X + X_Datensatz [Richtung];                         // Neue Koordinaten
        Y = Y + Y_Datensatz [Richtung];
        if ((Y > 0 && Y < 56 && X < 61 && X > 2)) {             // Rand nicht bechreiben
           matrix.drawPixel(X, Y, matrix.Color333(0, 7, 0));    // Ball anzeigen
        }    
    
        if ((Y < 2) or (Y > 55)) {                              // Rand Oben/Unten erreicht?
            if (Richtung < 7) {                                 // Bewegung von Links nach Rechts
                Richtung = 6 - Richtung;                        // Neue Richtung
            }
            if (Richtung > 6) {                                 // Bewegung von Rechts nach Links
                Richtung = 7 - Richtung + 13;                   // Neue Richtung
            }
        }

        Zufallszahl:
            int Z = random (-1,2);                              // Richtung Faktor via Zufall
            if (Z == Zufall_Richtung) {
                goto Zufallszahl;
            } 
        Zufall_Richtung = Z;
        Punkte_Alt [0] = Punkte [0];
        Punkte_Alt [1] = Punkte [1];

        if (X > 59) {                                           // Rechte Feldgrenze erreicht?
            Richtung = 13 - Richtung;                           // Folgerichtung Grundregel
            Punkte [0]++;                                       // Punkt für LINKS
            if (X == 61) { X = 60;}
            
                                                                // Schläger getroffen ?
            if (Y >= Schlaeger_Soll_Pos [1] && Y <= Schlaeger_Soll_Pos [1] + 5) {  
                Richtung = Richtung + Zufall_Richtung;          // Folgerichtung bestimmen
                if (Richtung > 13) { Richtung = 11; }           // Richtung Grenzen
                if (Richtung < 7) { Richtung = 9; }
                Punkte [0]--;                                   // Kein Punktverlust
            }
            Punkte_Anzeige ();
        }

        if (X < 4) {                                            // Linke Feldgrenze erreicht?
            Richtung = 13 - Richtung;
            Punkte [1]++;                                       // Punkt für RECHTS
            if (X == 2) { X = 3;}
                                                                // Schläger getroffen ?
            if (Y >= Schlaeger_Soll_Pos [0] && Y <= Schlaeger_Soll_Pos [0] + 5) {  
                Richtung = Richtung + Zufall_Richtung;          // Richtung bestimmen
                if (Richtung < 0) { Richtung = 2; }             // Grenzwerte
                if (Richtung > 6) { Richtung = 4; }
                Punkte [1]--;                                   // Kein Punktverlust
            }
            Punkte_Anzeige ();
        }    
    }
}
// ************************************************************************************************

void Punkte_Anzeige () {

    if (Spiel_laeuft) {
        matrix.setCursor(11, 57);                               // Cursor neue Position
        matrix.setTextColor(matrix.Color333(0,0,0));            // Textfarbe zum Löschen
        String x1 = String (Punkte_Alt [0]);
        String x2 = String (Punkte_Alt [1]);
        matrix.println(x1 + " - " + x2);                        // Punkte Anzeige löschen

        matrix.setCursor(11, 57);                               // Cursor neue Position
        matrix.setTextColor(matrix.Color333(0,7,0));            // Textfarbe zum Löschen
        x1 = String (Punkte [0]);
        x2 = String (Punkte [1]);    
        matrix.println(x1 + " - " + x2);                        // Punkte aktuell anzeigen
    } else {
        Punkte [0] = Punkte [1] = 0;
    }
}
// ************************************************************************************************

void Schlaeger_bewegen () {
                                                
    for (int i=0; i<2; i++) {                                   // X,Y Koordinaten 
        int PosX, PosY;
        if (i == 0) {                                           // Schläger LINKS
            PosX = 2;                                           // X-Koordinate Links
        } 
        if (i == 1) {                                           // Schläger RECHTS
            PosX = 61;                                          // X-Koordinate Rechts
        }
        if ( Schlaeger_Soll_Pos [i] != Pos_Alt [i]) { 
            PosY = Pos_Alt [i];
            matrix.drawLine(PosX, PosY, PosX, PosY + 5, matrix.Color333(0, 0, 0));  // Löschen
            PosY = Schlaeger_Soll_Pos [i];
            matrix.drawLine(PosX, PosY, PosX, PosY + 5, matrix.Color333(7, 7, 7));  // Zeichnen
            Pos_Alt [i] = Schlaeger_Soll_Pos [i];               // Alte Position merken
        }
    }
}
// ************************************************************************************************ 

void Analoges_Mittelwert () {

    for (int i=0; i<9; i++) {                                   // 10 Werte für Mittelwert
        Analoge_Werte [0][i] = Analoge_Werte [0][i+1];          // Werte in Tabelle schieben
        Analoge_Werte [1][i] = Analoge_Werte [1][i+1];
    }
    Analoge_Werte [0][9] = analogRead(A5);                      // Wert Poti Links auslesen  
    Analoge_Werte [1][9] = analogRead(A6);                      // Wert Poti Rechts auslesen    

    for (int i=0; i<10; i++) {                                  // Mittelwerte ermitteln
        Mittelwert [0] = Mittelwert [0] + Analoge_Werte [0][i];
        Mittelwert [1] = Mittelwert [1] + Analoge_Werte [1][i];
    }   
    Mittelwert [0] = Mittelwert [0] / 10;
    Mittelwert [1] = Mittelwert [1] / 10;

    Schlaeger_Soll_Pos [0] = ((Mittelwert [0] * 10) / 230) + 1; // Neue Schläger Position berechnen
    Schlaeger_Soll_Pos [1] = ((Mittelwert [1] * 10) / 230) + 1;
}
// ************************************************************************************************        


Für die Bewegung der Schläger sind zwei Potis zuständig. Mithilfe von diesen Potis können die Spieler ihre Schläger hin und her bewegen. Um das zu realisieren, werden dauernd zwei analoge Eingänge ausgelesen und abhängig von dem ausgelesenen Wert die Y-Koordinate eines Schlägers bestimmt. Da sich die Schläger jeweils auf einer Seite des Spielfeldes bewegen, bleiben die X-Koordinaten unverändert. Weil die anlogen Werte nicht immer stabil bleiben, wird aus den letzten 10 analogen Werten ein Mittelwert errechnet. Für die Bewegung des Balles sind zwei weitere XY-Koordinaten notwendig, die dauernd neu berechnet werden müssen. Um das zu vereinfachen, werden bestimmte Richtungen festgelegt, die der Ball annehmen kann. Diese Zuordnung wird auf der folgenden Abbildung dargestellt:

Ball-Richtungen


Wenn sich z.B. der Ball gerade in Richtung 2 bewegt, werden seine Folgekoordinate nach der Formel X(next)=X+2, Y(next)=Y-1 berechnet. Die Berechnungen finden in 50 ms-Takt, womit das Tempo der Bewegung bestimmt wird. Erreicht der Ball in dem Fall den oberen Rand des Spielfeldes, wird seine neue Richtung nach der Formel „Richtung = 6 – Richtung“ berechnet. Seine neue Bewegungsrichtung hat folglich jetzt die Nummer 4 (Neue Richtung = 6 – 2 = 4). Die neuen Werte, die zu X- und zu Y-Koordinaten addiert werden, sind in der Array-Variablen „X_Datensatz [14]“ und „Y_Datensatz [14]“ hinterlegt.
Um den Richtungswechsel unvorhersehbar zu machen, wird eine weitere Variable „Zufall_Richtung“ eingeführt. Hier handelt es sich um Werte zwischen -1 und 1, die zu der errechneten Richtung addiert werden. Dieser unvorhersehbare Richtungswechsel kann nur dann stattfinden, wenn der Ball einen der Schläger trifft.
Zu kleinen Attraktionen des Spiels gehört die Änderung der Bewegungsgeschwindigkeit des Balles. Die Anzeige des Pixels, der den Ball symbolisiert, erfolgt stets im festen Takt von 50 ms. Je nachdem, wie weit zwei Punkte auf der Matrix voneinander entfernt sind, ergibt sich daraus das aktuelle Bewegungstempo. Mit der langsamsten Geschwindigkeit haben wir dann zu tun, wenn der Ball die Richtungen 3 oder 10 angenommen hat (Entfernung der Punkte = 1). Bei den Richtungen 1, 5, 8 und 12 bekommt man den Eindruck, dass der Ball schneller wird (Entfernung der Punkte = 1,41). Ganz schnelle Bewegung erfolgt in den Richtungen 0, 2, 4, 6, 7, 9, 11 und 12 (Entfernung der Punkte = 2,23).
Mit dem Taster S1 kann das Spiel gestartet bzw. unterbrochen werden.

Matrix-Info
Arduino & Friends


Kurzvideo

Kurzvideo


Weitere Themen:


Google-Suche auf MEINE-SCHALTUNG.de :


Home Impressum Datenschutz