Guide · 20 min di lettura

Giochi con Arduino: Pong, T-Rex, Snake e Simon passo passo

Come creare giochi con Arduino: Pong e T-Rex su OLED, Snake su matrice LED, Simon, un runner su LCD e un test di reazione, con game loop e record in EEPROM.

Creare giochi con Arduino è il modo più divertente per imparare elettronica e programmazione: con un display OLED, qualche pulsante e un joystick puoi costruire da solo classici come Pong, il T-Rex runner, Snake e il gioco di memoria Simon, e sfidare i tuoi amici a battere il tuo record. Mentre scrivi un gioco impari quasi senza accorgertene gli argomenti più importanti della programmazione: temporizzazione, macchine a stati, sistemi di coordinate, calcolo delle collisioni, numeri casuali e memoria permanente. Tutti questi giochi funzionano su un solo Arduino Uno; i suoi 2 KB di RAM bastano, se usi la memoria con giudizio.

In questa guida costruiamo sei giochi dall'inizio alla fine: Pong e il T-Rex runner su un display OLED, Snake su una matrice LED NeoPixel 8x8 e 16x16, Simon con quattro LED e quattro pulsanti, un gioco di corsa su un LCD 16x2 e un test di reazione preciso al millisecondo. Per ogni gioco trovi l'elenco dei componenti, i collegamenti e un codice che compila. Prima impari una volta lo scheletro che tutti hanno in comune, cioè il game loop, il rilevamento delle collisioni, il punteggio e il record salvato nella EEPROM, poi lo riutilizzi in ogni gioco. Alla fine trovi anche le versioni per ESP32 e Raspberry Pi Pico.

Gioco Snake con Arduino su una matrice LED NeoPixel 8x8
Matrice LED NeoPixel WS2812B 8x8: 64 pixel a colori e un solo filo dati, un mini display ideale per Snake.

Quali componenti servono per ogni gioco?

Quando crei un gioco con Arduino, la prima scelta è il display, perché è lui a decidere che tipo di gioco puoi fare. Con pochi LED puoi già realizzare Simon o un test di reazione, mentre un OLED da 128×64 pixel rende possibili Pong e T-Rex, con grafica in movimento e fisica. La tabella riassume i giochi di questa guida dal più facile al più difficile:

GiocoDisplayIngressoAltri componentiDifficoltà
Test di reazione1 LED + monitor seriale1 pulsanteResistenza da 220 Ω, buzzer opzionaleFacile
Gioco di memoria Simon4 LED colorati4 pulsanti4 resistenze da 220 Ω, buzzer passivoFacile–media
Runner su LCDLCD 16x2 I2C1 pulsanteBuzzer passivoMedia
PongOLED SSD1306 da 0,96"Joystick o potenziometro da 10 kΩBuzzer passivoMedia
T-Rex runnerOLED SSD1306 da 0,96"1 pulsante o il pulsante del joystickBuzzer passivoMedia
Snake (8x8)Matrice NeoPixel 8x8JoystickResistenza da 330 Ω, condensatore da 1000 µFMedia–avanzata
Snake (16x16)Matrice NeoPixel 16x16JoystickAlimentatore esterno 5 V da 3–4 AAvanzata
Arduino Uno
Arduino Uno
OLED SSD1306 0.96"
OLED SSD1306 0.96"
Joystick
Joystick
LCD 16x2 I2C
LCD 16x2 I2C
Buzzer
Buzzer
Push button
Push button

Collegamento comune: un solo circuito come console di gioco

Abbiamo scelto pin comuni in modo che tutti i giochi tranne Simon funzionino sullo stesso circuito. L'OLED e l'LCD I2C condividono gli stessi due fili (SDA e SCL); il joystick va agli ingressi analogici, il pulsante di azione a D3 e il buzzer a D8. Una volta montata questa "console", cambi gioco semplicemente caricando un altro sketch. Puoi vedere la piedinatura completa della tua scheda nello strumento Pinout.

Componente / pinArduino UnoESP32Raspberry Pi Pico
OLED o LCD I2C: SDAA4GPIO 21GP4
OLED o LCD I2C: SCLA5GPIO 22GP5
Joystick VRx (asse X)A0GPIO 34GP26
Joystick VRy (asse Y)A1GPIO 35GP27
Joystick SW (pulsante)D2GPIO 25GP15
Pulsante di azione (altro piedino a GND)D3GPIO 26GP14
Buzzer passivo (+)D8GPIO 27GP16
NeoPixel DIN (tramite 330 Ω)D6GPIO 13GP22
Su ESP32 e Pico alimenta il joystick e l'OLED a 3,3 V; un joystick alimentato a 5 V porta sui pin analogici più di 3,3 V e può danneggiare la scheda. Se colleghi un modulo LCD I2C a 5 V a un ESP32, metti un convertitore di livello logico tra i due. Tutti i pulsanti usano la resistenza di pull-up interna: un piedino va al pin, l'altro a GND, e premendo il pin legge LOW.

Come funziona il game loop?

Ogni videogioco si basa su un ciclo di quattro passi che si ripete decine di volte al secondo: leggere l'ingresso (pulsanti, joystick), aggiornare il mondo (posizione, velocità, punteggio), controllare le collisioni e disegnare lo schermo. Un giro del ciclo si chiama fotogramma (frame). Trenta fotogrammi al secondo (30 FPS) bastano per un'immagine fluida, quindi ogni fotogramma dura circa 33 ms. La funzione loop() di Arduino è già un ciclo, ma gira molto velocemente e in modo irregolare; per questo bisogna dividerla in fotogrammi fissi con millis().

Qui è fondamentale non usare delay(). Se scrivi delay(33), Arduino non fa nulla per quei 33 ms, può perdere una pressione breve e la velocità del gioco cambia ogni volta che il disegno richiede più o meno tempo. Con millis(), invece, loop() legge l'ingresso a ogni giro ma fa avanzare il gioco solo quando è il momento di un nuovo fotogramma. La differenza tra i due metodi è spiegata in dettaglio in Arduino millis o delay. Lo scheletro qui sotto è la base di tutti i giochi della guida:

C++ (Arduino IDE)
1// Oyun döngüsü iskeleti / Game loop skeleton
2enum State { MENU, PLAYING, GAME_OVER };   // oyunun durumları / game states
3State state = MENU;
4
5const byte BUTTON_PIN = 3;                 // aksiyon butonu / action button
6const unsigned long FRAME_MS = 33;         // ~30 kare/sn / ~30 frames per second
7unsigned long lastFrame = 0;
8unsigned int score = 0;
9
10// Sıçramasız basış: yalnız basıldığı an true / bounce-free: true only at the moment of a press
11bool buttonPressed() {
12  static bool stable = HIGH, lastRead = HIGH;
13  static unsigned long changedAt = 0;
14  bool r = digitalRead(BUTTON_PIN);
15  if (r != lastRead) { lastRead = r; changedAt = millis(); }
16  if (millis() - changedAt > 15 && r != stable) {   // 15 ms sabit kaldı mı? / stable for 15 ms?
17    stable = r;
18    return stable == LOW;
19  }
20  return false;
21}
22
23void setup() {
24  pinMode(BUTTON_PIN, INPUT_PULLUP);       // basınca LOW / LOW when pressed
25  Serial.begin(9600);
26}
27
28void loop() {
29  bool pressed = buttonPressed();          // 1) giriş: her turda oku / input: read on every pass
30  if (pressed && state != PLAYING) {       // menüden ya da oyun sonundan başlat / start from menu or game over
31    score = 0;
32    state = PLAYING;
33  }
34  if (millis() - lastFrame < FRAME_MS) return;   // kare zamanı gelmedi / not time for a frame yet
35  lastFrame = millis();
36
37  if (state == PLAYING) {
38    score++;                               // 2) güncelle: konum, hız, puan / update: position, speed, score
39    if (score >= 300) state = GAME_OVER;   // 3) çarpışma ya da bitiş kontrolü / collision or end check
40    Serial.println(score);                 // 4) çiz: burada ekrana çizilir / draw: the screen is drawn here
41  }
42}

La seconda idea chiave del codice è la macchina a stati. In ogni momento il gioco si trova in uno di tre stati: MENU, PLAYING o GAME_OVER. Ogni stato reagisce in modo diverso allo stesso pulsante: nel menu una pressione avvia la partita, durante il gioco fa saltare il personaggio, dopo il game over apre un nuovo turno. Dividere il codice così elimina gli if annidati e rende facile aggiungere un nuovo stato, come PAUSE. Approfondiamo l'argomento in macchina a stati con Arduino.

Leggere pulsanti e joystick

Collegare i pulsanti con INPUT_PULLUP è il metodo più pulito: non serve una resistenza esterna, il pin legge HIGH a riposo e LOW quando premi. Un pulsante meccanico però rimbalza per qualche millisecondo quando viene premuto o rilasciato, e Arduino lo vede come più pressioni di fila. In un gioco significa saltare due volte con una sola pressione o un colore sbagliato contato in Simon. La funzione buttonPressed() dello scheletro accetta un nuovo stato solo quando il pin è rimasto stabile per 15 ms e restituisce true una sola volta, nel momento della pressione. Per lo schema e altri metodi, guarda come collegare un pulsante ad Arduino.

Un joystick è formato da due potenziometri e un pulsante. Su Uno ogni asse restituisce valori da 0 a 1023 e, con la leva libera, il centro è intorno a 512, ma questo valore varia da modulo a modulo tra circa 490 e 530. Per questo i giochi a griglia usano soglie invece di valori esatti: sotto 300 significa sinistra o su, sopra 700 destra o giù. L'ampia zona intermedia (zona morta) impedisce al personaggio di spostarsi da solo quando lasci la leva. In Pong, invece, colleghiamo il valore analogico direttamente alla racchetta: map() trasforma l'intervallo 0–1023 nella corsa di 0–48 pixel della racchetta. Calibrazione e tabella delle direzioni sono nella guida al modulo joystick.

Come si rilevano le collisioni?

Il rilevamento delle collisioni verifica se due oggetti si trovano nello stesso punto nello stesso momento. Nei giochi con schermo il metodo più diffuso è il test AABB (bounding box allineata agli assi): consideri ogni oggetto come un rettangolo. Due rettangoli si sovrappongono solo se quattro condizioni sono vere insieme: il bordo sinistro di A è a sinistra del bordo destro di B, il bordo destro di A è a destra del bordo sinistro di B, e le stesse due condizioni valgono sull'asse verticale. Nel codice è una sola riga: ax < bx + bw && ax + aw > bx && ay < by + bh && ay + ah > by. Nei giochi a griglia (Snake, il runner su LCD) è ancora più semplice: se due celle hanno le stesse coordinate, c'è una collisione.

GiocoCosa urta con cosa?Metodo
PongPallina con pareti e racchetteAABB + inversione della velocità
T-RexDinosauro con i cactusAABB (con una box un po' più piccola)
SnakeTesta con pareti, corpo e ciboUguaglianza delle celle della griglia
Runner su LCDCorridore con un cactusStessa colonna e stessa riga
SimonColore premuto con il colore della sequenzaConfronto tra elementi dell'array
Test di reazioneIstante della pressione con quello del segnaleConfronto dei tempi (falsa partenza)
Gli oggetti piccoli e veloci possono attraversare un ostacolo in un solo fotogramma e saltare la collisione; si chiama tunneling. In Pong, se la pallina avanza di più di 4 pixel per fotogramma, supera la zona di controllo della racchetta da 3 pixel senza mai toccarla. La soluzione è mantenere la velocità per fotogramma inferiore alla larghezza di quella zona, ed è proprio ciò che fa il limite MAX_SPEED = 2.8 nel codice di Pong.

Punteggio e record salvato nella EEPROM

Il punteggio sta in una variabile in RAM e sparisce appena stacchi Arduino dalla corrente. Per rendere permanente il record si usa la EEPROM: l'ATmega328P di Arduino Uno ha 1024 byte di EEPROM che conservano il contenuto senza alimentazione. Per un record bastano due byte di tipo uint16_t (0–65535). L'unico punto debole della EEPROM è la durata: ogni cella sopporta circa 100.000 scritture. Sembrano tante, ma se scrivi il punteggio a ogni fotogramma, cioè 30 volte al secondo, la cella si consuma in circa 55 minuti. Il metodo giusto è scrivere solo a fine partita e solo se il record è stato battuto; anche con 10 nuovi record al giorno la cella dura 27 anni.

C++ (Arduino IDE)
1#include <EEPROM.h>
2
3const int HISCORE_ADDR = 0;          // rekor EEPROM'un 0-1. baytında / high score in EEPROM bytes 0-1
4uint16_t hiScore = 0;
5
6uint16_t loadHiScore() {
7  uint16_t v;
8  EEPROM.get(HISCORE_ADDR, v);       // 2 bayt oku / read 2 bytes
9  if (v == 0xFFFF) v = 0;            // hiç yazılmamış EEPROM 0xFF okunur / blank EEPROM reads 0xFF
10  return v;
11}
12
13void saveIfRecord(uint16_t score) {
14  if (score > hiScore) {             // yalnız rekor kırılınca yaz / write only on a new record
15    hiScore = score;
16    EEPROM.put(HISCORE_ADDR, hiScore);   // put() yalnız değişen baytları yazar / put() writes only changed bytes
17  }
18}
19
20void setup() {
21  Serial.begin(9600);
22  hiScore = loadHiScore();
23  Serial.print(F("Rekor / High score: "));
24  Serial.println(hiScore);
25  saveIfRecord(120);                 // örnek: oyun 120 puanla bitti / example: a game ended with 120 points
26}
27
28void loop() {}

Il codice ha due finezze. Primo, ogni byte di una EEPROM mai scritta vale 0xFF; per questo al primissimo avvio il record appare come 65535, e il controllo su 0xFFFF lo trasforma in 0. Nel test di reazione è il contrario: 0xFFFF significa "ancora nessun record", perché lì un tempo più breve è migliore. Secondo, EEPROM.put() legge ogni byte prima di scrivere e scrive solo quelli cambiati, allungando ancora di più la durata. Per gli altri comandi, consulta come usare la EEPROM di Arduino. Per permetterti di caricare tutti i giochi di questa guida uno dopo l'altro sullo stesso Uno, ogni gioco ha il suo indirizzo:

GiocoIndirizzo EEPROMValore salvato
T-Rex runner0–1Punteggio più alto
Snake2–3Maggior numero di cibi mangiati
Runner su LCD4–5Punteggio più alto
Test di reazione6–7Tempo più breve (ms)
Simon8–9Livello più alto raggiunto

Pong: display OLED e joystick

Pong è il gioco di ping pong del 1972 che ha dato inizio all'era dei videogiochi. Nella nostra versione controlli la racchetta sinistra con l'asse Y del joystick (o con un potenziometro su A1), mentre Arduino gioca con quella destra. Componenti: un Arduino Uno, un OLED SSD1306 I2C da 0,96 pollici, un joystick e un buzzer passivo. Quando installi la libreria Adafruit SSD1306 dal gestore librerie, installa anche la sua dipendenza Adafruit GFX. Il collegamento del display e i comandi di disegno sono spiegati in dettaglio in display OLED SSD1306.

C++ (Arduino IDE)
1#include <Wire.h>
2#include <Adafruit_GFX.h>
3#include <Adafruit_SSD1306.h>
4
5Adafruit_SSD1306 display(128, 64, &Wire, -1);
6
7const byte JOY_Y_PIN  = A1;          // joystick VRy ya da potansiyometre / joystick VRy or potentiometer
8const byte BUZZER_PIN = 8;           // pasif buzzer / passive buzzer
9const byte PADDLE_H = 16, PADDLE_W = 3;
10const byte WIN_SCORE = 5;            // 5 sayı alan kazanır / first to 5 wins
11const float MAX_SPEED = 2.8;         // raketten kalın olmasın / keep below paddle thickness + ball size
12const unsigned long FRAME_MS = 30;   // ~33 kare/sn / ~33 frames per second
13
14float ballX, ballY, velX, velY;
15float playerY = 24, cpuY = 24;
16byte playerScore = 0, cpuScore = 0;
17unsigned long lastFrame = 0;
18
19void serve(int dir) {                // topu ortadan at / serve from the center
20  ballX = 63; ballY = 31;
21  velX = 1.8 * dir;
22  velY = random(-10, 11) / 10.0;     // -1,0 ile 1,0 arası / between -1.0 and 1.0
23}
24
25// Topun rakete çarptığı nokta yeni açıyı belirler / where the ball hits the paddle sets the new angle
26void hitPaddle(float paddleY) {
27  float offset = (ballY + 1 - (paddleY + PADDLE_H / 2.0)) / (PADDLE_H / 2.0);  // -1 üst, +1 alt / -1 top, +1 bottom
28  velX = constrain(-velX * 1.05, -MAX_SPEED, MAX_SPEED);   // yön değiştir, %5 hızlan / reverse, 5% faster
29  velY = offset * 1.5;
30  tone(BUZZER_PIN, 880, 15);
31}
32
33void setup() {
34  Serial.begin(9600);
35  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
36    Serial.println(F("SSD1306 allocation failed"));
37    for (;;);                        // ekran yoksa dur / halt without a display
38  }
39  display.setTextColor(SSD1306_WHITE);
40  randomSeed(analogRead(A3));        // boştaki analog pin / a floating analog pin
41  serve(1);
42}
43
44void loop() {
45  if (millis() - lastFrame < FRAME_MS) return;
46  lastFrame = millis();
47
48  // 1) Giriş: oyuncu raketi / Input: player paddle
49  playerY = map(analogRead(JOY_Y_PIN), 0, 1023, 0, 64 - PADDLE_H);
50
51  // 2) Basit yapay zekâ: CPU topu sınırlı hızla izler / Simple AI: the CPU follows the ball at limited speed
52  float target = ballY - PADDLE_H / 2.0;
53  cpuY += constrain(target - cpuY, -1.2, 1.2);
54  cpuY = constrain(cpuY, 0, 64 - PADDLE_H);
55
56  // 3) Topu hareket ettir / Move the ball
57  ballX += velX;
58  ballY += velY;
59
60  // 4) Çarpışmalar / Collisions
61  if (ballY <= 0 || ballY >= 62) {   // üst ve alt duvar / top and bottom walls
62    velY = -velY;
63    ballY = constrain(ballY, 0, 62);
64  }
65  if (velX < 0 && ballX <= 4 && ballX > 0 && ballY + 2 >= playerY && ballY <= playerY + PADDLE_H) {
66    ballX = 4;
67    hitPaddle(playerY);
68  }
69  if (velX > 0 && ballX >= 122 && ballX < 126 && ballY + 2 >= cpuY && ballY <= cpuY + PADDLE_H) {
70    ballX = 122;
71    hitPaddle(cpuY);
72  }
73
74  // 5) Sayı / Point
75  if (ballX <= 0)   { cpuScore++;    tone(BUZZER_PIN, 220, 200); serve(1); }
76  if (ballX >= 126) { playerScore++; tone(BUZZER_PIN, 660, 200); serve(-1); }
77
78  // 6) Çiz / Draw
79  display.clearDisplay();
80  for (byte y = 0; y < 64; y += 6) display.drawFastVLine(63, y, 3, SSD1306_WHITE);  // orta çizgi / center line
81  display.fillRect(1, (int)playerY, PADDLE_W, PADDLE_H, SSD1306_WHITE);
82  display.fillRect(124, (int)cpuY, PADDLE_W, PADDLE_H, SSD1306_WHITE);
83  display.fillRect((int)ballX, (int)ballY, 2, 2, SSD1306_WHITE);
84  display.setTextSize(1);
85  display.setCursor(52, 0); display.print(playerScore);
86  display.setCursor(70, 0); display.print(cpuScore);
87  display.display();
88
89  // 7) Oyun bitti mi? / Game over?
90  if (playerScore >= WIN_SCORE || cpuScore >= WIN_SCORE) {
91    display.setTextSize(2);
92    if (playerScore >= WIN_SCORE) { display.setCursor(22, 24); display.print(F("YOU WIN")); }
93    else                          { display.setCursor(10, 24); display.print(F("GAME OVER")); }
94    display.display();
95    delay(3000);
96    playerScore = cpuScore = 0;
97    serve(1);
98  }
99}

Ciò che rende divertente Pong è la funzione hitPaddle(). Se la pallina colpisce il centro della racchetta torna quasi dritta; se colpisce il bordo rimbalza con un angolo stretto e mette in difficoltà l'avversario. Per farlo riportiamo la distanza della pallina dal centro della racchetta a un valore tra -1 e +1 e la trasformiamo in velocità verticale. A ogni colpo la pallina accelera del 5 % fino al limite di MAX_SPEED. La racchetta di Arduino si muove al massimo di 1,2 pixel per fotogramma; se dai alla pallina un angolo stretto non riesce a raggiungerla e fai punto. Per rendere il gioco più difficile porta il valore a 1,5; per giocare in due, cancella le righe della CPU e controlla la seconda racchetta con un potenziometro su A2.

Perché 30 ms per fotogramma? Uno invia il buffer di immagine da 1024 byte all'OLED tramite I2C a 400 kHz in circa 25 ms, quindi il display non può mostrare più di 35–40 fotogrammi al secondo. Tenere la durata del fotogramma appena sopra questo tempo di invio rende costante la velocità del gioco. E non farti ingannare dal messaggio del compilatore "Global variables use 28%": il buffer dell'SSD1306 viene allocato durante l'esecuzione e l'uso reale della RAM sale a circa il 78 %. Se la memoria non basta compare l'errore SSD1306 allocation failed; abbiamo raccolto le soluzioni in Arduino memoria insufficiente.

T-Rex runner: fisica del salto e record

Portiamo sul display OLED il gioco del dinosauro che Chrome mostra quando manca internet. Il dinosauro corre sul posto, i cactus scorrono da destra a sinistra e tu li salti premendo il pulsante. Per rendere il salto realistico usiamo una fisica semplice: alla pressione la velocità verticale diventa -4,2 pixel per fotogramma, a ogni fotogramma la gravità le aggiunge 0,3 e la posizione cambia in base alla velocità. Così il dinosauro sale rallentando, resta un attimo in cima e ricade sempre più veloce; sale di circa 27 pixel e resta in aria per 27 fotogrammi, cioè circa 0,8 secondi. I componenti sono gli stessi di Pong; per saltare usi il pulsante su D3 o quello del joystick.

C++ (Arduino IDE)
1#include <Wire.h>
2#include <EEPROM.h>
3#include <Adafruit_GFX.h>
4#include <Adafruit_SSD1306.h>
5
6Adafruit_SSD1306 display(128, 64, &Wire, -1);
7
8const byte JUMP_PIN     = 3;         // aksiyon butonu / action button
9const byte JOY_SW_PIN   = 2;         // joystick butonu da zıplatır / the joystick button jumps too
10const byte BUZZER_PIN   = 8;
11const int  HISCORE_ADDR = 0;         // EEPROM 0-1. bayt / EEPROM bytes 0-1
12const byte GROUND_Y = 56;            // zemin çizgisi / ground line
13const byte DINO_X = 10, DINO_W = 14, DINO_H = 14;
14const float JUMP_V  = -4.2;          // zıplama hızı (piksel/kare) / jump speed (px/frame)
15const float GRAVITY = 0.3;           // yer çekimi (piksel/kare²) / gravity (px/frame²)
16const unsigned long FRAME_MS = 30;   // ~33 kare/sn / ~33 frames per second
17
18enum State { MENU, PLAYING, GAME_OVER };
19State state = MENU;
20
21struct Cactus { float x; byte w, h; };
22Cactus cacti[2];
23
24float dinoY, velY, speed;
25uint16_t score = 0, hiScore = 0;
26unsigned long lastFrame = 0;
27
28bool jumpHeld() {
29  return digitalRead(JUMP_PIN) == LOW || digitalRead(JOY_SW_PIN) == LOW;
30}
31
32uint16_t loadHiScore() {
33  uint16_t v;
34  EEPROM.get(HISCORE_ADDR, v);
35  return (v == 0xFFFF) ? 0 : v;      // boş EEPROM 0xFFFF okunur / blank EEPROM reads 0xFFFF
36}
37
38void placeCactus(byte i, float startX) {
39  cacti[i].x = startX;
40  cacti[i].w = random(4, 9);         // 4-8 piksel genişlik / 4-8 px wide
41  cacti[i].h = random(8, 17);        // 8-16 piksel yükseklik / 8-16 px tall
42}
43
44void resetGame() {
45  dinoY = GROUND_Y - DINO_H;
46  velY = 0;
47  speed = 2.0;
48  score = 0;
49  placeCactus(0, 140);
50  placeCactus(1, 140 + random(70, 140));
51}
52
53// İki dikdörtgen üst üste biniyor mu? (AABB) / Do two rectangles overlap? (AABB)
54bool overlap(int ax, int ay, int aw, int ah, int bx, int by, int bw, int bh) {
55  return ax < bx + bw && ax + aw > bx && ay < by + bh && ay + ah > by;
56}
57
58void drawDino(int x, int y, bool legA) {
59  display.fillRect(x + 6, y, 8, 5, SSD1306_WHITE);                  // kafa / head
60  display.drawPixel(x + 9, y + 1, SSD1306_BLACK);                   // göz / eye
61  display.fillRect(x, y + 5, 10, 6, SSD1306_WHITE);                 // gövde / body
62  display.fillRect(x + 2, y + 11, 2, legA ? 3 : 1, SSD1306_WHITE);  // sol bacak / left leg
63  display.fillRect(x + 6, y + 11, 2, legA ? 1 : 3, SSD1306_WHITE);  // sağ bacak / right leg
64}
65
66void drawScene() {
67  display.clearDisplay();
68  display.drawFastHLine(0, GROUND_Y, 128, SSD1306_WHITE);
69  bool onGround = dinoY >= GROUND_Y - DINO_H;
70  drawDino(DINO_X, (int)dinoY, onGround ? (score / 4) % 2 : true);  // koşma animasyonu / running animation
71  for (byte i = 0; i < 2; i++) {
72    display.fillRect((int)cacti[i].x, GROUND_Y - cacti[i].h, cacti[i].w, cacti[i].h, SSD1306_WHITE);
73  }
74  display.setTextSize(1);
75  display.setCursor(0, 0);  display.print(score);
76  display.setCursor(80, 0); display.print(F("HI ")); display.print(hiScore);
77}
78
79void showMessage(const __FlashStringHelper *msg) {
80  display.fillRect(14, 18, 100, 28, SSD1306_BLACK);
81  display.drawRect(14, 18, 100, 28, SSD1306_WHITE);
82  display.setCursor(64 - strlen_P((const char *)msg) * 3, 28);   // ortala (karakter 6 piksel) / center (6 px per char)
83  display.print(msg);
84  display.display();
85}
86
87void gameOver() {
88  tone(BUZZER_PIN, 150, 300);
89  if (score > hiScore) {             // yeni rekor: yalnız şimdi yaz / new record: write only now
90    hiScore = score;
91    EEPROM.put(HISCORE_ADDR, hiScore);
92  }
93  drawScene();
94  showMessage(F("GAME OVER"));
95  delay(800);                        // basılı buton hemen yeniden başlatmasın / a held button must not restart at once
96  state = GAME_OVER;
97}
98
99void setup() {
100  pinMode(JUMP_PIN, INPUT_PULLUP);
101  pinMode(JOY_SW_PIN, INPUT_PULLUP);
102  Serial.begin(9600);
103  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
104    Serial.println(F("SSD1306 allocation failed"));
105    for (;;);
106  }
107  display.setTextColor(SSD1306_WHITE);
108  randomSeed(analogRead(A3));
109  hiScore = loadHiScore();
110  if (digitalRead(JUMP_PIN) == LOW) {    // açılışta basılıysa rekoru sıfırla / held at power-up: reset the record
111    hiScore = 0;
112    EEPROM.put(HISCORE_ADDR, hiScore);
113  }
114  resetGame();
115  drawScene();
116  showMessage(F("PRESS START"));
117}
118
119void loop() {
120  if (millis() - lastFrame < FRAME_MS) return;
121  lastFrame = millis();
122
123  if (state != PLAYING) {            // menü ya da oyun sonu: basınca başla / menu or game over: press to start
124    if (jumpHeld()) {
125      while (jumpHeld());            // bırakılmasını bekle / wait for release
126      resetGame();
127      state = PLAYING;
128    }
129    return;
130  }
131
132  // 1) Giriş ve fizik / Input and physics
133  bool onGround = dinoY >= GROUND_Y - DINO_H;
134  if (onGround && jumpHeld()) {
135    velY = JUMP_V;
136    tone(BUZZER_PIN, 1200, 20);
137  }
138  velY += GRAVITY;                   // her karede hız artar / velocity grows every frame
139  dinoY += velY;
140  if (dinoY > GROUND_Y - DINO_H) { dinoY = GROUND_Y - DINO_H; velY = 0; }   // yere indi / landed
141
142  // 2) Engeller ve çarpışma / Obstacles and collision
143  for (byte i = 0; i < 2; i++) {
144    cacti[i].x -= speed;
145    if (cacti[i].x < -(float)cacti[i].w) {            // ekrandan çıktı / left the screen
146      float otherX = cacti[1 - i].x;
147      placeCactus(i, max(128.0, otherX + random(70, 140)));
148    }
149    // biraz küçük çarpışma kutusu daha adil hissettirir / a slightly smaller hitbox feels fairer
150    if (overlap(DINO_X + 2, (int)dinoY + 1, DINO_W - 4, DINO_H - 2,
151                (int)cacti[i].x, GROUND_Y - cacti[i].h, cacti[i].w, cacti[i].h)) {
152      gameOver();
153      return;
154    }
155  }
156
157  // 3) Puan ve zorluk / Score and difficulty
158  score++;
159  if (score % 200 == 0 && speed < 5.0) speed += 0.25;   // yaklaşık 6 sn'de bir hızlan / speed up every ~6 s
160
161  // 4) Çiz / Draw
162  drawScene();
163  display.display();
164}
ImpostazioneValoreEffetto
JUMP_V-4.2Forza del salto; con -5 il dinosauro sale di circa 40 pixel, quasi fino al bordo superiore
GRAVITY0.3Un valore più alto rende il salto più corto e secco
speed2,0 → 5,0Velocità dei cactus; aumenta di 0,25 ogni 200 fotogrammi
random(70, 140)pixelDistanza tra due cactus; valori più piccoli rendono il gioco più difficile
Box di collisione10×12 pixelPiù piccola del dinosauro disegnato da 14×14, così i passaggi per un soffio non contano come urto

Il record viene scritto nella EEPROM nel momento in cui la partita finisce, e solo se è stato battuto; appare nell'angolo in alto a destra come HI. Se tieni premuto il pulsante su D3 mentre accendi la scheda, il record si azzera, utile quando inizi un nuovo torneo con gli amici. Altezza e larghezza dei cactus vengono da random(). Perché ogni partita inizi in modo diverso, la riga randomSeed(analogRead(A3)) usa come seme il rumore di un pin analogico non collegato.

Snake su matrice LED (8x8 e 16x16)

Il gioco del serpente reso famoso dai vecchi telefoni Nokia si gioca al meglio su una matrice LED, perché ogni LED è una cella del gioco. Usiamo una matrice a colori WS2812B NeoPixel: la versione 8x8 ha 64 pixel e quella 16x16 ne ha 256, tutti pilotati con un solo filo dati. Il corpo del serpente è memorizzato in due array (snakeX e snakeY) e l'elemento 0 è la testa. A ogni passo il corpo avanza di una cella e la nuova posizione viene scritta nella testa; quando mangia, la coda non viene cancellata e il serpente si allunga di una cella. Per compilare installa la libreria Adafruit NeoPixel; i dettagli del collegamento sono nella guida WS2812B NeoPixel.

C++ (Arduino IDE)
1#include <Adafruit_NeoPixel.h>
2#include <EEPROM.h>
3
4const byte W = 8, H = 8;             // 16x16 matris için 16 yap / set to 16 for a 16x16 matrix
5const bool SERPENTINE = false;       // zikzak dizilimli panelde true / true for zigzag-wired panels
6const byte DATA_PIN = 6;             // DIN, 330 Ω üzerinden / DIN through 330 Ω
7const byte JOY_X = A0, JOY_Y = A1, JOY_SW = 2;
8const byte BUZZER_PIN = 8;
9const int  HISCORE_ADDR = 2;         // EEPROM 2-3. bayt / EEPROM bytes 2-3
10
11Adafruit_NeoPixel matrix(W * H, DATA_PIN, NEO_GRB + NEO_KHZ800);
12
13byte snakeX[W * H], snakeY[W * H];   // [0] baş, sonrası gövde / [0] head, then the body
14int snakeLen;
15int8_t dirX, dirY, nextDX, nextDY;
16byte foodX, foodY;
17unsigned int stepMs;
18unsigned long lastStep = 0;
19uint16_t hiScore;
20bool alive = false;
21
22// (x, y) → LED sırası / (x, y) → LED index
23uint16_t xyToIndex(byte x, byte y) {
24  if (SERPENTINE && (y & 1)) return y * W + (W - 1 - x);   // tek satırlar ters yönde / odd rows run backwards
25  return y * W + x;
26}
27
28bool onSnake(byte x, byte y) {
29  for (int i = 0; i < snakeLen; i++) {
30    if (snakeX[i] == x && snakeY[i] == y) return true;
31  }
32  return false;
33}
34
35void placeFood() {
36  do {                               // yem yılanın üstüne düşmesin / food must not land on the snake
37    foodX = random(W);
38    foodY = random(H);
39  } while (onSnake(foodX, foodY));
40}
41
42void resetGame() {
43  snakeLen = 3;
44  for (int i = 0; i < snakeLen; i++) { snakeX[i] = 3 - i; snakeY[i] = H / 2; }
45  dirX = nextDX = 1;                 // sağa doğru başla / start moving right
46  dirY = nextDY = 0;
47  stepMs = 300;
48  placeFood();
49  alive = true;
50}
51
52void readJoystick() {
53  int x = analogRead(JOY_X), y = analogRead(JOY_Y);
54  // Geri dönüş yasak: kontrol şimdiki yöne göre / no reversing: check against the current direction
55  if      (x < 300 && dirX != 1)  { nextDX = -1; nextDY = 0; }
56  else if (x > 700 && dirX != -1) { nextDX = 1;  nextDY = 0; }
57  else if (y < 300 && dirY != 1)  { nextDX = 0;  nextDY = -1; }
58  else if (y > 700 && dirY != -1) { nextDX = 0;  nextDY = 1; }
59}
60
61void draw() {
62  matrix.clear();
63  matrix.setPixelColor(xyToIndex(foodX, foodY), matrix.Color(255, 0, 0));      // yem kırmızı / food red
64  for (int i = snakeLen - 1; i >= 0; i--) {
65    uint32_t c = (i == 0) ? matrix.Color(255, 200, 0) : matrix.Color(0, 160, 0); // baş sarı / head yellow
66    matrix.setPixelColor(xyToIndex(snakeX[i], snakeY[i]), c);
67  }
68  matrix.show();
69}
70
71void gameOver() {
72  alive = false;
73  uint16_t score = snakeLen - 3;     // yenen yem sayısı / food eaten
74  tone(BUZZER_PIN, 150, 400);
75  if (score > hiScore) { hiScore = score; EEPROM.put(HISCORE_ADDR, hiScore); }
76  for (byte k = 0; k < 3; k++) {     // kırmızı yanıp sön / flash red
77    matrix.fill(matrix.Color(80, 0, 0)); matrix.show(); delay(200);
78    matrix.clear();                      matrix.show(); delay(200);
79  }
80  for (uint16_t i = 0; i < score && i < W * H; i++) matrix.setPixelColor(i, matrix.Color(0, 0, 120));  // puan kadar LED / one LED per point
81  matrix.show();
82  Serial.print(F("Skor / Score: "));  Serial.print(score);
83  Serial.print(F("  Rekor / Best: ")); Serial.println(hiScore);
84}
85
86void step() {
87  dirX = nextDX; dirY = nextDY;
88  int nx = snakeX[0] + dirX, ny = snakeY[0] + dirY;
89  if (nx < 0 || nx >= W || ny < 0 || ny >= H) { gameOver(); return; }   // duvar / wall
90
91  bool eat = (nx == foodX && ny == foodY);
92  // Yem yoksa kuyruk ucu boşalacağı için ona çarpmak sayılmaz / without food the tail tip moves away, so it is not a hit
93  int checkLen = eat ? snakeLen : snakeLen - 1;
94  for (int i = 0; i < checkLen; i++) {
95    if (snakeX[i] == nx && snakeY[i] == ny) { gameOver(); return; }    // kendine çarptı / hit itself
96  }
97
98  if (eat) snakeLen++;
99  for (int i = snakeLen - 1; i > 0; i--) {      // gövdeyi bir hücre kaydır / shift the body one cell
100    snakeX[i] = snakeX[i - 1];
101    snakeY[i] = snakeY[i - 1];
102  }
103  snakeX[0] = nx; snakeY[0] = ny;
104
105  if (eat) {
106    tone(BUZZER_PIN, 1000, 40);
107    if (stepMs > 120) stepMs -= 10;             // her yemde hızlan / faster after every food
108    if (snakeLen == W * H) { gameOver(); return; }   // tahta doldu: kazandın / board full: you win
109    placeFood();
110  }
111}
112
113void setup() {
114  pinMode(JOY_SW, INPUT_PULLUP);
115  Serial.begin(9600);
116  matrix.begin();
117  matrix.setBrightness(30);          // düşük parlaklık = düşük akım / low brightness = low current
118  randomSeed(analogRead(A3));
119  EEPROM.get(HISCORE_ADDR, hiScore);
120  if (hiScore == 0xFFFF) hiScore = 0;
121  resetGame();
122  draw();
123}
124
125void loop() {
126  if (!alive) {                      // oyun bitti: joystick butonuyla yeniden / game over: restart with the joystick button
127    if (digitalRead(JOY_SW) == LOW) { resetGame(); draw(); delay(300); }
128    return;
129  }
130  readJoystick();                    // her turda oku, hiçbir hareketi kaçırma / read on every pass, never miss a move
131  if (millis() - lastStep >= stepMs) {
132    lastStep = millis();
133    step();
134    if (alive) draw();
135  }
136}

Nel codice ci sono tre regole fondamentali. Niente inversione: se un serpente che va a destra gira di colpo a sinistra, urta il proprio collo; readJoystick() confronta la nuova direzione con quella attuale del serpente e non con quella in attesa, così nemmeno due movimenti rapidi possono farlo tornare indietro. Eccezione della coda: in un passo senza cibo la punta della coda si libera, quindi entrarci con la testa non è una collisione. Posizionare il cibo: placeFood() riprova finché il cibo non cade sul serpente. Ogni cibo accorcia il passo di 10 ms (da 300 a 120 ms); a fine partita si accende in blu un LED per ogni cibo mangiato e il record viene salvato nella EEPROM.

Conta anche come sono collegati i LED della tua matrice. Nella maggior parte delle schede rigide 8x8 i LED procedono riga per riga nella stessa direzione (SERPENTINE = false). I pannelli flessibili 16x16 invece sono di solito collegati a zigzag: una riga va da sinistra a destra, la successiva da destra a sinistra. Se il serpente salta in diagonale o va al contrario una riga sì e una no, imposta SERPENTINE a true; se le direzioni risultano invertite, ruota il joystick o scambia le soglie 300 e 700. Per il 16x16 basta impostare W e H a 16; gli array del serpente occupano allora 512 byte, il buffer NeoPixel 768 byte e si riempie circa il 75 % della RAM di Uno.

Matrice LED NeoPixel 16x16
Matrice NeoPixel 16x16: un campo di gioco da 256 pixel significa un serpente più lungo e un gioco più difficile; richiede un'alimentazione esterna a 5 V.
Un singolo WS2812B assorbe circa 60 mA a bianco pieno; i 256 LED di una matrice 16x16 possono arrivare a 15 A nel caso peggiore, e né l'USB né il pin 5V di Arduino possono fornirli. Alimenta la matrice con un alimentatore esterno 5 V da 3–4 A, collega il GND dell'alimentatore al GND di Arduino e metti una resistenza da 330 Ω sulla linea dati e un condensatore da 1000 µF sull'ingresso di alimentazione. Nel codice setBrightness(30) riduce la luminosità a circa il 12 %; dato che in Snake pochi LED sono accesi insieme, la corrente resta di qualche centinaio di mA. Per un'alternativa monocromatica che usa meno RAM, guarda matrice LED MAX7219.

Gioco di memoria Simon: 4 LED e 4 pulsanti

Simon è il leggendario gioco di memoria uscito nel 1978: a ogni turno il dispositivo aggiunge un colore alla sequenza e tu devi ripetere l'intera sequenza nello stesso ordine. Componenti: quattro LED colorati (verde, rosso, giallo, blu), una resistenza da 220 Ω per ciascuno, quattro pulsanti e un buzzer passivo. Collega i LED a D9–D12, i pulsanti a D2–D5 e il buzzer a D8; questo gioco è un circuito separato dalla console comune. Puoi scegliere la resistenza giusta per i tuoi LED con il calcolatore di resistenza per LED. I quattro suoni del Simon originale sono note di un accordo di la maggiore; il codice usa le stesse note (330, 440, 554 e 659 Hz), per questo suona gradevole anche quando premi in fretta.

C++ (Arduino IDE)
1#include <EEPROM.h>
2
3const byte LED_PINS[4]    = {9, 10, 11, 12};       // yeşil, kırmızı, sarı, mavi / green, red, yellow, blue
4const byte BUTTON_PINS[4] = {2, 3, 4, 5};          // LED'lerle aynı sırada / same order as the LEDs
5const int  TONES[4]       = {330, 440, 554, 659};  // E4, A4, C#5, E5: A majör akoru / A major chord
6const byte BUZZER_PIN     = 8;
7const int  HISCORE_ADDR   = 8;                     // EEPROM 8-9. bayt / EEPROM bytes 8-9
8const byte MAX_LEVEL      = 50;
9
10byte sequence[MAX_LEVEL];
11byte level = 0;
12uint16_t hiScore;
13
14void playColor(byte c, int ms) {
15  digitalWrite(LED_PINS[c], HIGH);
16  tone(BUZZER_PIN, TONES[c]);
17  delay(ms);
18  digitalWrite(LED_PINS[c], LOW);
19  noTone(BUZZER_PIN);
20}
21
22// Basılan rengi döndürür; 3 sn içinde basılmazsa -1 / returns the pressed colour; -1 after 3 s
23int waitForButton() {
24  unsigned long start = millis();
25  while (millis() - start < 3000) {
26    for (byte i = 0; i < 4; i++) {
27      if (digitalRead(BUTTON_PINS[i]) == LOW) {
28        playColor(i, 200);                              // basılan rengi göster / show the pressed colour
29        while (digitalRead(BUTTON_PINS[i]) == LOW);     // bırakılmasını bekle / wait for release
30        delay(30);                                      // sıçramayı atla / skip the bounce
31        return i;
32      }
33    }
34  }
35  return -1;
36}
37
38// LED'ler döner; ilk basış oyunu başlatır ve rastgeleliği tohumlar
39// LEDs chase until the first press, which starts the game and seeds the randomness
40void waitForStart() {
41  byte i = 0;
42  while (true) {
43    digitalWrite(LED_PINS[i], HIGH);
44    for (byte t = 0; t < 20; t++) {                     // 200 ms boyunca butonlara bak / poll the buttons for 200 ms
45      for (byte b = 0; b < 4; b++) {
46        if (digitalRead(BUTTON_PINS[b]) == LOW) {
47          digitalWrite(LED_PINS[i], LOW);
48          randomSeed(micros());                         // basış anı her seferinde farklı / the press time always differs
49          while (digitalRead(BUTTON_PINS[b]) == LOW);
50          delay(600);
51          return;
52        }
53      }
54      delay(10);
55    }
56    digitalWrite(LED_PINS[i], LOW);
57    i = (i + 1) % 4;
58  }
59}
60
61void endGame(uint16_t score) {
62  tone(BUZZER_PIN, 110, 700);
63  for (byte k = 0; k < 3; k++) {                        // tüm LED'ler yanıp söner / all LEDs flash
64    for (byte i = 0; i < 4; i++) digitalWrite(LED_PINS[i], HIGH);
65    delay(200);
66    for (byte i = 0; i < 4; i++) digitalWrite(LED_PINS[i], LOW);
67    delay(200);
68  }
69  if (score > hiScore) { hiScore = score; EEPROM.put(HISCORE_ADDR, hiScore); }
70  Serial.print(F("Seviye / Level: "));  Serial.print(score);
71  Serial.print(F("  Rekor / Best: ")); Serial.println(hiScore);
72  level = 0;
73  delay(1000);
74}
75
76void setup() {
77  for (byte i = 0; i < 4; i++) {
78    pinMode(LED_PINS[i], OUTPUT);
79    pinMode(BUTTON_PINS[i], INPUT_PULLUP);              // basınca LOW / LOW when pressed
80  }
81  Serial.begin(9600);
82  EEPROM.get(HISCORE_ADDR, hiScore);
83  if (hiScore == 0xFFFF) hiScore = 0;
84}
85
86void loop() {
87  if (level == 0) waitForStart();
88  if (level == MAX_LEVEL) { endGame(MAX_LEVEL); return; }   // hepsini bildin / you completed every level
89
90  sequence[level++] = random(4);                       // diziye yeni bir renk ekle / append a new colour
91  int showMs = max(180, 520 - level * 20);             // seviye arttıkça hızlanır / faster at higher levels
92  for (byte i = 0; i < level; i++) {                   // diziyi baştan göster / replay the whole sequence
93    playColor(sequence[i], showMs);
94    delay(showMs / 2);
95  }
96  for (byte i = 0; i < level; i++) {                   // oyuncu aynı sırayı tekrarlar / the player repeats it
97    if (waitForButton() != sequence[i]) {              // yanlış ya da geç / wrong or too late
98      endGame(level - 1);
99      return;
100    }
101  }
102  delay(700);                                          // tur arası / pause between rounds
103}

In Simon la casualità è tutto: se la sequenza è uguale in ogni partita, il giocatore la impara a memoria. Il metodo randomSeed(analogRead(A0)), che legge un pin scollegato, in pratica è debole, perché il pin restituisce spesso valori simili. Questo codice usa un trucco più solido: il gioco aspetta la prima pressione del giocatore e prende come seme il valore di micros() in quell'istante. Nessuno riesce a ripetere il momento di una pressione al microsecondo, quindi ogni partita parte in modo diverso. Il tempo di visualizzazione si accorcia di 20 ms a ogni livello e si ferma a 180 ms; se non premi entro 3 secondi o premi il colore sbagliato, la partita finisce e, se è un record, il livello raggiunto viene salvato nella EEPROM.

Runner su LCD: caratteri personalizzati su un display 16x2

Su un LCD a caratteri 16x2 non puoi disegnare pixel, ma con createChar() puoi definire fino a 8 caratteri personalizzati da 5×8 pixel. Per il runner ne bastano tre: due pose di corsa e un cactus. La riga inferiore del display è il terreno; i cactus scorrono da destra a sinistra e, quando premi il pulsante, il corridore sale sulla riga superiore e resta in aria per qualche passo. Poiché il gioco è a griglia, il controllo della collisione è una sola riga: se nella colonna del corridore c'è un cactus mentre lui è a terra, la partita finisce. Per la libreria e il collegamento, guarda display LCD I2C.

C++ (Arduino IDE)
1#include <Wire.h>
2#include <EEPROM.h>
3#include <LiquidCrystal_I2C.h>
4
5LiquidCrystal_I2C lcd(0x27, 16, 2);  // adres 0x3F de olabilir / the address may also be 0x3F
6
7const byte JUMP_PIN = 3, BUZZER_PIN = 8;
8const int  HISCORE_ADDR = 4;         // EEPROM 4-5. bayt / EEPROM bytes 4-5
9const byte PLAYER_COL = 1;           // koşucunun sütunu / runner's column
10const byte JUMP_STEPS = 4;           // zıplama süresi (adım) / jump length (steps)
11const byte MIN_GAP = 4;              // kaktüsler arası en az boşluk / minimum gap between cacti
12
13// 5x8 özel karakterler / 5x8 custom characters
14byte runA[8]   = {0b01110, 0b01110, 0b00100, 0b01110, 0b10101, 0b00100, 0b01010, 0b10001};
15byte runB[8]   = {0b01110, 0b01110, 0b00100, 0b01110, 0b10101, 0b00100, 0b01010, 0b01010};
16byte cactus[8] = {0b00100, 0b00101, 0b10101, 0b10101, 0b10110, 0b01100, 0b00100, 0b00100};
17
18byte ground[16];                     // 1 = o sütunda kaktüs var / 1 = cactus in that column
19byte gap = 0, jumpLeft = 0;
20uint16_t score = 0, hiScore = 0;
21unsigned int stepMs = 300;
22unsigned long lastStep = 0;
23bool alive = false;
24
25// Sıçramasız basış: yalnız basıldığı an true / bounce-free: true only at the moment of a press
26bool buttonPressed() {
27  static bool stable = HIGH, lastRead = HIGH;
28  static unsigned long changedAt = 0;
29  bool r = digitalRead(JUMP_PIN);
30  if (r != lastRead) { lastRead = r; changedAt = millis(); }
31  if (millis() - changedAt > 15 && r != stable) {
32    stable = r;
33    return stable == LOW;
34  }
35  return false;
36}
37
38void draw() {
39  byte row = jumpLeft > 0 ? 0 : 1;   // havadaysa üst satır / top row while jumping
40  lcd.setCursor(0, 0);
41  for (byte c = 0; c < 11; c++) lcd.write((uint8_t)(row == 0 && c == PLAYER_COL ? 0 : ' '));
42  char buf[6];
43  sprintf(buf, "%5u", score);        // sağa yaslı skor / right-aligned score
44  lcd.print(buf);
45  lcd.setCursor(0, 1);
46  for (byte c = 0; c < 16; c++) {
47    if (row == 1 && c == PLAYER_COL) lcd.write((uint8_t)(score & 1));   // runA / runB sırayla / alternately
48    else lcd.write((uint8_t)(ground[c] ? 2 : ' '));
49  }
50}
51
52void startGame() {
53  memset(ground, 0, sizeof(ground));
54  gap = 0; jumpLeft = 0; score = 0; stepMs = 300;
55  alive = true;
56  lastStep = millis();
57  draw();
58}
59
60void die() {
61  alive = false;
62  tone(BUZZER_PIN, 150, 400);
63  if (score > hiScore) { hiScore = score; EEPROM.put(HISCORE_ADDR, hiScore); }
64  lcd.clear();
65  lcd.print(F("GAME OVER ")); lcd.print(score);
66  lcd.setCursor(0, 1);
67  lcd.print(F("BEST ")); lcd.print(hiScore);
68}
69
70void setup() {
71  pinMode(JUMP_PIN, INPUT_PULLUP);
72  lcd.init();
73  lcd.backlight();
74  lcd.createChar(0, runA);
75  lcd.createChar(1, runB);
76  lcd.createChar(2, cactus);
77  EEPROM.get(HISCORE_ADDR, hiScore);
78  if (hiScore == 0xFFFF) hiScore = 0;
79  randomSeed(analogRead(A3));
80  lcd.setCursor(0, 0); lcd.print(F("LCD RUNNER"));
81  lcd.setCursor(0, 1); lcd.print(F("BEST ")); lcd.print(hiScore);
82}
83
84void loop() {
85  if (buttonPressed()) {
86    if (!alive) startGame();                       // başlat / start
87    else if (jumpLeft == 0) {                      // yerdeyse zıpla / jump only from the ground
88      jumpLeft = JUMP_STEPS;
89      tone(BUZZER_PIN, 1200, 20);
90      draw();                                      // zıplamayı hemen göster / show the jump at once
91    }
92  }
93  if (!alive || millis() - lastStep < stepMs) return;
94  lastStep = millis();
95
96  for (byte c = 0; c < 15; c++) ground[c] = ground[c + 1];   // dünya sola kayar / the world scrolls left
97  if (gap >= MIN_GAP && random(100) < 35) { ground[15] = 1; gap = 0; }
98  else { ground[15] = 0; gap++; }
99
100  if (jumpLeft > 0) jumpLeft--;                              // havada geçen bir adım / one step in the air
101  if (jumpLeft == 0 && ground[PLAYER_COL]) { die(); return; } // yerdeyken kaktüse çarptı / hit a cactus on the ground
102
103  score++;
104  if (score % 25 == 0 && stepMs > 120) stepMs -= 15;         // giderek hızlan / speed up gradually
105  draw();
106}

Far scorrere il mondo significa copiare ogni elemento dell'array ground di una posizione a sinistra; la colonna più a destra riceve un nuovo cactus con il 35 per cento di probabilità. La regola MIN_GAP = 4 lascia almeno quattro colonne vuote tra due cactus, così ogni ostacolo è superabile. Ogni 25 punti il passo si accorcia di 15 ms, da 300 a 120 ms. Riscrivere le due righe a ogni passo invece di cancellare lo schermo con lcd.clear() evita lo sfarfallio; il solo comando clear() richiede circa 2 ms. Se vedi solo quadratini, gira il trimmer del contrasto sul retro del modulo; se non vedi nulla, cambia l'indirizzo da 0x27 a 0x3F.

Test di reazione: misura il tuo tempo di reazione in millisecondi

È il gioco più semplice e il più coinvolgente: un LED si accende in un momento casuale, tu premi il pulsante il più in fretta possibile e Arduino scrive nel monitor seriale il tempo trascorso in millisecondi. Ti servono solo un LED su D7 con una resistenza da 220 Ω, il pulsante su D3 e, se vuoi, un buzzer. L'attesa è casuale, tra 2 e 5 secondi; se premi durante l'attesa ricevi l'avviso "troppo presto", quindi non si può barare tirando a indovinare. Il tempo viene misurato con micros(), viene calcolata la tua media e il tuo tempo migliore viene salvato nella EEPROM.

C++ (Arduino IDE)
1#include <EEPROM.h>
2
3const byte LED_PIN = 7, BUTTON_PIN = 3, BUZZER_PIN = 8;
4const int  BEST_ADDR = 6;            // EEPROM 6-7. bayt / EEPROM bytes 6-7
5const bool USE_SOUND = false;        // true: LED ile birlikte bip / true: beep together with the LED
6
7uint16_t best;                       // en iyi süre (ms); 0xFFFF = kayıt yok / best time (ms); 0xFFFF = none
8unsigned long total = 0;
9unsigned int rounds = 0;
10
11bool pressed() { return digitalRead(BUTTON_PIN) == LOW; }
12
13void waitRelease() {
14  while (pressed());
15  delay(50);                         // bırakma sıçramasını atla / skip the release bounce
16}
17
18void setup() {
19  pinMode(LED_PIN, OUTPUT);
20  pinMode(BUTTON_PIN, INPUT_PULLUP);
21  Serial.begin(9600);
22  EEPROM.get(BEST_ADDR, best);       // boş EEPROM 0xFFFF = kayıt yok / blank EEPROM 0xFFFF = no record
23  Serial.println(F("Baslamak icin butona bas / Press the button to start"));
24}
25
26void loop() {
27  while (!pressed());                // başlatma basışı / start press
28  randomSeed(micros());
29  waitRelease();
30  Serial.println(F("Hazir ol... / Get ready..."));
31
32  unsigned long waitMs = random(2000, 5001);   // 2-5 sn rastgele bekleme / random 2-5 s wait
33  unsigned long start = millis();
34  while (millis() - start < waitMs) {
35    if (pressed()) {                 // erken basış / false start
36      tone(BUZZER_PIN, 150, 500);
37      Serial.println(F("Erken bastin! / Too early!"));
38      waitRelease();
39      return;
40    }
41  }
42
43  unsigned long t0 = micros();
44  digitalWrite(LED_PIN, HIGH);       // işaret / signal
45  if (USE_SOUND) tone(BUZZER_PIN, 2000, 50);
46  while (!pressed() && micros() - t0 < 2000000UL);   // en fazla 2 sn bekle / wait at most 2 s
47  unsigned long ms = (micros() - t0) / 1000;
48  digitalWrite(LED_PIN, LOW);
49
50  if (ms >= 2000) {
51    Serial.println(F("Cok yavas / Too slow"));
52    return;
53  }
54  total += ms; rounds++;
55  Serial.print(F("Sure / Time: ")); Serial.print(ms);
56  Serial.print(F(" ms   Ortalama / Average: ")); Serial.print(total / rounds);
57  Serial.println(F(" ms"));
58  if (ms < best) {                   // daha kısa süre daha iyi / shorter is better
59    best = ms;
60    EEPROM.put(BEST_ADDR, best);
61    tone(BUZZER_PIN, 1500, 150);
62    Serial.println(F("Yeni rekor! / New record!"));
63  }
64  Serial.print(F("Rekor / Best: ")); Serial.print(best); Serial.println(F(" ms"));
65  waitRelease();
66  delay(1000);
67}

Il tempo medio di reazione umano alla luce è di circa 200–250 ms; un giocatore ben concentrato può scendere un po' sotto. Se imposti USE_SOUND a true, insieme al LED suona anche un bip. Di solito si reagisce un po' più in fretta a un suono che a una luce, quindi confrontare i record delle due modalità è un esperimento interessante. Se mostri il tempo su un display a 7 segmenti o su un OLED invece che nel monitor seriale, ottieni un dispositivo a batteria che funziona senza computer.

Creare giochi con ESP32

Gran parte del codice di questa guida funziona su ESP32 con piccole modifiche: le librerie Adafruit SSD1306 e NeoPixel supportano ESP32, e tone() è disponibile nelle versioni 2.x e 3.x del core Arduino per ESP32. Le cose da cambiare sono tre: i pin (colonna ESP32 della tabella comune), le soglie del joystick e il salvataggio del record. L'ADC di ESP32 è a 12 bit e restituisce valori da 0 a 4095, quindi usa circa 1200 e 2800 al posto di 300 e 700. Collega il joystick ai pin ADC1, GPIO 32–39, perché i pin ADC2 non si possono leggere con il Wi-Fi attivo.

ESP32 non ha una vera EEPROM; la libreria EEPROM la emula nella flash e richiede le chiamate EEPROM.begin() ed EEPROM.commit(). La strada più pulita è la libreria Preferences: salva i valori con uno spazio dei nomi (namespace) e una chiave nella partizione NVS della flash e distribuisce l'usura da sola. Il gioco "gara di clic" qui sotto conta quante volte riesci a premere il pulsante in 5 secondi e salva il record con Preferences:

C++ (Arduino IDE)
1#include <Wire.h>
2#include <Adafruit_GFX.h>
3#include <Adafruit_SSD1306.h>
4#include <Preferences.h>
5
6Adafruit_SSD1306 display(128, 64, &Wire, -1);   // SDA = GPIO 21, SCL = GPIO 22
7Preferences prefs;
8
9const int BUTTON_PIN = 26;           // aksiyon butonu / action button
10const int BUZZER_PIN = 27;           // pasif buzzer / passive buzzer
11const unsigned long ROUND_MS = 5000; // 5 saniyelik tur / 5-second round
12
13uint32_t best = 0;
14
15// Sıçramasız basış: yalnız basıldığı an true / bounce-free: true only at the moment of a press
16bool buttonPressed() {
17  static bool stable = HIGH, lastRead = HIGH;
18  static unsigned long changedAt = 0;
19  bool r = digitalRead(BUTTON_PIN);
20  if (r != lastRead) { lastRead = r; changedAt = millis(); }
21  if (millis() - changedAt > 10 && r != stable) {
22    stable = r;
23    return stable == LOW;
24  }
25  return false;
26}
27
28void screen(const char *title, uint32_t big, uint32_t small) {
29  display.clearDisplay();
30  display.setTextSize(1);
31  display.setCursor(0, 0);   display.print(title);
32  display.setCursor(0, 56);  display.print("BEST "); display.print(best);
33  display.setTextSize(3);
34  display.setCursor(0, 20);  display.print(big);
35  display.setTextSize(1);
36  display.setCursor(90, 56); display.print(small);
37  display.display();
38}
39
40void setup() {
41  pinMode(BUTTON_PIN, INPUT_PULLUP);
42  Serial.begin(115200);
43  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
44    Serial.println("SSD1306 allocation failed");
45    for (;;) delay(1000);
46  }
47  display.setTextColor(SSD1306_WHITE);
48  prefs.begin("oyun", false);        // "oyun" ad alanı, okuma-yazma / "oyun" namespace, read-write
49  best = prefs.getUInt("rekor", 0);  // kayıt yoksa 0 / 0 when nothing is stored
50}
51
52void loop() {
53  screen("TAP RACE: PRESS", 0, 0);
54  while (!buttonPressed()) delay(1); // başlatma / start
55  for (int i = 3; i > 0; i--) {      // 3-2-1 geri sayım / 3-2-1 countdown
56    screen("GET READY", i, 0);
57    tone(BUZZER_PIN, 800, 80);
58    delay(1000);
59  }
60  tone(BUZZER_PIN, 1600, 150);
61
62  uint32_t taps = 0;
63  unsigned long start = millis(), lastDraw = 0;
64  while (millis() - start < ROUND_MS) {
65    if (buttonPressed()) taps++;
66    if (millis() - lastDraw > 100) { // ekranı 10 kez/sn çiz / redraw 10 times per second
67      lastDraw = millis();
68      screen("TAP!", taps, (ROUND_MS - (millis() - start)) / 1000 + 1);
69    }
70  }
71
72  if (taps > best) {                 // yeni rekor flash'a / new record to flash
73    best = taps;
74    prefs.putUInt("rekor", best);
75    tone(BUZZER_PIN, 2000, 300);
76  }
77  screen("RESULT", taps, 0);
78  Serial.printf("Basis / Taps: %lu  Rekor / Best: %lu\n", (unsigned long)taps, (unsigned long)best);
79  delay(3000);
80}

prefs.getUInt("rekor", 0) restituisce il secondo parametro se non c'è nulla di salvato, quindi non devi mai preoccuparti del problema dello 0xFFFF della EEPROM. I nomi dello spazio e della chiave possono avere al massimo 15 caratteri. Anche se il processore di ESP32 è molte volte più veloce di quello di Uno, l'OLED usa lo stesso bus I2C, quindi la frequenza dei fotogrammi cambia poco; per giochi molto più fluidi e a colori puoi usare un display TFT ILI9341 via SPI. Il Bluetooth di ESP32, invece, è un buon punto di partenza per trasformare lo smartphone in un controller.

Giochi con Raspberry Pi Pico e MicroPython

Se programmi il Raspberry Pi Pico in MicroPython, la logica del gioco resta la stessa e cambia solo la sintassi: time.ticks_ms() e time.ticks_diff() al posto di millis(), read_u16(), che restituisce valori da 0 a 65535, al posto di analogRead(), e il PWM al posto di tone(). Il Pico non ha una EEPROM, ma grazie al file system di MicroPython è facilissimo scrivere il record in un piccolo file di testo nella flash. Il codice qui sotto è la versione per Pico del test di reazione e salva il record nel file rekor.txt; se lo salvi sulla scheda come main.py con Thonny, partirà da solo a ogni accensione. Se sei alle prime armi con MicroPython, leggi prima cos'è il Raspberry Pi Pico.

MicroPython
1from machine import Pin, PWM
2import time, random
3
4led = Pin("LED", Pin.OUT)                 # kart üstündeki LED / onboard LED
5button = Pin(14, Pin.IN, Pin.PULL_UP)     # GP14, basınca 0 / 0 when pressed
6buzzer = PWM(Pin(16))                     # GP16, pasif buzzer / passive buzzer
7buzzer.duty_u16(0)                        # başlangıçta sessiz / silent at start
8FILE = "rekor.txt"                        # rekor flash'ta dosyada / record kept in a file on flash
9
10def beep(freq, ms):
11    buzzer.freq(freq)
12    buzzer.duty_u16(32768)                # %50 görev oranı / 50% duty cycle
13    time.sleep_ms(ms)
14    buzzer.duty_u16(0)
15
16def load_best():
17    try:
18        with open(FILE) as f:
19            return int(f.read())
20    except (OSError, ValueError):         # dosya yok ya da bozuk / file missing or corrupt
21        return 99999
22
23def save_best(ms):
24    with open(FILE, "w") as f:
25        f.write(str(ms))
26
27def wait_release():
28    while button.value() == 0:
29        time.sleep_ms(5)
30    time.sleep_ms(50)                     # bırakma sıçraması / release bounce
31
32best = load_best()
33print("Baslamak icin butona bas / Press the button to start")
34
35while True:
36    while button.value() == 1:            # başlatma basışı / start press
37        time.sleep_ms(5)
38    wait_release()
39    print("Hazir ol... / Get ready...")
40
41    wait = random.randint(2000, 5000)     # 2-5 sn rastgele bekleme / random 2-5 s wait
42    start = time.ticks_ms()
43    early = False
44    while time.ticks_diff(time.ticks_ms(), start) < wait:
45        if button.value() == 0:
46            early = True
47            break
48    if early:
49        print("Erken bastin! / Too early!")
50        beep(150, 400)
51        wait_release()
52        continue
53
54    t0 = time.ticks_us()
55    led.on()                              # işaret / signal
56    while button.value() == 1 and time.ticks_diff(time.ticks_us(), t0) < 2000000:
57        pass
58    ms = time.ticks_diff(time.ticks_us(), t0) // 1000
59    led.off()
60
61    if ms >= 2000:
62        print("Cok yavas / Too slow")
63        continue
64    print("Sure / Time:", ms, "ms")
65    if ms < best:                         # daha kısa süre daha iyi / shorter is better
66        best = ms
67        save_best(best)
68        print("Yeni rekor! / New record!")
69        beep(1500, 150)
70    print("Rekor / Best:", best, "ms")
71    wait_release()
72    time.sleep(1)

Quando misuri il tempo è importante usare ticks_diff(): i contatori ripartono da zero dopo un certo valore e sottrarre direttamente due valori in quel momento dà un risultato sbagliato; ticks_diff() gestisce correttamente questo overflow. Per i giochi con OLED, il driver ssd1306 che installi dal gestore pacchetti di Thonny offre comandi come fill_rect(), text() e show(), simili a quelli di Adafruit GFX. Il modulo neopixel che serve per una matrice NeoPixel è già incluso in MicroPython. Così puoi portare anche Pong e Snake sul Pico con la stessa logica.

Errori comuni e soluzioni

SintomoCausaSoluzione
Il display resta vuotoSe il monitor seriale mostra "SSD1306 allocation failed" manca RAM; altrimenti l'indirizzo o i collegamenti sono sbagliatiMetti i testi fissi dentro F() e riduci gli array grandi; senza il messaggio controlla i fili SDA/SCL e prova l'indirizzo 0x3D
Il gioco inizia sempre allo stesso modoManca randomSeed() o il seme è sempre ugualeUsa come seme il valore di micros() alla prima pressione del giocatore
Una pressione causa due saltiRimbalzo del pulsanteUsa la funzione buttonPressed(), che attende 15 ms di stabilità
Il record mostra 65535Una EEPROM mai scritta legge 0xFFConsidera 0xFFFF come 0
Il personaggio si sposta da solo con il joystick rilasciatoIl centro non è 512 e la zona morta è troppo strettaMantieni le soglie 300/700 o misura il centro all'avvio e calibra
Il serpente salta in diagonaleLa matrice è collegata a zigzagImposta SERPENTINE = true
Arduino si riavvia quando aumenta la luminositàLa matrice LED assorbe troppa correnteAbbassa setBrightness() e usa un alimentatore esterno a 5 V
Il PWM sul pin 3 o 11 smette di funzionare quando suona tone()Su Uno, tone() usa il Timer2Sposta il componente che richiede il PWM sul pin 5, 6, 9 o 10
Il gioco rallenta col passare del tempoSerial.print o disegno inutile a ogni fotogrammaTogli l'output seriale e tieni il fotogramma sopra il tempo di invio del display
L'LCD mostra solo quadratiniContrasto sbagliato o indirizzo erratoGira il trimmer sul retro e prova l'indirizzo 0x27 o 0x3F
Se un gioco non si comporta come ti aspetti, prova prima la logica senza display: stampa nel monitor seriale posizione, velocità e stato a ogni fotogramma e segui qualche fotogramma a mano. Trovato l'errore, cancella quelle righe, perché Serial.print costa qualche millisecondo per fotogramma e altera la velocità del gioco. Se il codice non si carica nemmeno sulla scheda, consulta la guida Arduino non carica lo sketch.

Idee per migliorare i tuoi giochi

  • Pong in due: cancella le righe della CPU, controlla la seconda racchetta con un potenziometro su A2 e porta i due controller alle estremità del tavolo con cavi lunghi.
  • Controllo a inclinazione: usa un MPU6050 al posto del joystick e guida il serpente o la racchetta inclinando la scheda.
  • Musica ed effetti: riproduci musica di gioco con un DFPlayer Mini; per effetti brevi basta un buzzer passivo.
  • Giocare con lo smartphone: trasforma il telefono in un controller wireless con un modulo Bluetooth HC-05.
  • Controller per il computer: dato che Arduino Leonardo può comportarsi come una tastiera USB, puoi costruire un controller per giochi su PC con joystick e pulsanti.
  • Tabellone dei punteggi: mostra i record come testo scorrevole su un modulo MAX7219 4 in 1.
  • Giochi di fortuna: realizza una roulette con un anello WS2812B o un dado elettronico con i LED; il modo più divertente per esercitarti con random().

Creare giochi con Arduino è il modo più piacevole per imparare insieme codice e hardware. Una volta capiti il game loop, il rilevamento delle collisioni e il record nella EEPROM, progettare un gioco tutto tuo diventa solo una questione di idee; per altra ispirazione dai un'occhiata all'elenco di idee per progetti Arduino. Nell'app BlueGrays trovi Pong, il T-Rex su OLED, Snake su matrici 8x8 e 16x16, Simon, il runner su LCD e il test di reazione come progetti passo passo, con schemi del circuito e codice pronto all'uso.

Domande frequenti

Quali giochi si possono fare con Arduino?

Con un Arduino Uno puoi realizzare Pong, il T-Rex runner, giochi in stile Flappy Bird, Snake, semplici giochi a blocchi, il gioco di memoria Simon, un test di reazione e giochi di fortuna come un dado o una roulette. Per i giochi con grafica basta un OLED da 128×64, per quelli colorati a griglia una matrice LED NeoPixel. Simon e il test di reazione si giocano persino senza display, solo con LED e pulsanti.

Qual è il display migliore per un gioco con Arduino?

Per chi inizia la scelta migliore è l'OLED SSD1306 da 0,96 pollici: costa poco, si collega con soli due fili dati e disegna grafica in movimento a 128×64 pixel. Se vuoi un'immagine più grande e a colori va bene un display TFT ILI9341 o ST7789, per i giochi in stile pixel art una matrice LED 8x8 o 16x16. Anche un LCD 16x2 con caratteri personalizzati basta per semplici giochi di corsa.

Come si salva il record (high score) su Arduino?

Se scrivi il record nella EEPROM, non si perde nemmeno quando manca la corrente. Lo scrivi con EEPROM.put(indirizzo, record) e lo leggi con EEPROM.get(indirizzo, record); Arduino Uno ha a disposizione 1024 byte. Poiché ogni cella sopporta circa 100.000 scritture, scrivi solo a fine partita e solo se il record è stato battuto.

Si può costruire un gamepad con Arduino?

Sì. Le schede con il processore ATmega32U4, come Arduino Leonardo, Micro o Pro Micro, possono presentarsi al computer come tastiera, mouse o controller USB; leggono joystick e pulsanti e inviano pressioni di tasti. Arduino Uno non può farlo direttamente, ma può inviare i valori del joystick tramite porta seriale a un programma sul computer.

Perché il mio gioco Arduino è lento o scatta?

Le cause più comuni sono i cicli costruiti con delay(), le chiamate a Serial.print a ogni fotogramma e il tempo di invio del display. Su Uno, inviare tutta l'immagine dell'OLED richiede circa 25 ms, quindi un fotogramma da 30 ms è realistico. Usa una durata di fotogramma fissa con millis() al posto di delay(), togli l'output seriale e disegna solo ciò che serve.

Qual è il gioco Arduino più facile per principianti?

Il test di reazione è il più facile: richiede un LED, un pulsante e uno sketch breve, ma insegna concetti di base come temporizzazione, numeri casuali ed EEPROM. Dopo, un buon percorso è passare a Simon con quattro LED e quattro pulsanti, e poi a Pong e T-Rex con un display OLED.

Vedi tutti i progetti
Gioco delle Racchette - SSD1306
#86Intermedio
Premium

Gioco delle Racchette - SSD1306

Una pallina, due racchette e tu: giochi a ping pong, uno dei primissimi videogiochi, su un piccolo display OLED. Con due pulsanti muovi su e giù la tua racchetta a destra, mentre il computer guida que

4.5(47)
65
OLED T-Rex Run: Gioco del dinosauro con Arduino
#87Avanzato
Premium

OLED T-Rex Run: Gioco del dinosauro con Arduino

Il gioco del dinosauro che compare quando salta internet, ora sulla tua scrivania: il dinosauro corre sul piccolo display OLED, i cactus gli vengono incontro e tu salti con un solo pulsante. Il punteg

4.7(33)
53
Serpente di pixel Matrice 8x8
#88Avanzato
Premium

Serpente di pixel Matrice 8x8

Costruisci con le tue mani il classico gioco del serpente e giocaci. Su una matrice di luci colorate 8x8 guidi un serpente verde con un joystick e mangi mele rosse; a ogni mela il serpente si allunga,

4.7(39)
57
Matrice Snake 16x16
#89Avanzato
Premium

Matrice Snake 16x16

Il classico gioco del serpente, questa volta su un enorme display LED fatto di 256 luci. Guidi il serpente verde con un joystick e raccogli le mele rosse; a ogni mela il serpente si allunga, il punteg

4.8(52)
67
Gioco di memoria con Arduino (4 LED + 4 pulsanti + buzzer)
#93Avanzato
Premium

Gioco di memoria con Arduino (4 LED + 4 pulsanti + buzzer)

Ti fidi della tua memoria? Quattro LED colorati si accendono in sequenza, ognuno con la sua nota, e tu ripeti lo stesso ordine premendo i pulsanti. Ogni turno giusto aggiunge un nuovo colore alla sequ

4.8(46)
69
Gioco runner su LCD con Arduino
#98Intermedio
Premium

Gioco runner su LCD con Arduino

Gioco endless runner a scorrimento laterale su schermo LCD a 16x2 caratteri. L'eroe corre automaticamente, il giocatore salta gli ostacoli premendo il pulsante. Sulla riga superiore e inferiore compaiono ostacoli casuali. Il cicalino passivo riproduce la melodia di apertura, il suono di salto, il suono di celebrazione dei 50 punti e la melodia di fine gioco. All'avvio viene visualizzata la schermata iniziale "BlueGrays - Arduino Projects".

4.6(32)
55
Arduino Test di Reazione con Segnale Acustico: Misura i tuoi Riflessi in Millisecondi (LCD + Buzzer + 3 LED)
#97Intermedio
Premium

Arduino Test di Reazione con Segnale Acustico: Misura i tuoi Riflessi in Millisecondi (LCD + Buzzer + 3 LED)

Quanto sono veloci i tuoi riflessi? Premi il pulsante: il LED rosso dice ai vostri posti, il giallo pronti e parte un'attesa casuale da 1,5 a 3,5 secondi. Appena si accende il LED verde e il buzzer suona, premi di nuovo; il tuo tempo di reazione compare in millisecondi sul display LCD I2C, con il record della sessione e la media sotto. Se premi prima del verde, il display mostra TOO EARLY! e senti un suono di sconfitta. Tieni premuto: nuovo giocatore, stesso ordine di attese.

4.6(33)
50