Arduino Retro-spil med en OLED-skærm

  • Harry James
  • 0
  • 2961
  • 353
Reklame

Har du nogensinde spekuleret på, hvor meget arbejde det kræver at skrive dine egne retrospil? Hvor let er Pong at kode for Arduino? Deltag i mig, mens jeg viser dig, hvordan du bygger en Arduino-drevet mini-retro-konsol, og hvordan du kode Pong fra bunden. Her er slutresultatet:

Byg plan

Dette er et ret simpelt kredsløb. EN potentiometer (pot) kontrollerer spillet, og en OLED-skærm vil blive drevet af Arduino. Dette vil blive produceret på en brødbræt, men du ønsker måske at gøre dette til et permanent kredsløb og installere det i et tilfælde. Vi har skrevet om at genskabe Pong Sådan genskabes det klassiske Pong-spil ved hjælp af Arduino Sådan genskabes det klassiske Pong-spil Brug af Arduino Pong var det første videospil nogensinde, der nåede til massemarkedet. For første gang i historien blev konceptet med et "videospil" bragt ind i familiens hjem takket være Atari 2600 -… før, dog i dag, viser jeg dig hvordan du skriver koden fra bunden og nedbryder alle en del.

Hvad du har brug for

Her er hvad du har brug for:

  • 1 x Arduino (enhver model)
  • 1 x 10k potentiometer
  • 1 x 0,96 "I2C OLED-skærm
  • 1 x Breadboard
  • Assorterede mandlige> mandlige tilslutningstråde

Enhver Arduino skal fungere, så se på vores købsguide Arduino Købsguide: Hvilket bord skal du få? Arduino Købsvejledning: Hvilket bord skal du få? Der er så mange forskellige slags Arduino-tavler derude, du vil blive tilgitt for at blive forvirret. Hvilket skal du købe til dit projekt? Lad os hjælpe med denne Arduino-købsguide! hvis du ikke er sikker på, hvilken model du skal 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 findes i fuld farve, men disse tilføjer et helt andet niveau til kompleksiteten og omkostningerne ved dette projekt.

Kredsløbet

Dette er et ganske enkelt kredsløb. Hvis du ikke har meget erfaring med Arduino, så tjek disse begynderprojekter 15 Store Arduino-projekter for begyndere 15 Store Arduino-projekter for begyndere Interesseret i Arduino-projekter, men er ikke sikker på, hvor de skal begynde? Disse nybegynderprojekter vil lære dig, hvordan du kommer i gang. først.

Her er det:

Når du ser på fronten af ​​gryden, skal du tilslutte den venstre pin til +5V og den rigtige pin til jord. Tilslut den midterste pin til analog pin 0 (A0).

OLED-displayet er tilsluttet ved hjælp af I2C-protokollen. Opret forbindelse VCC og GND til Arduino +5V og jord. Opret forbindelse SCL til analog fem (A5). Opret forbindelse SDA til analog 4 (A4). Årsagen til at dette er forbundet med de analoge stifter er enkel; disse ben indeholder det kredsløb, der kræves til I2C-protokollen. Sørg for, at disse er korrekt tilsluttet og ikke krydses. De nøjagtige stifter varierer efter model, men A4 og A5 bruges på Nano og Uno. Se Wire-bibliotekets 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øj > Bestyrelse og Værktøj > Havn menuer):

ugyldig opsætning () // læg din opsætningskode her for at køre en gang: Serial.begin (9600); // setup serial void loop () // læg din hovedkode her for at køre gentagne gange: Serial.println (analogRead (A0)); // udskriv værdien fra pot forsinkelsen (500); 

Åbn nu den serielle skærm (Øverst til højre > Seriel skærm) og vend gryden. Du skal se den værdi, der vises på den serielle skærm. Helt mod uret skal være nul, og helt med uret skal være 1023:

Du justerer dette senere, men i øjeblikket er det fint. Hvis der ikke sker noget, eller værdien ændrer sig, uden at du gør noget, skal du afbryde forbindelsen og dobbelt kontrollere kredsløbet.

OLED-test

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

  • Mac OS: / Brugere / brugernavn / Dokumenter / Arduino / biblioteker
  • Linux: / Home / brugernavn / Sketchbook
  • Windows: / Brugere / Arduino / biblioteker

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

Hvis der ikke sker noget efter upload, skal du afbryde forbindelsen og kontrollere den igen. 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 rimelig mængde kode, så hvis du ikke føler dig selvsikker, så tjek disse 10 gratis ressourcer Lær at kode: 10 gratis og fantastiske online ressourcer til at finpudse dine færdigheder 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 kunne tage nogle kurser om emnet i et nærliggende ... for at lære at kode.

Start med at inkludere de nødvendige biblioteker:

#include #include #include #include 

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

Konfigurer derefter skærmen:

Adafruit_SSD1306 display (4);

Indstil derefter alle de nødvendige variabler for at køre spillet:

int-opløsning [2] = 128, 64, bold [2] = 20, (oplø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'; boolsk inProgress = sand;

Disse gemmer alle de nødvendige data for at køre spillet. Nogle af disse gemmer placeringen af ​​bolden, størrelsen på skærmen, placeringen af ​​spilleren og så videre. Bemærk, hvordan nogle af disse er konst hvilket betyder at de er konstante og vil aldrig ændre sig. Dette lader Arduino-kompilatoren fremskynde tingene lidt.

Skærmopløsningen og kuglens placering gemmes i arrays. Arrays er samlinger af lignende ting, og for kuglen skal du gemme koordinaterne (x og Y). Det er let at få adgang til elementer i matriser (medtag ikke denne kode i din fil):

beslutning [1];

Når arrays starter ved nul, returnerer dette det andet element i opløsningsarrayet (64). Det er endnu lettere at opdatere elementer (igen, medtag ikke denne kode):

kugle [1] = 15;

Inde ugyldig opsætning (), konfigurer skærmen:

tom opsætning () 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 kaldte metoder 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 x og y koordinater for bolden og tegne den på skærmen ved hjælp af drawCircle metode fra skærmbibliotekerne. Dette bruger konstanten BALL_SIZE defineret tidligere. Prøv at ændre dette og se, hvad der sker. Denne drawCircle-metode accepterer en pixelfarve - SORT eller HVID. Da dette er en monokromatisk skærm (en farve), svarer hvid til, at en pixel er tændt, og sort slukker pixlen.

Opret nu en metode, der kaldes moveAi:

void moveAi () eraseAiPaddle (aiPos); if (bold [1]> aiPos) ++ aiPos;  andet hvis (bold [1] < aiPos)  --aiPos;  drawAiPaddle(aiPos); 

Denne metode håndterer flytning af Kunstig intelligens eller AI spiller. Dette er en ganske simpel computermodstander - Hvis bolden er over padlen, skal du gå op. Det er under skovlen, bevæg dig ned. Ganske enkelt, men det fungerer godt. Forøgelses- og dekrementssymbolerne bruges (++aiPos og -aiPos) for at tilføje eller trække en fra aiPositionen. Du kan tilføje eller trække et større antal for at få AI til at gå hurtigere og derfor være vanskeligere at slå. Sådan gør du:

aiPos + = 2;

Og:

aiPos - = 2;

Det Plus lige og Minus lige tegn er kortfattet til at tilføje eller trække to fra / til den aktuelle 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 padlen og derefter trækker den igen. Dette skal gøres sådan. Hvis den nye position af pagajen blev trukket, ville der være to overlappende padler på skærmen.

Det drawNet metoden bruger to løkker til at trække 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 variabler for at indstille dets størrelse.

Opret opkaldte metoder drawPixels og erasePixels. Ligesom kuglemetoderne er den eneste forskel mellem disse to farverne på pixels:

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

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

Det 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.set markør (45, 0); display.println (playerScore); display.set markør (75, 0); display.println (aiScore); 

Denne metode har også en eraseScore modstykke, der sætter pixels til sort eller slukket.

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

void erasePlayerPaddle (int række) erasePixel (0, række - (PADDLE_WIDTH * 2), PADDLE_WIDTH); slet Pixel (0, række - PADDLE_WIDTH, PADDLE_WIDTH); slet Pixel (0, række, PADDLE_WIDTH); slet Pixel (0, række + PADDLE_WIDTH, PADDLE_WIDTH); slet Pixel (0, række + (PADDLE_WIDTH + 2), PADDLE_WIDTH); 

Bemærk, hvordan de kalder erasePixel oprette metode tidligere. Disse metoder tegner og sletter den passende padle.

Der er en smule mere logik i hovedløkken. Her er hele koden:

#include #include #include #include Adafruit_SSD1306 display (4); int-opløsning [2] = 128, 64, bold [2] = 20, (oplø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'; boolsk inProgress = sand; tom opsætning () display.begin (SSD1306_SWITCHCAPVCC, 0x3C); display.display ();  void loop () if (aiScore> 9 || playerScore> 9) // check game state inProgress = false;  if (inProgress) eraseScore (); eraseBall (bold [0], bold [1]); if (ballDirectionVerti == 'U') // flytte bolden op diagonalt kugle [1] = bold [1] - HASTIGHED;  if (ballDirectionVerti == 'D') // bevæg kuglen diagonalt ned kugle [1] = kugle [1] + HASTIGHED;  if (bold [1] = opløsning [1]) // afvisning af bolden fra den nederste boldDirectionVerti = 'U';  if (ballDirectionHori == 'R') ball [0] = ball [0] + HASTIGHED; // bevæg kugle hvis (kugle [0]> = (opløsning [0] - 6)) // kugle er ved AI-kanten af ​​skærmen, hvis ((aiPos + 12)> = kugle [1] && (aiPos - 12) (aiPos + 4)) // afbøj kuglen ned ad ballDirectionVerti = 'D';  andet hvis (bold [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]  playerScore)  display.println("YOU LOSE!");  else if (playerScore > aiScore) display.println ("DU VINDER!");  display.display ();  void moveAi () // flyt AI-paddle eraseAiPaddle (aiPos); if (bold [1]> aiPos) ++ aiPos;  andet hvis (bold [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:

Når du er overbevist om koden, er der adskillige ændringer, du kan foretage:

  • Tilføj en menu til sværhedsgrader (skift AI og kuglehastighed).
  • Føj nogle tilfældige bevægelser til bolden eller AI.
  • Tilføj en anden pot til to spillere.
  • Tilføj en pause-knap.

Se nu på disse retro gaming 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 hjemmebryggelsesverdenen med storm, hvilket gør det muligt at revidere gamle projekter og inspirerende nybegynderne, især i de retro-fans af hjerte. .

Har du kodet Pong ved hjælp af denne kode? Hvilke ændringer foretog du? Fortæl mig det i kommentarerne nedenfor, jeg vil meget gerne se nogle billeder!




Endnu ingen kommentarer

Om moderne teknologi, enkel og overkommelig.
Din guide i en verden af moderne teknologi. Lær hvordan du bruger de teknologier og gadgets, der omgiver os hver dag, og lær, hvordan du finder interessante ting på Internettet.