BALLUFF BNI005H


Google-Suche auf MEINE-SCHALTUNG.de :





Online Rechner

Chronik

Dauerkalender


Pong

(Schaltung mit analogem Joystick)

Das Spiel Pong gehört zu den bekanntesten und ältesten Computer- und Videospielen. Seine Karriere begann 1972 als eine Entwicklung der Firma Atari. Das Spielprinzip ähnelt dem des Tischtennis (Ping Pong). Normalerweise ist Pong ein Spiel für zwei Personen. Daran wollen wir uns jedoch nicht stören und basteln uns ein Spiel für nur eine Person. Die Regeln sind einfach. Auf einem Spielfeld bewegt sich hin und her ein Ball. Auf der rechten Seite des Spielfeldes befindet sich ein Ballschläger, der von dem Spieler rauf und runter bewegt werden kann. Die Aufgabe des Spielers ist es, den Ballschläger so zu steuern, dass der Ball, sobald er den rechten Rand des Spielfeldes erreicht, von dem Ballschläger getroffen wird. Gelingt es nicht, hat der Spieler die Runde verloren.
Als Spielfeld kommt eine RGB-Matrix mit 64 RGB-Leuchtdioden zum Einsatz. Eine blau leuchtende LED symbolisiert den Ball. Zwei grüne Leuchtdioden auf dem rechten Rand der Matrix übernehmen die Aufgabe des Ballschlägers. Die Steuerung des Ballschlägers erfolgt mithilfe eines Joysticks. Ein Mikrocontroller, hier Arduino Uno, koordiniert das Geschehen auf dem Spielfeld.
In dieser Grundausführung verzichten wir auf weitere Erweiterungen, die bei solchen Spielen üblich sind. Man kann die folgende Lösung noch kräftig ausbauen und optimieren. Eine Anreicherung des Spiels mit z.B. akustischen Geräuschen bzw. Signalen und einem Spielstandzähler wäre durchaus empfehlenswert.

RGB-Matrix

RGB Matrix 64

Eine 8x8 RGB-Matrix fungiert in der Schaltung als das Spielfeld.

Analoger Joystick

Joystick mit Druckknopf

Bei dem Joystick-Modul handelt es sich um ein kleines Modul, das für verschiedene Entwicklungs- und Testschaltungen eingesetzt werden kann. Das Modul arbeitet mit einer Spannung von 5V und eignet sich sehr gut für Experimente mit Arduino. Das Modul hat fünf Anschlüsse. Zwei Anschlüsse werden für die Versorgungsspannung benötigt. Zwei weitere Anschlüsse werden für die Achse X und Y verwendet und liefen jeweils ein analoges Signal im Bereich von 0V bis 5V. Der letzte Pin ist der Ausgang eines Tasters, der in das Modul integriert ist. Der Taster wird durch einen senkrechten Druck auf den Joystick aktiviert und liefert ein digitales Signal. In diesem Moment wird der Pin mit Masse verbunden. Der Mikrocontroller Eingang wird deswegen als Pullup-Eingang definiert. Alle Signale können problemlos direkt von Arduino erfasst und ausgewertet werden. Einbindung einer Bibliothek ist nicht erforderlich.

Joystick und seine Anschlüsse

Pinbelegung:

GND – Masse
+5V –Spannungsversorgung +5V
VRx – Anschluss für die X-Achse
VRy – Anschluss für die Y-Achse
SW – Switch – Anschluss. Pin für den intergierten Taster.

Rückseite

Die Abmessungen des Moduls betragen (BxLxH) 26 x 34 x 36 mm. Es kann bei vielen Anbietern erworben werden. Der Preis liegt im Bereich 2-3 Euro.

Arduino Uno

Arduino Uno

Der Mikrocontroller Arduino Uno bringt die Farbe und Bewegung auf das Spielfeld.

Arduino


Spannungsregler

Spannungsregler

Spannungsregler. Die Matrix besteht aus 8x8x3 = 192 Leuchtdioden. Deswegen empfiehlt es sich, für sie eine separate Spannungsversorgung vorzusehen. Je nachdem, wie viele Leuchtdioden gleichzeitig angesteuert werden, kann die Matrix für den +5V Ausgang des Arduino zu große Belastung darstellen.

Spannungsregler mit LM2596 und Anzeige


Der Schaltplan

Schaltplan

Um eventuellen Störungen vorzubeugen, kann der Anschluss der Matrix um einen Kondensator und Widerstand erweitert werden. Ein Beispiel für den Anschluss mit diesen Komponenten findet man hier:

Lichtspiele mit RGB-Matrix


Das Programm (Sketch)

// *****************************************************************************************
// Pong-Spiel mit analogem Joystick
// Arduino UNO, RGB-Matrix 8x8
// IDE 1.8.16
// *****************************************************************************************

#include <Adafruit_NeoPixel.h> 
#define LED_PIN   13
#define LED_COUNT 64
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

#define Joystick_Start    2                             // Joystick digital

bool Verloren;
int PosX, PosY;                                         // aktuelle Ball-Position
int Pixel_Anzeige [] = { -1, -1, -1, -1 };              // Ball mit Kometenschweif
int Bewegungsrichtung;                                  // Aktuelle Bewegungsrichtung des Balles
int PosXY [6][2] = {                                    // Additionswerte für X und Y bei bestimmter Richtung
    { 1,  1 },               
    { 1,  0 },
    { 1, -1 },
    {-1,  1 },
    {-1,  0 },
    {-1, -1 }  };
                                // Mögliche Bewegungsrichtungen:
                                // 0, LRS = Bewegung von Links nach Rechts, Steigend)
                                // 1, LRW = Bewegung von Links nach Rechts, Waagerecht)
                                // 2, LRF = Bewegung von Links nach Rechts, Fallend)
                                // 3, RLS = Bewegung von Rechts nach Links, Steigend)
                                // 4, RLW = Bewegung von Rechts nach Links, Waagerecht)
                                // 5, RLF = Bewegung von Rechts nach Links, Fallend)

                                // Statustabelle für Bestimmung der Folgerichtung
                                // beim Erreichen des Randes
int Status [18][5] = {          // { Status, Max für Random, Richtung 1, Richtung 2, Richtung 3)
                                // 9 - ohne Bedeutung
    {  114, 2, 0, 1, 9 },       // PosX=1, PosY=1, RLW
    {  115, 2, 0, 1, 9 },       // PosX=1, PosY=1, RLF
    {  183, 2, 1, 2, 9 },       // PosX=1, PosY=8, RLS
    {  184, 2, 1, 2, 9 },       // PosX=1, PosY=8, RLW
    {  193, 2, 0, 1, 9 },       // PosX=1, PosY=2..7, RLS
    {  194, 3, 0, 1, 2 },       // PosX=1, PosY=2..7, RLW
    {  195, 2, 1, 2, 9 },       // PosX=1, PosY=2..7, RLF
    
    {  711, 2, 3, 4, 9 },       // PosX=7, PosY=1, LRW
    {  712, 2, 3, 4, 9 },       // PosX=7, PosY=1, LRF
    {  780, 2, 4, 5, 9 },       // PosX=7, PosY=8, LRS
    {  781, 2, 4, 5, 9 },       // PosX=7, PosY=8, LRW
    {  790, 2, 3, 4, 9 },       // PosX=7, PosY=2..7, LRS
    {  791, 3, 3, 4, 5 },       // PosX=7, PosY=2..7, LRW
    {  792, 2, 4, 5, 9 },       // PosX=7, PosY=2..7, LRF

    {  912, 2, 0, 1, 9 },       // PosX=2..6, PosY=1, LRF
    {  915, 2, 3, 4, 9 },       // PosX=2..6, PosY=1, RLF
    {  980, 2, 1, 2, 9 },       // PosX=2..6, PosY=8, LRS
    {  983, 2, 4, 5, 9 },       // PosX=2..6, PosY=8, RLS 
    };

int Schlaeger [8][2] = {        // Mögliche Schläger-Positionen + Feldbewertung
    { 63, 0 },
    { 55, 0 },
    { 47, 0 },
    { 39, 0 },
    { 31, 0 },
    { 23, 0 },
    { 15, 0 },
    {  7, 0 }  };

int Verloren_Kreuz_Pixel [] = { 18, 21, 27, 28, 35, 36, 42, 45 };  // Rotes Kreuz für Verloren

void setup() {                                              // SetUp
    
    strip.begin();           
    strip.show();            
    strip.setBrightness(30); 
    pinMode(Joystick_Start, INPUT_PULLUP);
    randomSeed(analogRead(A2));                              // Startzahl für Random-Funktion
}

void loop() {                                                // Hauptprogramm

    if (!digitalRead(Joystick_Start)) {
        Verloren_Kreuz (0);                                  // Rotes Kreuz löschen
        Verloren = false;
        PosX = 2;                                            // Start-Position via Zufall        
        PosY  = random (2,8);
        Bewegungsrichtung = random (0,3);                    // Start Richtung via Zufall

        while (!Verloren) {
                                                             // Tennisschläger 
            for (int Schlaeger_Zeit = 0; Schlaeger_Zeit < 50; Schlaeger_Zeit++) {
                int Pos_Schlaeger = 1023-(analogRead(0));    // Joystick abfragen
                int PosY_Schlaeger = Pos_Schlaeger / 170;                
                for (int i=0; i<8; i++) {                    // Schläger-Felder löschen
                    strip.setPixelColor(Schlaeger [i][0], 0, 0, 0);
                    Schlaeger [i][1] = 0;                    // Feldbewertung löschen
                }
            Schlaeger [PosY_Schlaeger][1] = 1;               // Neue Feldbewertung
            Schlaeger [PosY_Schlaeger+1][1] = 1;
            strip.setPixelColor(Schlaeger [PosY_Schlaeger][0], 0, 255, 0);
            strip.setPixelColor(Schlaeger [PosY_Schlaeger+1][0], 0, 255, 0);
            strip.show();
            delay (3);
            }
                                                                      // Start der Prüfung der Randposition
            int X = PosX;                                             // Statusberechnung für aktuelle Position
            if (PosX > 1 and PosX < 7) { X = 9; }
            int Y = PosY;
            if (PosY > 1 and PosY < 8) { Y = 9; }
            int StatusXY = (X * 100) + (Y * 10) + Bewegungsrichtung;    
            int Richtungswechsel = -1;                                   // -1 für keinen Richtungswechsel
            for (int i=0; i<18; i++) {                                   // Durchsuchung der Statustabelle
                if (Status [i][0] == StatusXY) {                
                    Richtungswechsel = i; }                              // Richtungswechsel erforderlich
            }
            if (Richtungswechsel > -1) {                                 // Neue Richtung via Zufall bestimmen
                int Richtung_Zufall = random (0,Status [Richtungswechsel][1]);
                Bewegungsrichtung = Status [Richtungswechsel][Richtung_Zufall + 2];
            }

            PosX = PosX + PosXY [Bewegungsrichtung][0];                  // Berechnung der neuen Koordinaten
            PosY = PosY + PosXY [Bewegungsrichtung][1];
            int Pixel_Nr = (9-PosY)*8 - (8-PosX)-1;                      // Pixel Nummer auf der Matrix
            Ball_Anzeige (Pixel_Nr);                                     // Ball bewegen

            if ((PosX == 7) and (Schlaeger [PosY - 1][1] != 1)) {        // Spiel verloren
                Verloren = true; 
                Verloren_Kreuz (255);                                    // Rotes Kreuz anzeigen
            }
            
        } 
    }  
}

void Ball_Anzeige (int Pixel_Nummer) {
  
    for (int i=3; i>0; i--) { 
        Pixel_Anzeige [i] = Pixel_Anzeige [i-1];
    }
    Pixel_Anzeige [0] = Pixel_Nummer;
    strip.setPixelColor(Pixel_Anzeige [3], 0, 0, 0);
    strip.setPixelColor(Pixel_Anzeige [2], strip.gamma32(strip.ColorHSV(43000, 255, 100)));
    strip.setPixelColor(Pixel_Anzeige [1], strip.gamma32(strip.ColorHSV(43000, 255, 200)));
    strip.setPixelColor(Pixel_Anzeige [0], 0, 0, 255);
    strip.show();
}

void Verloren_Kreuz (int Wert) {
    for (int i=0; i<8; i++) {
        strip.setPixelColor(Verloren_Kreuz_Pixel [i], Wert, 0, 0);                    
    }
    strip.show();
}

// *****************************************************************************************       
      

Das Spielfeld betrachten wir als ein Koordinatensystem mit X und Y Achse. Die Position des Balles in der unteren linken Ecke hat die Koordinaten PosX=1 und PosY=1. Folglich hat die obere rechte Ecke die Koordinaten PosX=8 und PosY=8.
Das Spiel beginnt, sobald der Schalter am Joystick betätigt wird. Hierzu wird der digitale Eingang 2 des Arduino abgefragt.
Nach dem Start geht das Programm in eine while-Schleife, die erst dann verlassen wird, wenn der Spieler den Ball nicht trifft. Über die Fortsetzung des Spiels entscheidet die Variable „Verloren“, die während des Spiels auf false steht. Die Untersuchung findet immer dann statt, wenn der Ball in der siebten Spalte steht (PosX=7). Hier wird geprüft, ob das Feld in der achten Spalte mit der gleichen Y-Position bereits belegt ist. Wenn der Spieler es nicht geschafft hat, den Schläger rechtzeitig auf die richtige Position zu bringen, geht „Verloren“ auf true und das Spiel wird beendet.
Die Bewegung des Schlägers ist in einer For-Schleife gefangen. Erst nach Verlassen dieser Schleife erfolgt die Änderung der Position des Balles. Nach jedem Durchlauf der Schleife wird das Programm mit delay(3) kurz angehalten. Auf diese Weise kann man die Geschwindigkeit des Balles steuern. An dieser Stelle könnte man einen weiteren Schalter in die Schaltung integrieren, um mehrere Levels des Spieles zu kreieren. Zwischen den Wartezeiten wird die Position des Joysticks untersucht und der Schläger entsprechend positioniert. Das geschieht, indem der analoge Ausgang des Joysticks VRx ausgelesen wird (Arduino analoger Eingang A0). In dem Beispiel wird nur ein Kanal des Joysticks verwendet. Positioniert man den Joystick anders, kann es notwendig sein, den anderen Kanal (VRy) abzufragen.
Die Bewegung des Balles koordiniert die Tabelle (zweidimensionales Array) „Status“. An der ersten Stelle jeder Zahlenfolge steht hier eine Zahl, die aktuelle PosX, PosY und Bewegungsrichtung zusammenfasst. Diese Zahlen beziehen sich nur auf mögliche Randpositionen des Balles. Bevor der Ball bewegt wird, wird zuerst sein Status (Variable StatusXY) berechnet und mit der Tabelle verglichen. Die Grundformel hier lautet:
StatusXY = PosX * 100 + PosY * 10 + Bewegungsrichtung.
Die möglichen Bewegungsrichtungen sind festgelegt und nummeriert. Eine waagerechte Bewegung von rechts nach links hat z.B. die Nummer 4. Die vollständige Nummerierung steht in Kommentaren des Programms.
In der linken oberen Ecke des Feldes hat demnach der Ball, der bis dato waagerecht von rechts nach links bewegt wurde, einen StatusXY = 1*100 + 8*10 + 4 = 184.
Für die Positionen 1 < PosY < 8 am linken Rand steht pauschal die Zahl 9. Befindet sich der Ball nach einer waagerechten Bewegung von rechts nach links an der Position PosX=1, PosY=3, so hat sein Status den Wert 194. Ähnlich wird auch bei anderen Grenzlagen verfahren.
Diese Werte signalisieren dem Programm, dass ein Richtungswechsel notwendig ist. Die möglichen neuen Richtungen stehen ebenfalls in der Variable „Status“ und zwar auf den Plätzen 3, 4 und 5. Stimmt also der aktuelle Status des Balles mit einem der Werte in der Tabelle überein, wird via Zufall eine neue Richtung bestimmt. An der zweiten Stelle in der Tabelle steht der maximale Wert für die Funktion random(). Je nach Position und bisheriger Bewegungsrichtung sind 2 oder 3 Folgerichtungen möglich.

Die Testschaltung

Testschaltung

Testschaltung


Kurzvideo


Weitere Themen:


Google-Suche auf MEINE-SCHALTUNG.de :


Home Impressum Datenschutz