Arduino Retro Gaming Med OLED Display

Har du nogensinde spekuleret på, hvor meget arbejde det kræver at skrive dine egne retro spil? Hvor nemt er Pong at kode for Arduino?

Har du nogensinde spekuleret på, hvor meget arbejde det kræver at skrive dine egne retro spil?  Hvor nemt er Pong at kode for Arduino?
Reklame

Har du nogensinde spekuleret på, hvor meget arbejde det kræver at skrive dine egne retro spil? Hvor nemt er Pong at kode for Arduino? Kom med mig, da jeg viser dig, hvordan man opbygger en Arduino-powered mini retro spilkonsol, og hvordan man kan kode Pong fra bunden. Her er slutresultatet:

Build Plan

Dette er et ret simpelt kredsløb. Et potentiometer (pot) styrer spillet, og en OLED-skærm bliver drevet af Arduino. Dette vil blive produceret på et brændebræt, men du vil måske gøre dette til et permanent kredsløb og installere det i en sag. Vi har skrevet om at genskabe Pong Hvordan at genskabe det klassiske Pong-spil ved hjælp af Arduino Sådan genskabe det klassiske Pong-spil Ved at bruge Arduino Pong var det første nogensinde videospil, der nåede massemarkedet. For første gang i historien blev begrebet "videospil" bragt ind i familiens hjem takket være Atari 2600 -... Læs mere før, men i dag vil jeg vise dig, hvordan man skriver koden fra bunden, og nedbryde alle dele.

Hvad du har brug for

Retro Arduino Setup

Her er hvad du har brug for:

  • 1 x Arduino (enhver model)
  • 1 x 10k Potentiometer
  • 1 x 0, 96 "I2C OLED Display
  • 1 x brødbræt
  • Assorteret mand> mandlige hookup ledninger

Diymall 0.96 "Inch I2c IIC Serial 128x64 Oled LCD LED Hvid Display Modul til Arduino 51 Msp420 Stim32 SCR Diymall 0.96" Inch I2c IIC Serial 128x64 Oled LCD LED Hvid Display Modul til Arduino 51 Msp420 Stim32 SCR Køb Nu På Amazon $ 9.99

Enhver Arduino skal arbejde, så kig på vores købsvejledning Arduino Købsvejledning: Hvilket bord skal du få? Arduino Købsvejledning: Hvilket bestyrelse skal du få? Der er så mange forskellige slags Arduino boards derude, du ville blive tilgivet for at være forvirret. Hvad skal du købe til dit projekt? Lad os hjælpe med denne Arduino købeguide! Læs mere, hvis du ikke er sikker på hvilken model du vil købe.

Disse OLED-skærme er meget seje. De kan normalt købes i hvid, blå, gul eller en blanding af de tre. De eksisterer i fuld farve, men disse tilføjer et helt andet niveau til kompleksiteten og omkostningerne ved dette projekt.

Kredsløbet

Dette er ganske simpelt kredsløb. Hvis du ikke har meget erfaring med Arduino, skal du tjekke disse begynderprojekter. 10 Great Arduino-projekter for begyndere. 10 Great Arduino-projekter for begyndere. Gennemførelse af et Arduino-projekt giver dig en følelse af tilfredshed som ingen anden. De fleste begyndere er ikke sikre på, hvor de skal starte, og selv begynderprojekter kan virke temmelig skræmmende. Læs mere først.

Her er det:

Pongbrødbræt

Kig på forsiden af ​​puljen, tilslut den venstre stift til + 5V og den rigtige stift til jorden . Tilslut midterstiften til analogstiften 0 (A0).

OLED-displayet er tilsluttet ved hjælp af I2C-protokollen. Tilslut VCC og GND til Arduino + 5V og jord . Tilslut SCL til analog fem ( A5 ). Tilslut SDA til analog 4 ( A4 ). Grunden til dette er forbundet med de analoge stifter er simpel; disse stifter indeholder kredsløbet, der kræves til I2C-protokollen. Sørg for at disse er tilsluttet korrekt og ikke krydset over. De nøjagtige stifter varierer efter model, men A4 og A5 bruges på Nano og Uno. Kontroller Wirebibliotekets dokumentation for din model, hvis du ikke bruger en Arduino eller Nano.

Pot Test

Upload denne testkode (sørg for at vælge det rigtige kort og port fra Værktøjer > Styring og Værktøjer > Portmenuer):

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

Åbn nu seriel skærmen ( Top Right > Serial Monitor ) og drej potten. Du bør se den værdi, der vises på seriel skærmen. Helt mod uret skal være nul, og helt med uret skal være 1023 :

Pong seriel skærm

Du vil justere dette senere, men det er fint nu. Hvis der ikke sker noget, eller værdien ændres, uden at du gør noget, skal du afbryde og tjekke kredsløbet.

OLED Test

OLED-grafik

OLED-skærmen er lidt mere kompleks at konfigurere. Du skal installere to biblioteker for at køre displayet først. Download Adafruit_SSD1306 og Adafruit-GFX biblioteker fra Github. Kopier filerne til bibliotekets mappe. Dette varierer afhængigt af dit operativsystem:

  • Mac OS: / Brugere / Brugernavn / Dokumenter / Arduino / Biblioteker
  • Linux: / home / Brugernavn / Skitsebog
  • Windows: / Brugere / Arduino / biblioteker

Upload nu en testskitse. Gå til Filer > Eksempler > Adafruit SSD1306 > ssd1306_128x64_i2c . Dette skal give dig en stor skitse, der indeholder masser af grafik:

OLED-grafik

Hvis der ikke sker noget, når du uploader, afbryd og tjek dine forbindelser. Hvis eksemplerne ikke findes i menuerne, skal du muligvis genstarte din Arduino IDE.

Koden

Nu er det tid til koden. Jeg vil forklare hvert trin, så spring til slutningen, hvis du bare vil få det til at køre. Dette er en del kode, så hvis du ikke føler dig selvsikker, skal du tjekke disse 10 gratis ressourcer. Lær at kode: 10 gratis og fantastiske online ressourcer til at udnytte dine evner. Lær at kode: 10 gratis og fantastiske online ressourcer til at finpudse din Færdighedskodning. Et emne, der undgås af mange. Der er en overflod af gratis ressourcer og værktøjer, som alle er tilgængelige online. Sikker på at du kan tage nogle kurser om emnet på en nærliggende ... Læs mere for at lære at kode.

Start med at inkludere de nødvendige biblioteker:

 #include #include #include #include 

SPI og WIRE er to Arduino-biblioteker til håndtering af I2C-kommunikationen. Adafruit_GFX og Adafruit_SSD1306 er de biblioteker, du tidligere har installeret.

Dernæst konfigurer displayet:

 Adafruit_SSD1306 display(4); 

Derefter opsæt alle de nødvendige variable til at kø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 gemmer alle de data, der er nødvendige for at køre spillet. Nogle af disse gemmer placeringen af ​​bolden, størrelsen på skærmen, afspillerenes placering osv. Bemærk hvordan nogle af disse er const betyder at de er konstante, og vil aldrig ændre sig. Dette gør det muligt for Arduino-kompilatoren at få fart på ting.

Skærmopløsningen og kuglens placering gemmes i arrayer . Arrays er samlinger af lignende ting, og for bolden gemme koordinaterne ( X og Y ). Adgang til elementer i arrays er let (ikke inkluder denne kode i din fil):

 resolution[1]; 

Da arrayer starter ved nul, vil dette returnere det andet element i opløsnings arrayet ( 64 ). Opdatering af elementer er endnu nemmere (igen, ikke inkluder denne kode):

 ball[1] = 15; 

Inden tomrumsopsætning (), konfigurer displayet:

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

Den første linje fortæller Adafruit-biblioteket, hvilke dimensioner og kommunikationsprotokol din skærm bruger (i dette tilfælde 128 x 64 og I2C ). Den anden linje ( display.display () ) fortæller skærmen at vise, hvad der er gemt i bufferen (hvilket ikke er noget).

Opret to metoder kaldet 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 tager kuglens x og y- koordinater og trækker det på skærmen ved hjælp af drawCircle- metoden fra displaybibliotekerne. Dette bruger den konstante BALL_SIZE defineret tidligere. Prøv at ændre dette og se, hvad der sker. Denne drawCircle metode accepterer en pixel farve - BLACK eller WHITE . Da dette er en monokromatisk skærm (en farve), svarer hvid til, at en pixel er tændt, og sort slukkes for pixel.

Opret nu en metode kaldet moveAi :

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

Denne metode håndterer at flytte Artificial Intelligence eller AI- afspilleren. Dette er en ganske enkel computer modstander - Hvis bolden er over padleen, skal du flytte op. Den er under padle, flytte ned. Ganske enkelt, men det fungerer godt. Symbolerne for stigning og formindskelse anvendes ( ++ aiPos og -aiPos ) for at tilføje eller trække en fra aiPositionen. Du kan tilføje eller subtrahere et større antal for at få AI'en til at bevæge sig hurtigere, og derfor være sværere at slå. Sådan gør du det:

 aiPos += 2; 

Og:

 aiPos -= 2; 

Plus Equals og Minus Equals tegnene er stenografi for at tilføje eller subtrahere to fra / til den nuværende værdi af aiPos. Her er en anden måde at gøre det på:

 aiPos = aiPos + 2; 

og

 aiPos = aiPos - 1; 

Bemærk, hvordan denne metode først sletter padleen, og trækker den igen. Dette skal gøres sådan. Hvis den nye position af padlen blev trukket, ville der være to overlappende padle på skærmen.

DrawNet- metoden bruger to løkker til at 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 bruger WALL_WIDTH variablerne til at indstille størrelsen.

Opret metoder kaldet drawPixels og erasePixels . Ligesom boldmetoderne er den eneste forskel mellem disse to farver i 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); } } } 

Igen bruger begge disse metoder to til sløjfer til at tegne en gruppe af pixels. I stedet for at skulle tegne hver pixel ved hjælp af bibliotekernes drawPixel- metode tegner sløjferne en gruppe af pixels baseret på de givne dimensioner.

DrawScore- metoden bruger tekstfunktionerne i biblioteket til at skrive afspilleren og AI-score til skærmen. Disse er gemt 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 metode har også en eraseScore- modpart, der sætter pixelerne til sort eller slukket.

De sidste fire metoder er meget ens. De tegner og sletter afspilleren og AI padlerne:

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

Bemærk hvordan de kalder erasePixel- metoden oprette tidligere. Disse metoder tegner og sletter den relevante padle.

Der er lidt mere logik 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 hvad du ender med:

OLED Pong

Når du er sikker på koden, er der mange ændringer, du kan gøre:

  • Tilføj en menu til sværhedsgrader (skift AI og boldhastighed).
  • Tilføj nogle tilfældige bevægelser til bolden eller AI.
  • Tilføj en anden pot til to spillere.
  • Tilføj en pauseknap.

Nu kig på disse retro spil Pi Zero-projekter 5 Retro Gaming-projekter med Raspberry Pi Zero 5 Retro Gaming-projekter med Raspberry Pi Zero Raspberry Pi Zero har taget DIY og homebrew verden med storm, hvilket gør det muligt at revidere gamle projekter og inspirerende nykommere, især i de fejrede sind for retro gaming fans. Læs mere .

Har du kodet Pong ved hjælp af denne kode? Hvilke modifikationer har du lavet? Lad mig vide i kommentarerne nedenfor, jeg vil gerne se nogle billeder!

In this article