Arduino Retro Gaming med en OLED-skärm
Någonsin undrat hur mycket arbete det tar att skriva egna retrospel? Hur lätt är Pong att koda för Arduino? Följ med mig när jag visar dig hur man bygger en Arduino-driven mini retro spelkonsol, och hur man kodar Pong från början. Här är slutresultatet:
Byggplan
Detta är en ganska enkel krets. en potentiometer (potten) kommer att styra spelet, och en OLED-skärm kommer att drivas av Arduino. Detta kommer att produceras på en brödbräda, men du kanske vill göra det till en permanent krets och installera den i ett fall. Vi har skrivit om att återskapa Pong Hur man återskapar det klassiska Pong-spelet med Arduino Hur man återskapar det klassiska Pong-spelet Använda Arduino Pong var det första videospel som nått massmarknaden. För första gången i historien togs begreppet "videospel" in i familjen hem, tack vare Atari 2600 - ... Läs mer innan, men idag kommer jag att visa dig hur man skriver koden från början och bryter ner varje del.
Vad du behöver
Här är vad du behöver:
- 1 x Arduino (någon modell)
- 1 x 10k Potentiometer
- 1 x 0,96 "I2C OLED Display
- 1 x brödbräda
- Assorterad hane> Hankopplingskablar
DIYmall 0.96 "Inch I2c IIC Seriell 128x64 Oled LCD LED Vit Visningsmodul för Arduino 51 Msp420 Stim32 SCR DIYmall 0.96" Inch I2c IIC Seriell 128x64 Oled LCD LCD Vit Vit Modul för Arduino 51 Msp420 Stim32 SCR Köp nu hos Amazon $ 8.99
Alla Arduino ska fungera, så titta på vår köpguide Arduino Köpguide: Vilket styrelse ska du få? Arduino Köpguide: Vilket styrelse ska du få? Det finns så många olika typer av Arduino brädor där ute, du skulle bli förlåtad för att vara förvirrad. Vilket ska du köpa för ditt projekt? Låt oss hjälpa till med den här Arduino-köpguiden! Läs mer om du inte är säker på vilken modell du vill köpa.
Dessa OLED-skärmar är väldigt coola. De kan vanligtvis köpas i vit, blå, gul eller en blandning av de tre. De existerar i fullfärg, men dessa lägger en helt annan nivå till komplexiteten och kostnaden för projektet.
Kretskortet
Det här är en ganska enkel krets. Om du inte har mycket erfarenhet av Arduino, kolla in dessa nybörjare projekt 15 Great Arduino Projekt för nybörjare 15 Great Arduino Projekt för nybörjare Intresserad av Arduino men inte säker på var du ska börja? Här är några av våra bästa Arduino-projekt för nybörjare att komma igång med! Läs mer först.
Här är det:
Titta på krukans framsida, anslut vänster stift till +5V och rätt stift till jord. Anslut mellannålen till analog stift 0 (A0).
OLED-skärmen är ansluten med hjälp av I2C-protokollet. Ansluta VCC och GND till Arduino +5V och jord. Ansluta SCL till analog fem (A5). Ansluta SDA till analog 4 (A4). Anledningen till att detta är anslutet till analoga stiften är enkelt; Dessa stiften innehåller de kretsar som krävs för I2C-protokollet. Se till att dessa är korrekt anslutna och inte korsade. De exakta stiften varierar beroende på modell, men A4 och A5 används på Nano och Uno. Kontrollera dokumentationen för trådbiblioteket för din modell om du inte använder en Arduino eller Nano.
Pottest
Ladda upp denna testkod (se till att du väljer rätt kartong och port från Verktyg > Styrelse och Verktyg > Hamn menyer):
void setup () // sätt din setup code här, för att köra en gång: Serial.begin (9600); // setup serial void loop () // sätt in huvudkoden här, för att springa upprepade gånger: Serial.println (analogRead (A0)); // skriva ut värdet från potten fördröjning (500);
Öppna nu seriekontrollen (Överst till höger > Serial Monitor) och vrid krukan. Du bör se det värde som visas på seriell bildskärm. Helt moturs bör vara noll-, och helt medurs bör vara 1023:
Du kommer att justera detta senare, men för nu är det bra. Om inget händer, eller värdet ändras utan att du gör någonting, koppla loss och dubbelkontrollera kretsen.
OLED-test
OLED-skärmen är lite mer komplex att konfigurera. Du måste installera två bibliotek för att driva displayen först. Ladda ner Adafruit_SSD1306 och Adafruit-GFX-bibliotek från Github. Kopiera filerna till bibliotekets mapp. Detta varierar beroende på ditt operativsystem:
- Mac OS: / Användare / användarnamn / Dokument / Arduino / bibliotek
- Linux: / Home / användarnamn / Sketchbook
- Windows: / Användare / Arduino / bibliotek
Ladda nu upp en testskissa. Gå till Fil > exempel > Adafruit SSD1306 > ssd1306_128x64_i2c. Detta borde ge dig en stor skiss som innehåller mycket grafik:
Om inget händer efter uppladdning, koppla ifrån och dubbelkontrollera dina anslutningar. Om exemplen inte finns i menyerna kan du behöva starta om din Arduino IDE.
Koden
Nu är det dags för koden. Jag kommer att förklara varje steg, så hoppa till slutet om du bara vill få den att springa. Det här är en hel del kod, så om du inte känner dig säker, kolla in de 10 gratis resurserna Lär dig att koda: 10 gratis och fantastiska online-resurser för att fånga dina färdigheter Lär dig att koda: 10 gratis och fantastiska online-resurser för att fånga din Färdighetskodning. Ett ämne som undviks av många. Det finns ett överflöd av gratis resurser och verktyg, som alla är tillgängliga online. Visst kan du ta några kurser på ämnet på en närliggande ... Läs mer för att lära sig att koda.
Börja med att inkludera nödvändiga bibliotek:
#inkludera #inkludera #inkludera #inkludera
SPI och TRÅD är två Arduino-bibliotek för hantering av I2C-kommunikation. Adafruit_GFX och Adafruit_SSD1306 är de bibliotek du installerat tidigare.
Konfigurera sedan skärmen:
Adafruit_SSD1306 display (4);
Installera sedan alla variabler som behövs för att köra spelet:
int upplösning [2] = 128, 64, boll [2] = 20, (upplösning [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'; booleska inProgress = true;
Dessa lagrar all information som krävs för att köra spelet. Några av dessa lagrar platsen för bollen, skärmens storlek, platsen för spelaren och så vidare. Lägg märke till hur några av dessa är const vilket betyder att de är konstanta och kommer aldrig att förändras. Detta gör att Arduino-kompilatorn snabbar upp saker och ting.
Skärmens upplösning och bollplats lagras i arrayer. Arrays är samlingar av liknande saker, och för bollen, lagra koordinaterna (X och Y). Åtkomst till element i arrayer är enkelt (inkludera inte denna kod i din fil):
resolution [1];
När arrays startar vid noll kommer detta att returnera det andra elementet i upplösningsuppsättningen (64). Uppdatera element är ännu enklare (igen, inkludera inte denna kod):
boll [1] = 15;
Inuti void setup (), konfigurera displayen:
void setup () display.begin (SSD1306_SWITCHCAPVCC, 0x3C); display.display ();
Den första raden berättar för Adafruit-biblioteket vilka dimensioner och kommunikationsprotokoll din bildskärm använder (i det här fallet, 128 x 64 och I2C). Den andra raden (display.display ()) berättar skärmen för att visa vad som lagras i bufferten (vilket är ingenting).
Skapa två metoder som heter drawBall och eraseBall:
void drawBall (intx, int y) display.drawCircle (x, y, BALL_SIZE, WHITE); void eraseBall (int x, int y) display.drawCircle (x, y, BALL_SIZE, BLACK);
Dessa tar x och y bollens koordinater och dra den på skärmen med hjälp av drawcircle metod från visningsbiblioteken. Detta använder konstanten BALL_SIZE definieras tidigare. Försök ändra detta och se vad som händer. Denna drawCircle-metod accepterar en pixelfärg - SVART eller VIT. Eftersom detta är en monokromatisk display (en färg), motsvarar vitt en pixel på och svart svänger pixeln av.
Skapa nu en metod som heter moveAi:
void moveAi () eraseAiPaddle (aiPos); om (boll [1]> aiPos) ++ aiPos; annars om (boll [1] < aiPos) --aiPos; drawAiPaddle(aiPos);
Denna metod hanterar att flytta Artificiell intelligens eller AI spelare. Det här är en ganska enkel dator motståndare - Om bollen ligger ovanför paddeln, rör dig uppåt. Den är under padden, flytta ner. Ganska enkelt, men det fungerar bra. Symbolerna för ökning och minskning används (++aiPos och -aiPos) för att lägga till eller subtrahera en från aiPositionen. Du kan lägga till eller subtrahera ett större antal för att AI ska röra sig snabbare och därför vara svårare att slå. Så här skulle du göra det:
aiPos + = 2;
Och:
aiPos - = 2;
De Plus är lika och Minus är lika tecken är stenografi för att lägga till eller subtrahera två från / till det aktuella värdet av aiPos. Här är ett annat sätt att göra det:
aiPos = aiPos + 2;
och
aiPos = aiPos - 1;
Lägg märke till hur den här metoden först raderar padden och dra sedan den igen. Detta måste göras så här. Om den nya positionen av paddeln drogs skulle det finnas två överlappande paddlar på skärmen.
De drawNet Metoden använder två slingor för att rita nätet:
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);
Detta använder WALL_WIDTH variabler för att ställa in sin storlek.
Skapa metoder som heter drawPixels och erasePixels. Precis som bollmetoderna är den enda skillnaden mellan dessa två färgerna på pixlarna:
void drawPixel (int posX, int posY, int dimensioner) 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);
Återigen använder båda dessa metoder två för loopar för att rita en grupp pixlar. Snarare än att behöva rita varje pixel med hjälp av biblioteken drawPixel metod, slingorna ritar en grupp pixlar baserat på de givna dimensionerna.
De drawScore Metoden använder textfunktionerna i biblioteket för att skriva spelarens och AI-poäng till skärmen. Dessa lagras i playerScore och aiScore:
void drawScore () display.setTextSize (2); display.setTextColor (VIT); display.setCursor (45, 0); display.println (playerScore); display.setCursor (75, 0); display.println (aiScore);
Denna metod har också a eraseScore motpart, som ställer pixlarna på svart eller avstängt.
De sista fyra metoderna är mycket lika. De ritar och raderar spelaren och AI paddlarna:
void erasePlayerPaddle (int row) erasePixel (0, rad - (PADDLE_WIDTH * 2), PADDLE_WIDTH); erasePixel (0, rad - PADDLE_WIDTH, PADDLE_WIDTH); erasePixel (0, rad, PADDLE_WIDTH); erasePixel (0, rad + PADDLE_WIDTH, PADDLE_WIDTH); erasePixel (0, rad + (PADDLE_WIDTH + 2), PADDLE_WIDTH);
Lägg märke till hur de kallar erasePixel metod skapar tidigare. Dessa metoder ritar och tar bort lämplig paddla.
Det finns lite mer logik i huvudslingan. Här är hela koden:
#inkludera #inkludera #inkludera #inkludera Adafruit_SSD1306 display (4); int upplösning [2] = 128, 64, boll [2] = 20, (upplösning [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'; booleska inProgress = true; void setup () display.begin (SSD1306_SWITCHCAPVCC, 0x3C); display.display (); void loop () if (aiScore> 9 || playerScore> 9) // kontrollera spelstatus inProgress = false; om (inProgress) eraseScore (); eraseBall (boll [0], boll [1]); om (ballDirectionVerti == 'U') // flytta bollen upp diagonalt boll [1] = boll [1] - SPEED; om (ballDirectionVerti == 'D') // flytta bollen ner diagonalt boll [1] = boll [1] + SPEED; om (boll [1] <= 0) // bounce the ball off the top ballDirectionVerti = 'D'; if (ball[1] >= upplösning [1]) // studsa bollen från botten ballDirectionVerti = 'U'; om (ballDirectionHori == 'R') boll [0] = boll [0] + SPEED; // flytta bollen om (boll [0]> = (upplösning [0] - 6)) // bollen ligger vid skärmens AI-kant om ((aiPos + 12)> = boll [1] && (aiPos - 12) <= ball[1]) // ball hits AI paddle if (ball[1] > (aiPos + 4)) // avböja bollen ner ballDirectionVerti = 'D'; annars om (boll [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) >= boll [1] && (playerPos - 12) <= ball[1]) // ball hits player paddle if (ball[1] > (playerPos + 4)) // avböja bollen ner ballDirectionVerti = 'D'; annars om (boll [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!"); annars om (playerScore> aiScore) display.println ("YOU WIN!"); display.display (); void moveAi () // flytta AI paddle eraseAiPaddle (aiPos); om (boll [1]> aiPos) ++ aiPos; annars om (boll [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);
Här är vad du hamnar med:
När du är säker på koden finns det många modifieringar du kan göra:
- Lägg till en meny för svårighetsnivåer (ändra AI och bollhastighet).
- Lägg till lite slumpmässig rörelse till bollen eller AI.
- Lägg till en annan gryta för två spelare.
- Lägg till en pausknapp.
Ta en titt på dessa retrospel Pi Zero-projekt 5 Retro Gaming Projekt med Raspberry Pi Zero 5 Retro Gaming Projekt med Raspberry Pi Zero Raspberry Pi Zero har tagit DIY och Homebrew World med storm, vilket gör det möjligt att revidera gamla projekt och inspirerande nykomlingar, i synnerhet i retrospelsfansens feverade sinnen. Läs mer .
Har du kodat Pong med den här koden? Vilka ändringar gjorde du? Låt mig veta i kommentarerna nedan, jag skulle gärna vilja se några bilder!
Utforska mer om: Arduino, Elektronik, Retrospel.