Arduino Retro Gaming met een OLED-scherm
Ooit afgevraagd hoeveel werk het kost om je eigen retro-games te schrijven? Hoe eenvoudig is Pong om te coderen voor de Arduino? Doe met me mee, terwijl ik je laat zien hoe je een Arduino mini-gameconsole met afstandsbediening bouwt, en hoe je Pong vanuit het niets kunt coderen. Dit is het eindresultaat:
Bouw plan
Dit is een vrij eenvoudig circuit. EEN potentiometer (pot) zal het spel besturen, en een OLED-display zal worden bestuurd door de Arduino. Dit wordt geproduceerd op een breadboard, maar misschien wilt u dit een permanent circuit maken en het in een hoes installeren. We hebben geschreven over het opnieuw creëren van Pong Hoe het klassieke pongspel te recreëren met behulp van Arduino Hoe het klassieke pongspel te recreëren met behulp van Arduino Pong was de allereerste videogame die de massamarkt bereikte. Voor de eerste keer in de geschiedenis werd het concept van een "videogame" in het ouderlijk huis gebracht, dankzij de Atari 2600 - ... Lees meer voor, maar vandaag laat ik je zien hoe je de code helemaal opnieuw kunt schrijven en breken naar beneden elk deel.
Wat je nodig hebt
Dit is wat je nodig hebt:
- 1 x Arduino (elk model)
- 1 x 10k Potentiometer
- 1 x 0,96 "I2C OLED-scherm
- 1 x breadboard
- Geassorteerde mannelijke> mannelijke aansluitdraden
DIYmall 0,96 inch Inch I2c IIC Serieel 128x64 Oled LCD-scherm Wit Displaymodule voor Arduino 51 Msp420 Stim32 SCR DIY klein 0.96 "Inch I2c IIC Serieel 128x64 Oled LCD-scherm Wit Displaymodule voor Arduino 51 Msp420 Stim32 SCR Nu kopen bij Amazon $ 8,99
Elke Arduino zou moeten werken, dus kijk eens naar onze koopgids. Arduino Koopwijzer: welk bord moet je kopen? Arduino Koopwijzer: welk bord moet je kopen? Er zijn zoveel verschillende soorten Arduino-boards, dat je zou worden vergeven dat je in de war was. Welke moet je kopen voor je project? Laat ons helpen, met deze Arduino koopgids! Meer lezen als u niet zeker weet welk model u moet kopen.
Deze OLED-schermen zijn erg cool. Ze kunnen meestal worden gekocht in wit, blauw, geel of een mengsel van de drie. Ze bestaan in kleur, maar deze voegen een heel ander niveau toe aan de complexiteit en kosten van dit project.
Het circuit
Dit is een vrij eenvoudig circuit. Als je niet veel ervaring hebt met Arduino, bekijk dan deze beginnersprojecten. 15 Great Arduino Projects for Beginners 15 Great Arduino Projects for Beginners Geïnteresseerd in Arduino, maar niet zeker waar te beginnen? Hier zijn enkele van onze beste Arduino-projecten voor beginners om mee te beginnen! Lees meer eerst.
Hier is het:
Kijk naar de voorkant van de pot en verbind de linkerpin met +5V en de juiste pin voor grond. Verbind de middelste pin met analoge pin 0 (A0).
Het OLED-display is aangesloten via het I2C-protocol. Aansluiten VCC en GND naar de Arduino +5V en grond. Aansluiten SCL naar analoge vijf (A5). Aansluiten SDA naar analoog 4 (A4). De reden dat dit is verbonden met de analoge pinnen is eenvoudig; deze pinnen bevatten de circuits die vereist zijn voor het I2C-protocol. Zorg ervoor dat deze correct zijn aangesloten en niet zijn overgestoken. De exacte pinnen verschillen per model, maar A4 en A5 worden gebruikt op de Nano en Uno. Raadpleeg de documentatie bij de draadbibliotheek voor uw model als u geen Arduino of Nano gebruikt.
Potentest
Upload deze testcode (zorg ervoor dat u de juiste kaart en poort selecteert in de Hulpmiddelen > Boord en Hulpmiddelen > Haven menu's):
void setup () // zet hier je setup-code, om één keer te draaien: Serial.begin (9600); // setup serial void loop () // zet hier je hoofdcode, om herhaaldelijk uit te voeren: Serial.println (analoogLees (A0)); // print de waarde uit de potvertraging (500);
Open nu de seriële monitor (Rechtsboven > Seriële monitor) en draai de pot. U zou de waarde op het seriële scherm moeten zien. Volledig tegen de klok in zou moeten zijn nul, en volledig met de klok mee zou moeten zijn 1023:
Je zult dit later aanpassen, maar voorlopig is het prima. Als er niets gebeurt, of de waarde verandert zonder dat u iets doet, ontkoppel en controleer dan het circuit.
OLED-test
Het OLED-scherm is iets complexer om te configureren. U moet twee bibliotheken installeren om als eerste met het beeldscherm te werken. Download de Adafruit_SSD1306- en Adafruit-GFX-bibliotheken van Github. Kopieer de bestanden naar uw bibliothekenmap. Dit varieert afhankelijk van uw besturingssysteem:
- Mac OS: / Users / gebruikersnaam / Documents / Arduino / bibliotheken
- Linux: / Home / gebruikersnaam / Sketchbook
- Ramen: / Users / Arduino / bibliotheken
Upload nu een testschets. Ga naar het dossier > Voorbeelden > Adafruit SSD1306 > ssd1306_128x64_i2c. Dit zou je een grote schets moeten opleveren met veel afbeeldingen:
Als er niets gebeurt na het uploaden, ontkoppel en controleer je je verbindingen. Als de voorbeelden niet in de menu's staan, moet u wellicht uw Arduino IDE opnieuw opstarten.
De code
Nu is het tijd voor de code. Ik zal elke stap uitleggen, dus ga door tot het einde als je het gewoon wilt laten draaien. Dit is een behoorlijke hoeveelheid code, dus als je geen zelfvertrouwen hebt, bekijk dan deze 10 gratis bronnen. Learn To Code: 10 gratis en fantastische online bronnen om je vaardigheden te verbeteren Leren coderen: 10 gratis en fantastische online bronnen om je te verbeteren Vaardigheden coderen. Een onderwerp dat door velen wordt vermeden. Er is een overvloed aan gratis hulpmiddelen en hulpmiddelen, die allemaal online beschikbaar zijn. Natuurlijk kun je wat cursussen over het onderwerp volgen in een nabijgelegen ... Lees meer om te leren coderen.
Begin met het opnemen van de benodigde bibliotheken:
#include #include #include #include
SPI en DRAAD zijn twee Arduino-bibliotheken voor het verwerken van de I2C-communicatie. Adafruit_GFX en Adafruit_SSD1306 zijn de bibliotheken die u eerder hebt geïnstalleerd.
Configureer vervolgens het scherm:
Adafruit_SSD1306 display (4);
Stel vervolgens alle variabelen in die nodig zijn om het spel uit te voeren:
int resolutie [2] = 128, 64, bal [2] = 20, (resolutie [1] / 2); const int PIXEL_SIZE = 8, WALL_WIDTH = 4, PADDLE_WIDTH = 4, BALL_SIZE = 4, SPEED = 3; int playerScore = 0, aiScore = 0, playerPos = 0, aiPos = 0; char ballDirectionHori = 'R', ballDirectionVerti = 'S'; boolean inProgress = true;
Deze slaan alle gegevens op die nodig zijn om het spel uit te voeren. Sommige hiervan slaan de locatie van de bal op, de grootte van het scherm, de locatie van de speler, enzovoort. Merk op hoe sommige hiervan zijn const wat betekent dat ze constant zijn en nooit zullen veranderen. Hierdoor kan de Arduino-compiler dingen versnellen.
De schermresolutie en de ballocatie worden opgeslagen arrays. Arrays zijn verzamelingen met soortgelijke dingen en bewaar de coördinaten voor de bal (X en Y). Toegang tot elementen in arrays is eenvoudig (voeg deze code niet toe aan uw bestand):
Resolutie [1];
Omdat arrays bij nul beginnen, zal dit het tweede element in de resolutiearray retourneren (64). Het bijwerken van elementen is nog eenvoudiger (nogmaals, neem deze code niet op):
bal [1] = 15;
Binnen void setup (), configureer het scherm:
void setup () display.begin (SSD1306_SWITCHCAPVCC, 0x3C); Display.Display ();
De eerste regel vertelt de Adafruit-bibliotheek welk dimensies en communicatieprotocol uw display gebruikt (in dit geval, 128 X 64 en I2C). De tweede regel (Display.Display ()) vertelt het scherm om te laten zien wat er in de buffer is opgeslagen (wat niets is).
Maak twee methoden genaamd drawBall en eraseBall:
void drawBall (int x, int y) display.drawCircle (x, y, BALL_SIZE, WHITE); void eraseBall (int x, int y) display.drawCircle (x, y, BALL_SIZE, BLACK);
Deze nemen de X en Y coördinaten van de bal en teken deze op het scherm met behulp van de drawCircle methode uit de weergavebibliotheken. Dit gebruikt de constante BALL_SIZE eerder gedefinieerd. Probeer dit te veranderen en kijk wat er gebeurt. Deze drawCircle-methode accepteert een pixelkleur - ZWART of WIT. Omdat dit een monochroom display (één kleur) is, komt wit overeen met een pixel die aan staat en zwart zet de pixel uit.
Maak nu een methode genaamd moveAi:
void moveAi () eraseAiPaddle (aiPos); if (ball [1]> aiPos) ++ aiPos; else if (ball [1] < aiPos) --aiPos; drawAiPaddle(aiPos);
Deze methode verwerkt het verplaatsen van de Kunstmatige intelligentie of AI speler. Dit is een vrij eenvoudige computertegenstander. Als de bal zich boven de peddel bevindt, ga dan omhoog. Het is onder de peddel, ga naar beneden. Heel eenvoudig, maar het werkt goed. De oplopende en afnemende symbolen worden gebruikt (++aiPos en -aiPos) om een toe te voegen of af te trekken van de aiPosition. Je zou een groter getal kunnen optellen of aftrekken om de AI sneller te laten bewegen en daarom moeilijker te verslaan zijn. Zo zou je dat doen:
aiPos + = 2;
En:
aiPos - = 2;
De Plus is gelijk aan en Minus is gelijk aan tekens zijn kort om twee toe te voegen of af te trekken van / tot de huidige waarde van aiPos. Hier is een andere manier om dat te doen:
aiPos = aiPos + 2;
en
aiPos = aiPos - 1;
Merk op hoe deze methode eerst de paddle wist en deze vervolgens opnieuw tekent. Dit moet zo worden gedaan. Als de nieuwe positie van de paddle werd getekend, zouden er twee overlappende paddles op het scherm verschijnen.
De drawNet methode gebruikt twee lussen om het net te tekenen:
void drawNet () for (int i = 0; i < (resolution[1] / WALL_WIDTH); ++i) drawPixel(((resolution[0] / 2) - 1), i * (WALL_WIDTH) + (WALL_WIDTH * i), WALL_WIDTH);
Dit gebruikt de WALL_WIDTH variabelen om de grootte in te stellen.
Maak methoden genaamd drawPixels en erasePixels. Net als de ballen, is het enige verschil tussen deze twee de kleur van de pixels:
void drawPixel (int posX, int posY, int dimensions) for (int x = 0; x < dimensions; ++x) for (int y = 0; y < dimensions; ++y) display.drawPixel((posX + x), (posY + y), WHITE); void erasePixel(int posX, int posY, int dimensions) for (int x = 0; x < dimensions; ++x) for (int y = 0; y < dimensions; ++y) display.drawPixel((posX + x), (posY + y), BLACK);
Nogmaals, beide methoden gebruiken er twee voor lussen om een groep pixels te tekenen. In plaats van elke pixel te moeten tekenen met behulp van de bibliotheken drawPixel methode, tekenen de lussen een groep pixels op basis van de gegeven dimensies.
De drawScore methode gebruikt de tekstfuncties van de bibliotheek om de speler en de AI-score naar het scherm te schrijven. Deze worden opgeslagen in playerScore en aiScore:
void drawScore () display.setTextSize (2); display.setTextColor (wit); display.setCursor (45, 0); display.println (playerScore); display.setCursor (75, 0); display.println (aiScore);
Deze methode heeft ook een eraseScore tegenhanger, waarmee de pixels zwart worden of uitgeschakeld.
De laatste vier methoden lijken erg op elkaar. Ze tekenen en wissen de speler en de AI-paddles:
void erasePlayerPaddle (int rij) erasePixel (0, rij - (PADDLE_WIDTH * 2), PADDLE_WIDTH); erasePixel (0, rij - PADDLE_WIDTH, PADDLE_WIDTH); erasePixel (0, rij, PADDLE_WIDTH); erasePixel (0, rij + PADDLE_WIDTH, PADDLE_WIDTH); erasePixel (0, rij + (PADDLE_WIDTH + 2), PADDLE_WIDTH);
Let op hoe zij de erasePixel methode eerder creëren. Deze methoden tekenen en wissen de juiste paddle.
Er zit iets meer logica in de hoofdlus. Hier is de hele code:
#include #include #include #include Adafruit_SSD1306 display (4); int resolutie [2] = 128, 64, bal [2] = 20, (resolutie [1] / 2); const int PIXEL_SIZE = 8, WALL_WIDTH = 4, PADDLE_WIDTH = 4, BALL_SIZE = 4, SPEED = 3; int playerScore = 0, aiScore = 0, playerPos = 0, aiPos = 0; char ballDirectionHori = 'R', ballDirectionVerti = 'S'; boolean inProgress = true; void setup () display.begin (SSD1306_SWITCHCAPVCC, 0x3C); Display.Display (); void loop () if (aiScore> 9 || playerScore> 9) // check game state inProgress = false; if (inProgress) eraseScore (); eraseBall (bal [0], bal [1]); if (ballDirectionVerti == 'U') // bal naar boven schuiven bal [1] = bal [1] - SPEED; if (ballDirectionVerti == 'D') // bal naar beneden schuiven diagonaal bal [1] = bal [1] + SPEED; als (bal [1] <= 0) // bounce the ball off the top ballDirectionVerti = 'D'; if (ball[1] >= resolutie [1]) // stuiter de bal van de onderkant ballDirectionVerti = 'U'; if (ballDirectionHori == 'R') ball [0] = bal [0] + SPEED; // verplaats bal als (bal [0]> = (resolutie [0] - 6)) // bal aan de AI-rand van het scherm staat als ((aiPos + 12)> = bal [1] && (aiPos - 12) <= ball[1]) // ball hits AI paddle if (ball[1] > (aiPos + 4)) // bal naar beneden afbuigen ballDirectionVerti = 'D'; else if (ball [1] < (aiPos - 4)) // deflect ball up ballDirectionVerti = 'U'; else // deflect ball straight ballDirectionVerti = 'S'; // change ball direction ballDirectionHori = 'L'; else // GOAL! ball[0] = 6; // move ball to other side of screen ballDirectionVerti = 'S'; // reset ball to straight travel ball[1] = resolution[1] / 2; // move ball to middle of screen ++playerScore; // increase player score if (ballDirectionHori == 'L') ball[0] = ball[0] - SPEED; // move ball if (ball[0] <= 6) // ball is at the player edge of the screen if ((playerPos + 12) >= bal [1] && (playerPos - 12) <= ball[1]) // ball hits player paddle if (ball[1] > (playerPos + 4)) // bal naar beneden afbuigen ballDirectionVerti = 'D'; else if (ball [1] < (playerPos - 4)) // deflect ball up ballDirectionVerti = 'U'; else // deflect ball straight ballDirectionVerti = 'S'; // change ball direction ballDirectionHori = 'R'; else ball[0] = resolution[0] - 6; // move ball to other side of screen ballDirectionVerti = 'S'; // reset ball to straight travel ball[1] = resolution[1] / 2; // move ball to middle of screen ++aiScore; // increase AI score drawBall(ball[0], ball[1]); erasePlayerPaddle(playerPos); playerPos = analogRead(A2); // read player potentiometer playerPos = map(playerPos, 0, 1023, 8, 54); // convert value from 0 - 1023 to 8 - 54 drawPlayerPaddle(playerPos); moveAi(); drawNet(); drawScore(); else // somebody has won display.clearDisplay(); display.setTextSize(4); display.setTextColor(WHITE); display.setCursor(0, 0); // figure out who if (aiScore > playerScore) display.println ("YOU LOSE!"); else if (playerScore> aiScore) display.println ("YOU WIN!"); display.display (); void moveAi () // verplaats de AI paddle eraseAiPaddle (aiPos); if (ball [1]> aiPos) ++ aiPos; else if (ball [1] < aiPos) --aiPos; drawAiPaddle(aiPos); void drawScore() // draw AI and player scores display.setTextSize(2); display.setTextColor(WHITE); display.setCursor(45, 0); display.println(playerScore); display.setCursor(75, 0); display.println(aiScore); void eraseScore() // erase AI and player scores display.setTextSize(2); display.setTextColor(BLACK); display.setCursor(45, 0); display.println(playerScore); display.setCursor(75, 0); display.println(aiScore); void drawNet() for (int i = 0; i < (resolution[1] / WALL_WIDTH); ++i) drawPixel(((resolution[0] / 2) - 1), i * (WALL_WIDTH) + (WALL_WIDTH * i), WALL_WIDTH); void drawPixel(int posX, int posY, int dimensions) // draw group of pixels for (int x = 0; x < dimensions; ++x) for (int y = 0; y < dimensions; ++y) display.drawPixel((posX + x), (posY + y), WHITE); void erasePixel(int posX, int posY, int dimensions) // erase group of pixels for (int x = 0; x < dimensions; ++x) for (int y = 0; y < dimensions; ++y) display.drawPixel((posX + x), (posY + y), BLACK); void erasePlayerPaddle(int row) erasePixel(0, row - (PADDLE_WIDTH * 2), PADDLE_WIDTH); erasePixel(0, row - PADDLE_WIDTH, PADDLE_WIDTH); erasePixel(0, row, PADDLE_WIDTH); erasePixel(0, row + PADDLE_WIDTH, PADDLE_WIDTH); erasePixel(0, row + (PADDLE_WIDTH + 2), PADDLE_WIDTH); void drawPlayerPaddle(int row) drawPixel(0, row - (PADDLE_WIDTH * 2), PADDLE_WIDTH); drawPixel(0, row - PADDLE_WIDTH, PADDLE_WIDTH); drawPixel(0, row, PADDLE_WIDTH); drawPixel(0, row + PADDLE_WIDTH, PADDLE_WIDTH); drawPixel(0, row + (PADDLE_WIDTH + 2), PADDLE_WIDTH); void drawAiPaddle(int row) int column = resolution[0] - PADDLE_WIDTH; drawPixel(column, row - (PADDLE_WIDTH * 2), PADDLE_WIDTH); drawPixel(column, row - PADDLE_WIDTH, PADDLE_WIDTH); drawPixel(column, row, PADDLE_WIDTH); drawPixel(column, row + PADDLE_WIDTH, PADDLE_WIDTH); drawPixel(column, row + (PADDLE_WIDTH * 2), PADDLE_WIDTH); void eraseAiPaddle(int row) int column = resolution[0] - PADDLE_WIDTH; erasePixel(column, row - (PADDLE_WIDTH * 2), PADDLE_WIDTH); erasePixel(column, row - PADDLE_WIDTH, PADDLE_WIDTH); erasePixel(column, row, PADDLE_WIDTH); erasePixel(column, row + PADDLE_WIDTH, PADDLE_WIDTH); erasePixel(column, row + (PADDLE_WIDTH * 2), PADDLE_WIDTH); void drawBall(int x, int y) display.drawCircle(x, y, BALL_SIZE, WHITE); void eraseBall(int x, int y) display.drawCircle(x, y, BALL_SIZE, BLACK);
Dit is waar je uiteindelijk mee eindigt:
Zodra u zeker bent van de code, zijn er tal van wijzigingen die u kunt aanbrengen:
- Voeg een menu toe voor moeilijkheidsgraden (verander AI en balsnelheid).
- Voeg een willekeurige beweging toe aan de bal of AI.
- Voeg nog een pot toe voor twee spelers.
- Voeg een pauzeknop toe.
Bekijk nu deze retro gaming Pi Zero-projecten 5 Retro Gaming-projecten met de Raspberry Pi Zero 5 Retro Gaming-projecten met de Raspberry Pi Zero De Raspberry Pi Zero heeft de DIY en homebrew wereld veroverd, waardoor het mogelijk was om oude projecten te herzien en inspirerende nieuwkomers, vooral in de koortsige geesten van retro-gameliefhebbers. Lees verder .
Heb je Pong gecodeerd met deze code? Welke wijzigingen heb je aangebracht? Laat het me weten in de reacties hieronder, ik zou graag wat foto's willen laten lijken!
Ontdek meer over: Arduino, Electronics, Retro Gaming.