Arduino Retro Gaming Med OLED-skjerm

Noen gang lurt på hvor mye arbeid det tar å skrive dine egne retrospill? Hvor lett er Pong å kode for Arduino?

Noen gang lurt på hvor mye arbeid det tar å skrive dine egne retrospill?  Hvor lett er Pong å kode for Arduino?
Annonse

Noen gang lurt på hvor mye arbeid det tar å skrive dine egne retrospill? Hvor lett er Pong å kode for Arduino? Bli med meg da jeg viser deg hvordan du bygger en Arduino-drevet mini retro spillkonsoll, og hvordan du kodes Pong fra bunnen av. Her er sluttresultatet:

Bygg Plan

Dette er en ganske enkel krets. Et potensiometer (pott) vil styre spillet, og en OLED-skjerm vil bli drevet av Arduino. Dette vil bli produsert på et brettbrett, men du kan ønske å gjøre dette til en permanent krets og installere den i et tilfelle. Vi har skrevet om å gjenskape Pong Slik gjenoppretter du det klassiske Pong-spillet Bruke Arduino Slik gjenoppretter du det klassiske Pong-spillet Bruke Arduino Pong var det første noensinne videospillet som nådde massemarkedet. For første gang i historien ble begrepet "videospill" brakt inn i familiens hjem takket være Atari 2600 -... Les mer før, men i dag vil jeg vise deg hvordan du skriver koden fra grunnen av, og bryte ned hver del.

Hva trenger du

Retro Arduino Setup

Her er hva du trenger:

  • 1 x Arduino (hvilken som helst modell)
  • 1 x 10k Potentiometer
  • 1 x 0, 96 "I2C OLED Display
  • 1 x brødbrett
  • Assortert mann> mannlige hookup ledninger

Diymall 0.96 "Inch I2c IIC Seriell 128x64 Oled LCD LED Hvit Display Modul for Arduino 51 Msp420 Stim32 SCR Diymall 0.96" Inch I2c IIC Seriell 128x64 Oled LCD LED Hvit Display Modul for Arduino 51 Msp420 Stim32 SCR Kjøp nå På Amazon $ 9.99

Eventuelle Arduino bør jobbe, så se på vår kjøpeguide Arduino Kjøpsguide: Hvilket styre bør du få? Arduino Kjøpsguide: Hvilket styre bør du få? Det er så mange forskjellige typer Arduino boards der ute, du vil bli tilgitt for å være forvirret. Hvilken bør du kjøpe for prosjektet ditt? La oss hjelpe, med denne Arduino kjøpesiden! Les mer hvis du ikke er sikker på hvilken modell du skal kjøpe.

Disse OLED-skjermene er veldig kule. De kan vanligvis kjøpes i hvitt, blått, gult eller en blanding av de tre. De eksisterer i full farge, men disse legger et helt annet nivå til kompleksiteten og kostnaden av dette prosjektet.

Kredsløpet

Dette er en ganske enkel krets. Hvis du ikke har mye erfaring med Arduino, sjekk ut disse nybegynnerprosjektene 10 Great Arduino-prosjekter for nybegynnere 10 Great Arduino-prosjekter for nybegynnere Å fullføre et Arduino-prosjekt gir deg en følelse av tilfredshet som ingen andre. De fleste nybegynnere er ikke sikker på hvor du skal begynne, og selv begynnerprosjekter kan virke ganske skremmende. Les mer først.

Her er det:

Pongbrødbrett

Sett på forsiden av potten, koble venstre pinne til + 5V og høyre pinne til bakken . Koble den midterste pin til analog pin 0 (A0).

OLED-skjermen er koblet til ved hjelp av I2C-protokollen. Koble VCC og GND til Arduino + 5V og bakken . Koble SCL til analog fem ( A5 ). Koble SDA til analog 4 ( A4 ). Grunnen til at dette er koblet til de analoge pinnene er enkelt; Disse pinnene inneholder kretsene som kreves for I2C-protokollen. Kontroller at disse er koblet riktig, og ikke krysset over. De eksakte pinnene vil variere etter modell, men A4 og A5 brukes på Nano og Uno. Sjekk Wirebibliotekets dokumentasjon for din modell hvis du ikke bruker en Arduino eller Nano.

Pot Test

Last opp denne testkoden (pass på å velge riktig kort og port fra Verktøy > Styret og Verktøy > Portmenyer):

void setup() { // put your setup code here, to run once: Serial.begin(9600); // setup serial } void loop() { // put your main code here, to run repeatedly: Serial.println(analogRead(A0)); // print the value from the pot delay(500); } 

Åpne nå seriell skjerm ( Topp Høyre > Seriell Skjerm ) og vri pannen. Du bør se verdien som vises på seriell skjerm. Helt mot urviseren skal være null, og helt med urviseren skal være 1023 :

Pong seriell skjerm

Du vil justere dette senere, men for nå er det greit. Hvis ingenting skjer, eller verdien endres uten at du gjør noe, koble fra og dobbeltkoble kretsen.

OLED Test

OLED-grafikk

OLED-skjermen er litt kompleksere å konfigurere. Du må installere to biblioteker for å kjøre skjermen først. Last ned Adafruit_SSD1306 og Adafruit-GFX biblioteker fra Github. Kopier filene til bibliotekets mappe. Dette varierer avhengig av operativsystemet ditt:

  • Mac OS: / Brukere / Brukernavn / Dokumenter / Arduino / Biblioteker
  • Linux: / home / Brukernavn / Sketchbook
  • Windows: / Brukere / Arduino / biblioteker

Last opp en testskisse. Gå til Arkiv > Eksempler > Adafruit SSD1306 > ssd1306_128x64_i2c . Dette bør gi deg en stor skisse som inneholder mye grafikk:

OLED-grafikk

Hvis ingenting skjer etter opplasting, koble fra og dobbeltkoble tilkoblingene. Hvis eksemplene ikke er i menyene, må du kanskje starte din Arduino IDE på nytt.

Koden

Nå er det tid for koden. Jeg skal forklare hvert trinn, så hopp over til slutten hvis du bare vil få det til å løpe. Dette er en god del kode, så hvis du ikke føler deg trygg, sjekk ut disse 10 gratis ressursene. Lær å kode: 10 gratis og fantastiske elektroniske ressurser for å finpusse dine ferdigheter. Lær å kode: 10 gratis og fantastiske elektroniske ressurser for å finpusse Ferdighetskoding. Et emne som unngås av mange. Det finnes en overflod av gratis ressurser og verktøy, som alle er tilgjengelige online. Sikker på at du kan ta noen kurs på emnet på en nærliggende ... Les mer for å lære å kode.

Begynn med å inkludere de nødvendige bibliotekene:

 #include #include #include #include 

SPI og WIRE er to Arduino-biblioteker for håndtering av I2C-kommunikasjonen. Adafruit_GFX og Adafruit_SSD1306 er bibliotekene du installerte tidligere.

Deretter konfigurer skjermen:

 Adafruit_SSD1306 display(4); 

Sett deretter opp alle variablene som trengs for å kjøre spillet:

 int resolution[2] = {128, 64}, ball[2] = {20, (resolution[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; 

Disse lagrer alle dataene som er nødvendige for å kjøre spillet. Noen av disse lagrer plasseringen av ballen, størrelsen på skjermen, plasseringen av spilleren og så videre. Legg merke til hvordan noen av disse er const som betyr at de er konstante, og vil aldri forandre seg. Dette gjør at Arduino-kompilatoren raskerer opp ting.

Skjermoppløsningen og ballplasseringen er lagret i arrays . Arrays er samlinger av lignende ting, og for ballen lagrer du koordinatene ( X og Y ). Å få tilgang til elementer i arrays er enkelt (ikke inkluder denne koden i filen din):

 resolution[1]; 

Når arrayene starter ved null, returnerer dette det andre elementet i oppløsningsarmen ( 64 ). Oppdatere elementer er enda enklere (igjen, ikke inkluder denne koden):

 ball[1] = 15; 

Inne i tomt oppsett (), konfigurer skjermen:

 void setup() { display.begin(SSD1306_SWITCHCAPVCC, 0x3C); display.display(); } 

Den første linjen forteller Adafruit-biblioteket hvilke dimensjoner og kommunikasjonsprotokoll skjermen din bruker (i dette tilfellet 128 x 64 og I2C ). Den andre linjen ( display.display () ) forteller at skjermen viser hva som er lagret i bufferen (som ikke er noe).

Lag to metoder kalt drawBall og 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); } 

Disse tar x- og y- koordinatene til ballen og tegner den på skjermen ved hjelp av drawCircle- metoden fra displaybibliotekene. Dette bruker den konstante BALL_SIZE definert tidligere. Prøv å endre dette og se hva som skjer. Denne drawCircle-metoden aksepterer en pikselfarge - SVART eller HVID . Siden dette er en monokromatisk skjerm (en farge), står hvit i likhet med en piksel som er på, og svart svinger pikselet av.

Opprett nå en metode som heter moveAi :

 void moveAi() { eraseAiPaddle(aiPos); if (ball[1]>aiPos) { ++aiPos; } else if (ball[1]< aiPos) { --aiPos; } drawAiPaddle(aiPos); } 

Denne metoden håndterer å flytte Artificial Intelligence eller AI- spilleren. Dette er en ganske enkel datamaskin motstander - Hvis ballen er over padleen, flytter du opp. Den er under padleen, flytt ned. Ganske enkelt, men det fungerer bra. Symbolene for økning og dekrementering brukes ( ++ aiPos og -aiPos ) for å legge til eller trekke en fra aiPositionen. Du kan legge til eller trekke et større tall for å få AI til å bevege seg raskere, og derfor være vanskeligere å slå. Slik gjør du det:

 aiPos += 2; 

Og:

 aiPos -= 2; 

Plus-like og Minus-like tegn er shorthand for å legge til eller trekke to fra / til dagens verdi av aiPos. Her er en annen måte å gjøre det på:

 aiPos = aiPos + 2; 

og

 aiPos = aiPos - 1; 

Legg merke til hvordan denne metoden først sletter padlen, og trekker den deretter igjen. Dette må gjøres slik. Hvis den nye posisjonen til padlen ble trukket, ville det være to overlappende padler på skjermen.

DrawNet- metoden bruker to løkker for å tegne nettet:

 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); } } 

Dette bruker WALL_WIDTH- variablene til å angi størrelsen på den.

Lag metoder som kalles drawPixels og erasePixels . Akkurat som ballen, er den eneste forskjellen mellom disse to fargene på pikslene:

 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); } } } 

Igjen, begge disse metodene bruker to for sløyfer for å tegne en gruppe piksler. I stedet for å måtte tegne hver piksel ved hjelp av bibliotekene DrawPixel- metoden, tegner sløyfer en gruppe piksler basert på de oppgitte dimensjonene.

DrawScore- metoden bruker tekstfunksjonene i biblioteket til å skrive spilleren og AI-score til skjermen. Disse lagres i playerScore og aiScore :

 void drawScore() { display.setTextSize(2); display.setTextColor(WHITE); display.setCursor(45, 0); display.println(playerScore); display.setCursor(75, 0); display.println(aiScore); } 

Denne metoden har også en eraseScore- motpart, som setter piksler til svart eller av.

De siste fire metodene er veldig like. De tegner og sletter spilleren og AI padler:

 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); } 

Legg merke til hvordan de kaller erasePixel- metoden, opprett tidligere. Disse metodene tegner og sletter riktig padle.

Det er litt mer logikk i hovedløkken. Her er hele koden:

 #include #include #include #include Adafruit_SSD1306 display(4); int resolution[2] = {128, 64}, ball[2] = {20, (resolution[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(ball[0], ball[1]); if (ballDirectionVerti == 'U') { // move ball up diagonally ball[1] = ball[1] - SPEED; } if (ballDirectionVerti == 'D') { // move ball down diagonally ball[1] = ball[1] + SPEED; } if (ball[1] = resolution[1]) { // bounce the ball off the bottom ballDirectionVerti = 'U'; } if (ballDirectionHori == 'R') { ball[0] = ball[0] + SPEED; // move ball if (ball[0]>= (resolution[0] - 6)) { // ball is at the AI edge of the screen if ((aiPos + 12)>= ball[1] && (aiPos - 12) (aiPos + 4)) { // deflect ball down 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] = ball[1] && (playerPos - 12) (playerPos + 4)) { // deflect ball down 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() { // move the 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); } 

Her er hva du ender med:

OLED Pong

Når du er trygg på koden, er det mange modifikasjoner du kan gjøre:

  • Legg til en meny for vanskelighetsnivåer (endre AI og ballhastighet).
  • Legg til litt tilfeldig bevegelse til ballen eller AI.
  • Legg til en annen pott for to spillere.
  • Legg til en pause-knapp.

Ta en titt på disse retro spillene Pi Zero-prosjekter 5 Retro Gaming-prosjekter med Raspberry Pi Zero 5 Retro Gaming-prosjekter med Raspberry Pi Zero Raspberry Pi Zero har tatt DIY og homebrew verden med storm, noe som gjør det mulig å revidere gamle prosjekter og inspirerende nykommere, spesielt i de feberte hjernene til retrospillere. Les mer .

Har du kodet Pong ved hjelp av denne koden? Hvilke modifikasjoner gjorde du? Gi meg beskjed i kommentarene nedenfor, jeg vil gjerne se noen bilder!

In this article