Vi siete mai chiesti quanto lavoro ci vuole per scrivere i propri giochi retrò? Quanto è facile codificare Pong per l'Arduino? Unisciti a me mentre ti mostro come costruire una mini console per giochi retrò retroata con Arduino e come codificare Pong da zero. Ecco il risultato finale:
Piano di costruzione
Questo è un circuito abbastanza semplice. Un potenziometro (piatto) controllerà il gioco, e un display OLED sarà guidato da Arduino. Questo verrà prodotto su una breadboard, tuttavia potresti voler rendere questo circuito permanente e installarlo in una custodia. Abbiamo scritto di ricreare Pong Come ricreare il classico gioco Pong usando Arduino Come ricreare il classico gioco Pong Usando Arduino Pong è stato il primo videogioco in assoluto che ha raggiunto il mercato di massa. Per la prima volta nella storia, il concetto di "videogioco" è stato portato nella casa di famiglia, grazie all'Atari 2600 -... Leggi di più prima, tuttavia oggi ti mostrerò come scrivere il codice da zero, e abbattere ogni parte.
Quello che ti serve
Ecco di cosa hai bisogno:
- 1 x Arduino (qualsiasi modello)
- 1 x 10k Potenziometro
- Display OLED I2C da 1 x 0, 96 "
- 1 x tagliere
- Fili di collegamento maschio> maschio assortiti
Diymall 0.96 "Pollici I2c IIC Serial 128x64 Oled LCD Display a LED Bianco per Arduino 51 Msp420 Stim32 SCR Diymall 0.96" Pollici I2c IIC Serial 128x64 Oled LCD Display a LED bianchi per Arduino 51 Msp420 Stim32 SCR Acquista ora su Amazon $ 9, 99
Qualsiasi Arduino dovrebbe funzionare, quindi consulta la nostra guida all'acquisto Guida all'acquisto di Arduino: quale scheda dovresti acquistare? Guida all'acquisto di Arduino: quale scheda si dovrebbe ottenere? Ci sono così tanti diversi tipi di tavole Arduino là fuori, ti verrebbe perdonato per essere stato confuso. Quale dovresti comprare per il tuo progetto? Aiutaci, con questa guida all'acquisto di Arduino! Leggi di più se non sei sicuro del modello da acquistare.
Questi display OLED sono molto interessanti. Di solito possono essere acquistati in bianco, blu, giallo o una miscela dei tre. Esistono a colori, tuttavia questi aggiungono un altro livello alla complessità e al costo di questo progetto.
Il circuito
Questo è un circuito abbastanza semplice. Se non hai molta esperienza con Arduino, dai un'occhiata a questi progetti per principianti 10 Grandi progetti Arduino per principianti 10 Grandi progetti Arduino per principianti Completare un progetto Arduino ti dà un senso di soddisfazione come nessun altro. La maggior parte dei principianti non è sicura da dove iniziare, e anche i progetti per principianti possono sembrare piuttosto scoraggianti. Per saperne di più prima.
Ecco qui:
Guardando la parte anteriore del piatto, collega il pin sinistro a + 5V e il pin destro a terra . Collegare il pin centrale al pin analogico 0 (A0).
Il display OLED è collegato tramite il protocollo I2C. Connetti VCC e GND a Arduino + 5V e terra . Collega SCL all'analogo cinque ( A5 ). Collega l' SDA all'analogico 4 ( A4 ). Il motivo per cui è collegato ai pin analogici è semplice; questi pin contengono la circuiteria richiesta per il protocollo I2C. Assicurarsi che siano collegati correttamente e non incrociati. I pin esatti varieranno in base al modello, ma A4 e A5 vengono utilizzati su Nano e Uno. Controlla la documentazione della libreria Wire per il tuo modello se non stai utilizzando un Arduino o un Nano.
Pot Test
Carica questo codice di prova (assicurati di selezionare la scheda e la porta corrette da Strumenti > Schede e Strumenti > Menu porta ):
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); }
Ora apri il monitor seriale (in alto a destra > Monitor seriale ) e ruota il piatto. Dovresti vedere il valore visualizzato sul monitor seriale. Completamente in senso antiorario dovrebbe essere zero, e completamente in senso orario dovrebbe essere 1023 :
La regolerai in seguito, ma per ora va bene. Se non succede nulla, o il valore cambia senza che tu faccia nulla, disconnetti e ricontrolla il circuito.
Test OLED
Il display OLED è leggermente più complesso da configurare. È necessario installare due librerie per guidare prima il display. Scarica le librerie Adafruit_SSD1306 e Adafruit-GFX da Github. Copia i file nella cartella delle tue librerie. Questo varia a seconda del tuo sistema operativo:
- Mac OS: / Utenti / Nome utente / Documenti / Arduino / librerie
- Linux: / home / Nome utente / Sketchbook
- Windows: / Utenti / Arduino / librerie
Ora carica uno schizzo di prova. Vai su File > Esempi > Adafruit SSD1306 > ssd1306_128x64_i2c . Questo dovrebbe darti un grande schizzo contenente molti elementi grafici:
Se non succede nulla dopo il caricamento, disconnettere e ricontrollare le connessioni. Se gli esempi non sono nei menu, potrebbe essere necessario riavviare il tuo IDE Arduino.
Il codice
Ora è il momento per il codice. Spiegherò ogni passo, quindi salta alla fine se vuoi solo farlo funzionare. Questa è una buona quantità di codice, quindi se non ti senti sicuro, dai un'occhiata a queste 10 risorse gratuite. Impara a codificare: 10 risorse online gratuite e fantastiche per affinare le tue competenze. Impara a codificare: 10 risorse online gratuite e fantastiche per affinare il tuo Codifica delle competenze. Un argomento che viene evitato da molti. Ci sono un'abbondanza di risorse e strumenti gratuiti, tutti disponibili online. Certo, potresti seguire alcuni corsi sull'argomento in un vicino ... Leggi altro per imparare a programmare.
Inizia includendo le librerie necessarie:
#include #include #include #include
SPI e WIRE sono due librerie Arduino per la gestione della comunicazione I2C. Adafruit_GFX e Adafruit_SSD1306 sono le librerie che hai installato in precedenza.
Quindi, configura il display:
Adafruit_SSD1306 display(4);
Quindi imposta tutte le variabili necessarie per eseguire il gioco:
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;
Questi memorizzano tutti i dati necessari per eseguire il gioco. Alcuni di questi memorizzano la posizione della palla, la dimensione dello schermo, la posizione del giocatore e così via. Nota come alcuni di questi sono costanti, cioè sono costanti e non cambieranno mai. Ciò consente al compilatore Arduino di velocizzare le cose.
La risoluzione dello schermo e la posizione della palla sono memorizzati in array . Gli array sono raccolte di oggetti simili e, per la palla, memorizzano le coordinate ( X e Y ). L'accesso agli elementi negli array è facile (non includere questo codice nel file):
resolution[1];
Poiché le matrici partono da zero, ciò restituirà il secondo elemento nell'array di risoluzione ( 64 ). Aggiornare gli elementi è ancora più semplice (di nuovo, non includere questo codice):
ball[1] = 15;
In void setup (), configura il display:
void setup() { display.begin(SSD1306_SWITCHCAPVCC, 0x3C); display.display(); }
La prima riga indica alla libreria di Adafruit quali dimensioni e protocollo di comunicazione vengono utilizzati dal display (in questo caso 128 x 64 e I2C ). La seconda riga ( display.display () ) dice allo schermo di mostrare tutto ciò che è memorizzato nel buffer (che non è nulla).
Crea due metodi chiamati drawBall e 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); }
Questi prendono le coordinate xey della palla e la disegnano sullo schermo usando il metodo drawCircle dalle librerie di visualizzazione. Questo utilizza la costante BALL_SIZE definita in precedenza. Prova a cambiare questo e guarda cosa succede. Questo metodo drawCircle accetta un colore pixel: NERO o BIANCO . Poiché questo è un display monocromatico (un colore), il bianco equivale a un pixel acceso e il nero spegne il pixel.
Ora crea un metodo chiamato moveAi :
void moveAi() { eraseAiPaddle(aiPos); if (ball[1]>aiPos) { ++aiPos; } else if (ball[1]< aiPos) { --aiPos; } drawAiPaddle(aiPos); }
Questo metodo gestisce lo spostamento dell'intelligenza artificiale o del giocatore di intelligenza artificiale . Questo è un semplice avversario di computer - Se la palla è sopra la paletta, vai su. È sotto la pagaia, verso il basso. Abbastanza semplice, ma funziona bene. I simboli di incremento e decremento vengono utilizzati ( ++ aiPos e -aiPos ) per aggiungere o sottrarre uno da aiPosition. Puoi aggiungere o sottrarre un numero maggiore per far muovere l'IA più velocemente, e quindi essere più difficile da battere. Ecco come lo faresti:
aiPos += 2;
E:
aiPos -= 2;
I segni Plus Equals e Meno Equals sono abbreviazioni per aggiungere o sottrarre due da / al valore corrente di aiPos. Ecco un altro modo per farlo:
aiPos = aiPos + 2;
e
aiPos = aiPos - 1;
Si noti come questo metodo prima cancella la paddle e poi la disegna di nuovo. Questo deve essere fatto in questo modo. Se la nuova posizione della paletta è stata disegnata, ci saranno due paddle sovrapposti sullo schermo.
Il metodo drawNet utilizza due cicli per disegnare la rete:
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); } }
Questo utilizza le variabili WALL_WIDTH per impostarne la dimensione.
Crea metodi chiamati drawPixels e cancellaPixels . Proprio come i metodi palla, l'unica differenza tra questi due è il colore dei pixel:
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); } } }
Anche in questo caso, entrambi questi metodi usano due cicli for per disegnare un gruppo di pixel. Piuttosto che dover disegnare ogni pixel usando il metodo drawPixel delle librerie, i loop disegnano un gruppo di pixel in base alle dimensioni specificate.
Il metodo drawScore utilizza le funzionalità di testo della libreria per scrivere il punteggio giocatore e AI sullo schermo. Questi sono memorizzati in playerScore e aiScore :
void drawScore() { display.setTextSize(2); display.setTextColor(WHITE); display.setCursor(45, 0); display.println(playerScore); display.setCursor(75, 0); display.println(aiScore); }
Questo metodo ha anche una controparte di eraseScore, che imposta i pixel su nero o spento.
Gli ultimi quattro metodi sono molto simili. Disegnano e cancellano il giocatore e i paddle:
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); }
Si noti come chiamano il metodo erasePixel creare in precedenza. Questi metodi disegnano e cancellano la paletta appropriata.
C'è un po 'più di logica nel ciclo principale. Ecco l'intero codice:
#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); }
Ecco cosa ti ritrovi:
Una volta che sei sicuro del codice, ci sono numerose modifiche che puoi apportare:
- Aggiungi un menu per i livelli di difficoltà (modifica AI e velocità della palla).
- Aggiungi un movimento casuale alla palla o AI.
- Aggiungi un altro piatto per due giocatori.
- Aggiungi un pulsante di pausa.
Ora date un'occhiata a questi progetti di gioco retrò Pi Zero 5 Progetti di gioco retrò con i progetti di gioco retro Raspberry Pi Zero 5 con il Raspberry Pi Zero Il Raspberry Pi Zero ha preso d'assalto il mondo fai-da-te e homebrew, rendendo possibile la revisione di vecchi progetti e ispirando i nuovi arrivati, specialmente nelle menti febbricitanti dei fan dei giochi retrò. Leggi di più .
Hai codificato Pong usando questo codice? Quali modifiche hai apportato? Fatemi sapere nei commenti qui sotto, mi piacerebbe sembrare alcune immagini!