Panduan · 17 mnt baca

Game Arduino: Cara Membuat Pong, T-Rex, Snake, dan Simon

Proyek game Arduino langkah demi langkah: Pong dan T-Rex di OLED, Snake di LED matrix, Simon, runner LCD, dan tes reaksi, plus kode skor tertinggi di EEPROM.

Membuat game dengan Arduino adalah cara paling seru untuk belajar elektronika dan pemrograman: dengan layar OLED, beberapa tombol, dan joystick kamu bisa membuat sendiri game klasik seperti Pong, T-Rex runner, Snake, dan game memori Simon, lalu menantang teman-temanmu memecahkan skor tertinggimu. Sambil menulis game, kamu juga mempelajari topik pemrograman paling penting hampir tanpa sengaja: pewaktuan, state machine, sistem koordinat, matematika tabrakan, bilangan acak, dan memori permanen. Semua game ini berjalan di satu Arduino Uno; RAM 2 KB-nya cukup asalkan kamu memakai memori dengan bijak.

Di panduan ini kita membuat enam game dari awal sampai akhir: Pong dan T-Rex runner di layar OLED, Snake di LED matrix NeoPixel 8x8 dan 16x16, Simon dengan empat LED dan empat tombol, game runner di LCD 16x2, dan tes reaksi yang akurat sampai milidetik. Setiap game dilengkapi daftar komponen, pengkabelan, dan kode yang bisa dikompilasi. Pertama kita pelajari kerangka yang dipakai bersama oleh semuanya, yaitu game loop, deteksi tabrakan, penghitungan skor, dan penyimpanan skor tertinggi di EEPROM, lalu kita pakai ulang di setiap game. Di bagian akhir ada juga versi ESP32 dan Raspberry Pi Pico.

Game Snake Arduino di LED matrix NeoPixel 8x8
LED matrix NeoPixel WS2812B 8x8: 64 piksel berwarna dan satu kabel data, layar mini yang ideal untuk game Snake.

Game mana butuh komponen apa?

Ketika membuat game dengan Arduino, keputusan pertama adalah layarnya, karena layar menentukan jenis game yang bisa kamu buat. Dengan beberapa LED saja kamu sudah bisa membuat Simon atau tes reaksi, sedangkan OLED 128×64 piksel memungkinkan Pong dan T-Rex, lengkap dengan grafik bergerak dan fisika. Tabel di bawah merangkum game di panduan ini dari yang paling mudah sampai yang paling sulit:

GameLayarInputKomponen lainTingkat kesulitan
Tes reaksi1 LED + Serial Monitor1 tombolResistor 220 Ω, buzzer opsionalMudah
Game memori Simon4 LED berwarna4 tombol4 × resistor 220 Ω, buzzer pasifMudah–sedang
Runner LCDLCD I2C 16x21 tombolBuzzer pasifSedang
PongOLED SSD1306 0,96 inciJoystick atau potensiometer 10 kΩBuzzer pasifSedang
T-Rex runnerOLED SSD1306 0,96 inci1 tombol atau tombol joystickBuzzer pasifSedang
Snake (8x8)Matrix NeoPixel 8x8JoystickResistor 330 Ω, kapasitor 1000 µFSedang–mahir
Snake (16x16)Matrix NeoPixel 16x16JoystickCatu daya eksternal 5 V 3–4 AMahir
Arduino Uno
Arduino Uno
OLED SSD1306 0,96 inci
OLED SSD1306 0,96 inci
Joystick
Joystick
LCD 16x2 I2C
LCD 16x2 I2C
Buzzer
Buzzer
Push button
Push button

Pengkabelan bersama: satu rangkaian sebagai konsol game

Kami memilih pin bersama supaya semua game kecuali Simon berjalan di rangkaian yang sama. OLED dan LCD I2C berbagi dua kabel yang sama (SDA dan SCL); joystick masuk ke input analog, tombol aksi ke D3, dan buzzer ke D8. Begitu “konsol game” ini sudah kamu rakit, kamu cukup meng-upload kode yang berbeda untuk berganti game. Peta pin lengkap board-mu bisa kamu lihat di alat Pinout.

Komponen / pinArduino UnoESP32Raspberry Pi Pico
OLED atau LCD I2C: SDAA4GPIO 21GP4
OLED atau LCD I2C: SCLA5GPIO 22GP5
Joystick VRx (sumbu X)A0GPIO 34GP26
Joystick VRy (sumbu Y)A1GPIO 35GP27
Joystick SW (tombol)D2GPIO 25GP15
Tombol aksi (kaki lainnya ke GND)D3GPIO 26GP14
Buzzer pasif (+)D8GPIO 27GP16
DIN NeoPixel (lewat 330 Ω)D6GPIO 13GP22
Di ESP32 dan Pico, beri daya joystick dan OLED dari 3,3 V; joystick yang diberi daya 5 V akan memberikan tegangan lebih dari 3,3 V ke pin analog dan bisa merusak board. Kalau kamu menghubungkan modul LCD I2C 5 V ke ESP32, pasang konverter level logika di antaranya. Semua tombol memakai resistor pull-up internal: satu kaki ke pin, kaki lainnya ke GND, dan pin membaca LOW ketika ditekan.

Bagaimana cara kerja game loop?

Setiap video game dibangun di atas loop empat langkah yang berulang puluhan kali per detik: membaca input (tombol, joystick), memperbarui dunia (posisi, kecepatan, skor), mengecek tabrakan, dan menggambar layar. Satu putaran loop disebut frame. Tiga puluh frame per detik (30 FPS) sudah cukup untuk terlihat mulus, artinya setiap frame berlangsung sekitar 33 ms. Fungsi loop() Arduino memang sudah berupa loop, tetapi berjalan sangat cepat dan tidak teratur, jadi kamu perlu membaginya menjadi frame tetap dengan millis().

Menghindari delay() di sini wajib. Kalau kamu menulis delay(33), Arduino tidak melakukan apa-apa selama 33 ms itu, bisa melewatkan tekanan tombol yang singkat, dan kecepatan game berubah setiap kali proses menggambar butuh waktu lebih lama atau lebih singkat. Dengan millis(), loop() membaca input di setiap putaran tetapi hanya memajukan game ketika sudah waktunya frame baru. Perbedaan keduanya dijelaskan detail di Arduino millis() vs delay(). Kerangka di bawah ini adalah dasar semua game di panduan ini:

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}

Ide kunci kedua di kode ini adalah state machine. Pada setiap momen, game berada di salah satu dari tiga state: MENU, PLAYING, atau GAME_OVER. Setiap state bereaksi berbeda terhadap tombol yang sama: di menu, tekanan tombol memulai game; selama bermain, tombol membuat karakter melompat; dan setelah game over, tombol memulai ronde baru. Memecah kode seperti ini menghilangkan blok if bersarang dan memudahkan penambahan state baru, misalnya PAUSE. Topik ini kami bahas lebih dalam di state machine Arduino.

Membaca tombol dan joystick

Menyambungkan tombol dengan INPUT_PULLUP adalah cara paling rapi: tidak butuh resistor eksternal, pin membaca HIGH ketika diam dan LOW ketika ditekan. Namun, tombol mekanis memantul selama beberapa milidetik ketika ditekan atau dilepas, dan Arduino melihatnya sebagai beberapa tekanan berturut-turut. Di dalam game, itu berarti melompat dua kali untuk satu tekanan, atau Simon menghitung warna yang salah. Fungsi buttonPressed() di kerangka hanya menerima state baru kalau pin sudah stabil selama 15 ms, dan mengembalikan true hanya sekali, tepat pada momen tombol ditekan. Untuk rangkaian dan metode lainnya, lihat Cara Menghubungkan Push Button ke Arduino.

Joystick terdiri dari dua potensiometer dan satu tombol. Di Uno setiap sumbu memberi nilai 0 sampai 1023, dan ketika tuasnya dilepas titik tengahnya sekitar 512, tetapi nilai ini berbeda-beda antarmodul, kira-kira antara 490 dan 530. Karena itu game berbasis grid memakai ambang alih-alih nilai pasti: di bawah 300 berarti kiri atau atas, di atas 700 berarti kanan atau bawah. Zona lebar di antaranya (zona mati) mencegah karakter bergeser sendiri ketika kamu melepas tuas. Di Pong, sebaliknya, nilai analog langsung kita petakan ke pemukul (paddle): map() mengubah rentang 0–1023 menjadi jangkauan gerak pemukul 0–48 piksel. Kalibrasi dan tabel arah ada di panduan modul joystick.

Bagaimana cara kerja deteksi tabrakan?

Deteksi tabrakan menanyakan apakah dua objek berada di tempat yang sama pada waktu yang sama. Di game yang memakai layar, metode paling umum adalah uji AABB (axis-aligned bounding box, kotak pembatas yang sejajar sumbu): setiap objek kamu anggap sebagai persegi panjang. Dua persegi panjang hanya tumpang tindih kalau keempat syarat terpenuhi bersamaan: tepi kiri A ada di kiri tepi kanan B, tepi kanan A ada di kanan tepi kiri B, dan dua syarat yang sama berlaku di sumbu vertikal. Di kode, ini cukup satu baris: ax < bx + bw && ax + aw > bx && ay < by + bh && ay + ah > by. Di game berbasis grid (Snake, runner LCD) bahkan lebih sederhana: kalau dua sel punya koordinat yang sama, terjadi tabrakan.

GameApa bertabrakan dengan apa?Metode
PongBola dengan dinding dan pemukulAABB + membalik kecepatan
T-RexDinosaurus dengan kaktusAABB (dengan kotak yang sedikit lebih kecil)
SnakeKepala dengan dinding, badan, dan makananKesamaan sel grid
Runner LCDPelari dengan kaktusKolom yang sama dan baris yang sama
SimonWarna yang ditekan dengan warna di urutanPerbandingan elemen array
Tes reaksiWaktu tekan dengan waktu sinyalPerbandingan waktu (start terlalu cepat)
Objek kecil yang bergerak cepat bisa menembus langsung rintangan dalam satu frame dan melewatkan tabrakan; ini disebut tunnelling. Di Pong, kalau bola bergerak lebih dari 4 piksel dalam satu frame, bola melintasi zona cek pemukul selebar 3 piksel tanpa pernah menyentuhnya. Solusinya adalah menjaga kecepatan objek per frame lebih kecil dari lebar zona cek, dan itulah tepatnya yang dilakukan batas MAX_SPEED = 2.8 di kode Pong.

Penghitungan skor dan menyimpan skor tertinggi di EEPROM

Skor disimpan di variabel di RAM dan hilang begitu Arduino kamu cabut. Supaya skor tertinggi permanen, kamu memakai EEPROM: ATmega328P di Arduino Uno punya EEPROM 1024 byte yang tetap menyimpan isinya tanpa daya. Dua byte bertipe uint16_t sudah cukup untuk skor tertinggi (0–65535). Satu-satunya kelemahan EEPROM adalah daya tahan tulis: setiap sel bisa ditulis sekitar 100.000 kali. Kedengarannya banyak, tetapi kalau kamu menulis skor setiap frame, 30 kali per detik, sel itu aus dalam sekitar 55 menit. Cara yang benar adalah menulis hanya ketika game berakhir dan rekor terpecahkan; bahkan dengan 10 rekor baru sehari, sel itu akan bertahan 27 tahun.

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() {}

Ada dua poin halus di kode ini. Pertama, setiap byte EEPROM yang belum pernah ditulis bernilai 0xFF, jadi pada start pertama skor tertinggi tampil sebagai 65535, dan pengecekan 0xFFFF mengubahnya menjadi 0. Di tes reaksi justru kebalikannya: 0xFFFF berarti “belum ada rekor”, karena di sana waktu yang lebih singkat lebih baik. Kedua, EEPROM.put() membaca setiap byte sebelum menulis dan hanya menulis byte yang berubah, sehingga umur pakainya makin panjang. Untuk perintah EEPROM lainnya, lihat Memakai EEPROM Arduino. Supaya game di panduan ini bisa kamu upload ke Uno yang sama satu per satu, setiap game punya alamatnya sendiri:

GameAlamat EEPROMNilai yang disimpan
T-Rex runner0–1Skor tertinggi
Snake2–3Makanan terbanyak yang dimakan
Runner LCD4–5Skor tertinggi
Tes reaksi6–7Waktu tersingkat (ms)
Simon8–9Level tertinggi yang dicapai

Pong: layar OLED dan joystick

Pong adalah game tenis meja yang dirilis tahun 1972 dan memulai era video game. Di versi kita, kamu mengendalikan pemukul kiri dengan sumbu Y joystick (atau potensiometer di A1), dan Arduino memainkan pemukul kanan. Komponen: Arduino Uno, OLED I2C SSD1306 0,96 inci, joystick, dan buzzer pasif. Ketika menginstal library Adafruit SSD1306 dari Library Manager, instal juga dependensinya, Adafruit GFX. Pengkabelan layar dan perintah menggambarnya dijelaskan detail di layar 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}

Yang membuat Pong seru adalah fungsi hitPaddle(). Kalau bola mengenai tengah pemukul, bola kembali hampir lurus; kalau mengenai tepinya, bola memantul dengan sudut tajam dan menekan lawan. Kita melakukannya dengan menskalakan jarak bola dari titik tengah pemukul menjadi nilai antara -1 dan +1 lalu mengubahnya menjadi kecepatan vertikal. Bola makin cepat 5% di setiap pukulan dan dibatasi oleh MAX_SPEED. Pemukul Arduino paling banyak bisa bergerak 1.2 piksel per frame, jadi kalau kamu memberi bola sudut tajam, pemukul itu tidak bisa mengejar dan kamu mencetak poin. Untuk membuat game lebih sulit, naikkan nilai itu menjadi 1.5; untuk dua pemain, hapus baris-baris CPU dan kendalikan pemukul kedua dengan potensiometer di A2.

Kenapa waktu frame 30 ms? Uno mengirim frame buffer 1024 byte ke OLED lewat I2C 400 kHz dalam sekitar 25 ms, jadi layar tidak bisa menampilkan lebih dari 35–40 frame per detik. Menjaga waktu frame sedikit di atas waktu transfer itu membuat kecepatan game stabil. Dan jangan tertipu laporan compiler “Global variables use 28%”: buffer SSD1306 dialokasikan ketika program berjalan, jadi pemakaian RAM sebenarnya naik sampai sekitar 78%. Kalau memori habis, kamu akan melihat error SSD1306 allocation failed; solusinya sudah kami kumpulkan di memori Arduino tidak cukup.

T-Rex runner: fisika lompatan dan skor tertinggi

Kita membawa game dinosaurus yang muncul di Chrome ketika kamu sedang offline ke layar OLED. Dinosaurus berlari di tempat, kaktus bergeser dari kanan ke kiri, dan kamu melompatinya dengan menekan tombol. Supaya lompatannya terlihat realistis, kita pakai fisika sederhana: menekan tombol mengatur kecepatan vertikal menjadi -4.2 piksel per frame, setiap frame gravitasi menambahkan 0.3 ke kecepatan itu, dan posisi berubah sesuai kecepatan. Jadi dinosaurus naik sambil melambat, menggantung sejenak di puncak, lalu jatuh makin cepat; ia naik sekitar 27 piksel dan berada di udara selama 27 frame, sekitar 0,8 detik. Komponennya sama dengan Pong; untuk melompat kamu pakai tombol di D3 atau tombol 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}
PengaturanNilaiEfek
JUMP_V-4.2Kekuatan lompatan; di -5 dinosaurus naik sekitar 40 piksel, mendekati bagian atas layar
GRAVITY0.3Nilai yang lebih besar membuat lompatan lebih pendek dan lebih tajam
speed2.0 → 5.0Kecepatan kaktus; bertambah 0.25 setiap 200 frame
random(70, 140)pikselJarak antara dua kaktus; nilai yang lebih kecil membuat game lebih sulit
Hitbox10×12 pikselLebih kecil dari gambar dinosaurus 14×14, jadi nyaris kena tidak dihitung mati

Skor tertinggi ditulis ke EEPROM begitu game berakhir, dan hanya kalau rekornya terpecahkan; skor ini muncul di pojok kanan atas sebagai HI. Kalau kamu menahan tombol D3 sambil menyalakan board, rekor direset; berguna ketika kamu memulai turnamen baru bersama teman-teman. Tinggi dan lebar kaktus ditentukan oleh random(). Supaya setiap game dimulai berbeda, baris randomSeed(analogRead(A3)) memakai derau dari pin analog yang tidak tersambung sebagai seed.

Snake di LED matrix (8x8 dan 16x16)

Game Snake yang dipopulerkan ponsel Nokia lama paling enak dimainkan di LED matrix, karena setiap LED adalah satu sel game. Kita memakai matrix WS2812B NeoPixel berwarna: versi 8x8 punya 64 piksel dan versi 16x16 punya 256, semuanya dikendalikan lewat satu kabel data. Badan ular disimpan di dua array (snakeX dan snakeY), dan elemen 0 adalah kepalanya. Di setiap langkah badan bergeser satu sel dan posisi baru ditulis ke kepala; ketika ular makan, ekornya tidak dihapus, jadi ular bertambah panjang satu sel. Untuk mengompilasi kodenya, instal library Adafruit NeoPixel; detail pengkabelan ada di panduan LED strip 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}

Ada tiga aturan penting di kode ini. Tidak boleh berbalik arah: kalau ular yang bergerak ke kanan tiba-tiba berbelok ke kiri, ia menabrak lehernya sendiri; readJoystick() mengecek arah baru terhadap arah ular yang sekarang, bukan arah yang masih tertunda, jadi dua gerakan cepat pun tidak bisa membaliknya. Pengecualian ekor: di langkah tanpa makanan, ujung ekor ikut bergeser pergi, jadi memindahkan kepala ke sel itu bukan tabrakan. Menempatkan makanan: placeFood() terus mencoba sampai makanan tidak jatuh di atas tubuh ular. Setiap makanan memperpendek waktu langkah 10 ms (dari 300 ms turun ke 120 ms); ketika game berakhir, satu LED menyala biru untuk setiap makanan yang kamu makan, dan rekornya masuk ke EEPROM.

Cara LED di matrix-mu disambungkan juga penting. Di kebanyakan board 8x8 yang kaku, LED berurutan baris demi baris dengan arah yang sama (SERPENTINE = false). Panel 16x16 yang fleksibel biasanya disambung zig-zag: satu baris dari kiri ke kanan, baris berikutnya dari kanan ke kiri. Kalau ular melompat diagonal atau berjalan mundur di setiap baris kedua, atur SERPENTINE menjadi true; kalau arahnya terbalik, putar joystick atau tukar ambang 300 dan 700. Untuk 16x16, cukup atur W dan H menjadi 16; array ular kemudian memakan 512 byte, buffer NeoPixel 768 byte, dan sekitar 75% RAM Uno terpakai.

LED matrix NeoPixel 16x16
Matrix NeoPixel 16x16: arena bermain 256 piksel berarti ular yang lebih panjang dan game yang lebih menantang; butuh catu daya 5 V eksternal.
Satu WS2812B menarik sekitar 60 mA pada putih penuh; 256 LED di matrix 16x16 bisa mencapai 15 A dalam kasus terburuk, dan baik USB maupun pin 5V Arduino tidak sanggup menyuplainya. Beri daya matrix dari catu daya eksternal 5 V 3–4 A, sambungkan GND catu daya ke GND Arduino, dan pasang resistor 330 Ω di jalur data serta kapasitor 1000 µF di input daya. setBrightness(30) di kode menurunkan kecerahan menjadi sekitar 12%; karena di Snake hanya beberapa LED yang menyala bersamaan, arusnya tetap di beberapa ratus mA. Untuk pilihan satu warna yang lebih hemat RAM, lihat LED matrix MAX7219.

Game memori Simon: 4 LED dan 4 tombol

Simon adalah game memori legendaris yang dirilis tahun 1978: setiap ronde perangkat menambahkan satu warna ke urutan, dan kamu mengulang seluruh rangkaian warna itu dengan urutan yang sama. Komponen: empat LED berwarna (hijau, merah, kuning, biru), resistor 220 Ω untuk masing-masing, empat tombol, dan buzzer pasif. Sambungkan LED ke D9–D12, tombol ke D2–D5, dan buzzer ke D8; game ini memakai rangkaian terpisah dari konsol bersama. Resistor yang tepat untuk LED-mu bisa kamu pilih dengan kalkulator resistor LED. Empat bunyi Simon asli adalah nada-nada akor A mayor; kode ini memakai nada yang sama (330, 440, 554, dan 659 Hz), jadi bunyinya tetap enak didengar meski kamu menekan dengan cepat.

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}

Di Simon, keacakan adalah segalanya: kalau urutannya sama di setiap game, pemain tinggal menghafalnya. Metode randomSeed(analogRead(A0)), yang membaca pin yang tidak tersambung, dalam praktiknya lemah karena pin itu sering membaca nilai yang mirip. Kode ini memakai trik yang lebih andal: game menunggu tekanan pertama pemain dan memakai nilai micros() pada momen itu sebagai seed. Tidak ada yang bisa mengulang momen tekanan sampai ketepatan mikrodetik, jadi setiap game dimulai berbeda. Waktu tampil makin singkat 20 ms di setiap level dan berhenti di 180 ms; kalau kamu tidak menekan dalam 3 detik atau menekan warna yang salah, game berakhir, dan level yang kamu capai disimpan ke EEPROM kalau itu rekor.

Game runner LCD: karakter kustom di layar 16x2

Kamu tidak bisa menggambar piksel di LCD karakter 16x2, tetapi dengan createChar() kamu bisa mendefinisikan sampai 8 karakter kustom berukuran 5×8 piksel. Tiga sudah cukup untuk game runner: dua pose berlari dan satu kaktus. Baris bawah layar adalah tanah; kaktus bergeser dari kanan ke kiri, dan ketika kamu menekan tombol, pelari pindah ke baris atas dan tetap di udara selama beberapa langkah. Karena game ini berbasis grid, pengecekan tabrakannya cukup satu baris: kalau ada kaktus di kolom pelari ketika ia berada di tanah, game berakhir. Untuk library dan pengkabelan, lihat layar 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}

Menggeser dunia berarti menyalin setiap elemen array tanah satu tempat ke kiri; kolom paling kanan mendapat kaktus baru dengan peluang 35 persen. Aturan MIN_GAP = 4 menyisakan minimal empat kolom kosong di antara dua kaktus, jadi setiap rintangan bisa dilewati. Setiap 25 poin, waktu langkah berkurang 15 ms, dari 300 ms turun ke 120 ms. Menulis ulang kedua baris di setiap langkah, alih-alih menghapus layar dengan lcd.clear(), mencegah kedipan; perintah clear() sendiri butuh sekitar 2 ms. Kalau di layar hanya terlihat kotak-kotak, putar trimmer kontras di belakang modul; kalau tidak terlihat apa-apa sama sekali, ubah alamat dari 0x27 ke 0x3F.

Tes reaksi: ukur waktu reaksimu dalam milidetik

Ini game paling sederhana sekaligus paling bikin ketagihan: sebuah LED menyala pada momen acak, kamu menekan tombol secepat mungkin, dan Arduino mencetak waktu yang berlalu dalam milidetik ke Serial Monitor. Kamu cukup butuh LED di D7 dengan resistor 220 Ω, tombol di D3, dan buzzer opsional. Waktu tunggunya acak, antara 2 dan 5 detik; kalau kamu menekan selama menunggu, kamu mendapat peringatan “terlalu cepat”, jadi kamu tidak bisa curang dengan menebak. Waktunya diukur dengan micros(), rata-ratamu dihitung, dan waktu terbaikmu disimpan di 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}

Rata-rata waktu reaksi manusia terhadap cahaya sekitar 200–250 ms; pemain yang benar-benar fokus bisa sedikit di bawah itu. Kalau kamu mengatur USE_SOUND menjadi true, bunyi bip diputar bersamaan dengan LED. Reaksi terhadap suara biasanya sedikit lebih cepat daripada terhadap cahaya, jadi membandingkan rekor kedua mode menjadi eksperimen yang menarik. Tampilkan waktunya di display 7-segmen atau OLED alih-alih Serial Monitor, dan kamu punya perangkat bertenaga baterai yang bekerja tanpa komputer.

Membuat game dengan ESP32

Sebagian besar kode di panduan ini berjalan di ESP32 dengan sedikit perubahan: library Adafruit SSD1306 dan NeoPixel mendukung ESP32, dan tone() tersedia di versi 2.x dan 3.x ESP32 Arduino core. Ada tiga hal yang perlu diubah: pin (kolom ESP32 di tabel bersama), ambang joystick, dan cara menyimpan skor tertinggi. ADC ESP32 adalah 12-bit dan mengembalikan nilai 0 sampai 4095, jadi pakai sekitar 1200 dan 2800 sebagai ganti 300 dan 700. Sambungkan joystick ke pin ADC1, GPIO 32–39, karena pin ADC2 tidak bisa dibaca ketika Wi-Fi aktif.

ESP32 tidak punya EEPROM sungguhan; library EEPROM menirunya di flash dan membutuhkan pemanggilan EEPROM.begin() dan EEPROM.commit(). Cara yang lebih rapi adalah library Preferences: library ini menulis nilai dengan namespace dan nama kunci ke partisi NVS di flash dan meratakan keausan dengan sendirinya. Game “balapan tekan” di bawah ini menghitung berapa kali kamu bisa menekan tombol dalam 5 detik dan menyimpan rekornya dengan 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) mengembalikan parameter kedua kalau belum ada data yang tersimpan, jadi kamu tidak perlu berurusan dengan masalah 0xFFFF di EEPROM. Nama namespace dan kunci paling panjang 15 karakter. Walaupun prosesor ESP32 berkali-kali lebih cepat daripada Uno, OLED memakai bus I2C yang sama, jadi frame rate hampir tidak berubah; untuk game yang jauh lebih mulus dan berwarna, kamu bisa memakai layar TFT ILI9341 lewat SPI. Bluetooth ESP32, di sisi lain, adalah titik awal yang bagus untuk mengubah ponsel menjadi pengendali game.

Game dengan Raspberry Pi Pico dan MicroPython

Kalau kamu memprogram Raspberry Pi Pico dengan MicroPython, logika game-nya sama dan hanya sintaksnya yang berubah: time.ticks_ms() dan time.ticks_diff() sebagai ganti millis(), read_u16() yang mengembalikan 0–65535 sebagai ganti analogRead(), dan PWM sebagai ganti tone(). Pico tidak punya EEPROM, tetapi berkat sistem file MicroPython, menulis rekor ke file teks kecil di flash sangat mudah. Kode di bawah ini adalah versi Pico dari tes reaksi dan menyimpan rekornya di file rekor.txt; simpan ke board sebagai main.py di Thonny, dan program akan berjalan sendiri setiap kali board dinyalakan. Kalau kamu baru mengenal MicroPython, baca dulu Apa Itu 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)

Memakai ticks_diff() ketika mengukur waktu itu penting: penghitungnya berputar kembali ke awal setelah nilai tertentu, dan sekadar mengurangkan dua nilai memberi hasil yang salah pada momen itu; ticks_diff() menangani perputaran itu dengan benar. Untuk game OLED, driver ssd1306 yang kamu instal dari package manager Thonny menyediakan perintah fill_rect(), text(), dan show() yang mirip dengan Adafruit GFX. Modul neopixel yang kamu butuhkan untuk matrix NeoPixel sudah disertakan di MicroPython. Dengan begitu kamu bisa memindahkan Pong dan Snake ke Pico dengan logika yang sama.

Kesalahan umum dan cara mengatasinya

GejalaPenyebabSolusi
Layar kosongKalau Serial Monitor menampilkan “SSD1306 allocation failed”, RAM habis; kalau tidak, alamat atau pengkabelannya salahMasukkan teks tetap ke dalam F() dan perkecil array besar; kalau pesan itu tidak muncul, cek kabel SDA/SCL dan coba alamat 0x3D
Game selalu dimulai dengan cara yang samaTidak ada randomSeed() atau seed-nya selalu samaBeri seed dengan nilai micros() dari tekanan pertama pemain
Satu tekanan membuatnya melompat dua kaliPantulan tombolPakai fungsi buttonPressed() yang menunggu 15 ms sampai stabil
Skor tertinggi menampilkan 65535EEPROM yang belum pernah ditulis terbaca 0xFFAnggap 0xFFFF sebagai 0
Karakter bergeser ketika joystick dilepasTitik tengahnya bukan 512 dan zona matinya terlalu sempitPertahankan ambang 300/700 atau ukur titik tengah ketika start lalu kalibrasi
Ular melompat diagonalMatrix disambung zig-zagAtur SERPENTINE = true
Arduino reset ketika kecerahan dinaikkanLED matrix menarik arus terlalu besarTurunkan setBrightness() dan pakai catu daya 5 V eksternal
PWM di pin 3 atau 11 kacau ketika tone() berbunyiDi Uno, tone() memakai Timer2Pindahkan komponen yang butuh PWM ke pin 5, 6, 9, atau 10
Game makin lambat seiring berjalannya permainanSerial.print atau proses menggambar yang tidak perlu di setiap frameHapus output serial dan jaga waktu frame di atas waktu transfer layar
LCD hanya menampilkan kotak-kotakPengaturan kontras atau alamat salahPutar trimmer di belakang modul dan coba alamat 0x27 atau 0x3F
Kalau game tidak berperilaku seperti yang kamu harapkan, uji logikanya dulu tanpa layar: cetak posisi, kecepatan, dan state ke Serial Monitor di setiap frame, lalu ikuti beberapa frame secara manual. Begitu bug-nya ketemu, hapus baris-baris itu, karena Serial.print memakan beberapa milidetik per frame dan mengacaukan kecepatan game. Kalau kodenya sama sekali tidak mau ter-upload ke board, cek panduan kode Arduino gagal di-upload.

Ide untuk mengembangkan game-mu lebih jauh

  • Pong dua pemain: hapus baris-baris CPU, kendalikan pemukul kedua dengan potensiometer di A2, dan tarik kedua pengendali ke ujung meja yang berlawanan dengan kabel panjang.
  • Kendali kemiringan: pakai MPU6050 sebagai ganti joystick dan kendalikan ular atau pemukul dengan memiringkan board.
  • Musik dan efek suara: putar musik game dengan DFPlayer Mini; untuk efek pendek, buzzer pasif sudah cukup.
  • Main dari ponselmu: ubah ponselmu menjadi pengendali nirkabel dengan modul Bluetooth HC-05.
  • Gamepad untuk komputermu: karena Arduino Leonardo bisa bertindak sebagai keyboard USB, kamu bisa membuat pengendali untuk game PC dengan joystick dan tombol.
  • Papan skor: tampilkan skor tertinggi sebagai teks berjalan di modul MAX7219 4-in-1.
  • Game peluang: buat roda roulette dengan ring WS2812B atau dadu elektronik dengan LED; cara paling seru untuk berlatih random().

Membuat game dengan Arduino adalah cara paling menyenangkan untuk belajar kode dan hardware sekaligus. Begitu kamu sudah memahami game loop, deteksi tabrakan, dan skor tertinggi di EEPROM, merancang game sendiri hanya soal punya ide; untuk inspirasi lebih banyak, lihat daftar ide proyek Arduino. Di aplikasi BlueGrays kamu akan menemukan Pong, T-Rex di OLED, Snake di matrix 8x8 dan 16x16, Simon, runner LCD, dan tes reaksi sebagai proyek langkah demi langkah lengkap dengan skema rangkaian dan kode siap pakai.

Pertanyaan umum

Game apa saja yang bisa dibuat dengan Arduino?

Dengan Arduino Uno kamu bisa membuat Pong, T-Rex runner, game ala Flappy Bird, Snake, game balok sederhana, game memori Simon, tes reaksi, dan game peluang seperti dadu atau roulette. OLED 128×64 sudah cukup untuk game bergrafis, dan LED matrix NeoPixel untuk game grid yang berwarna-warni. Game seperti Simon dan tes reaksi dimainkan hanya dengan LED dan tombol, tanpa layar sama sekali.

Layar apa yang paling bagus untuk game Arduino?

Untuk pemula, pilihan terbaik adalah OLED SSD1306 0,96 inci: murah, tersambung hanya dengan dua kabel data, dan bisa menggambar grafik bergerak dengan resolusi 128×64 piksel. Kalau kamu ingin gambar berwarna yang lebih besar, layar TFT ILI9341 atau ST7789 cocok, dan untuk game pixel art, LED matrix 8x8 atau 16x16. LCD 16x2 dengan karakter kustom juga cukup untuk game runner sederhana.

Bagaimana cara menyimpan skor tertinggi di Arduino?

Kalau skor tertinggi ditulis ke EEPROM, skor itu tidak hilang ketika daya mati. Kamu menulisnya dengan EEPROM.put(address, score) dan membacanya dengan EEPROM.get(address, score); Arduino Uno punya 1024 byte untuk ini. Karena setiap sel bisa ditulis sekitar 100.000 kali, tulis hanya ketika game selesai dan rekornya terpecahkan.

Bisakah membuat gamepad dengan Arduino?

Bisa. Board dengan prosesor ATmega32U4, seperti Arduino Leonardo, Micro, atau Pro Micro, bisa dikenali komputer sebagai keyboard, mouse, atau pengendali game USB; board ini membaca joystick dan tombol lalu mengirim tekanan tombol keyboard. Arduino Uno tidak bisa melakukannya secara langsung, tetapi bisa mengirim nilai joystick lewat port serial ke program di komputer.

Kenapa game Arduino-ku lambat atau patah-patah?

Penyebab paling umum adalah loop yang dibuat dengan delay(), pemanggilan Serial.print di setiap frame, dan waktu transfer layar. Di Uno, mengirim seluruh layar OLED butuh sekitar 25 ms, jadi waktu frame 30 ms itu realistis. Pakai waktu frame tetap dengan millis() alih-alih delay(), hapus output serial, dan gambar hanya yang perlu.

Game Arduino apa yang paling mudah untuk pemula?

Tes reaksi paling mudah: cukup satu LED, satu tombol, dan sketch pendek, tetapi game ini mengajarkan ide inti seperti pewaktuan, bilangan acak, dan EEPROM. Setelah itu, lanjut ke Simon dengan empat LED dan empat tombol, lalu ke Pong dan T-Rex di layar OLED, adalah jalur belajar yang bagus.

Lihat semua proyek
Game Bola Pemukul - SSD1306
#86Menengah
Premium

Game Bola Pemukul - SSD1306

Satu bola, dua pemukul, dan kamu: kamu bermain tenis meja, salah satu video game paling awal, di layar OLED kecil. Dua tombol menggerakkan pemukulmu di kanan ke atas dan ke bawah, sementara komputer m

4.5(47)
65
OLED T-Rex Run: Game Dinosaurus Arduino
#87Mahir
Premium

OLED T-Rex Run: Game Dinosaurus Arduino

Game dinosaurus yang muncul ketika internetmu putus, sekarang ada di mejamu: dinosaurus berlari di layar OLED kecil, kaktus datang menghampiri, dan kamu melompat dengan satu tombol. Skormu naik secara

4.7(33)
53
Ular Piksel Matrix 8x8
#88Mahir
Premium

Ular Piksel Matrix 8x8

Buat sendiri dan mainkan game ular klasik. Di matrix lampu 8x8 yang berwarna-warni, kamu mengarahkan ular hijau dengan joystick dan memakan apel merah; di setiap apel ular bertambah panjang, sedikit l

4.7(39)
57
Ular Matrix 16x16
#89Mahir
Premium

Ular Matrix 16x16

Game ular klasik, kali ini di layar LED raksasa dari 256 lampu. Kamu mengarahkan ular hijau dengan joystick dan mengumpulkan apel merah; di setiap apel ular bertambah panjang, skormu naik di layar ang

4.8(52)
67
Game Memori Arduino (4 LED + 4 Tombol + Buzzer)
#93Mahir
Premium

Game Memori Arduino (4 LED + 4 Tombol + Buzzer)

Percaya dengan ingatanmu? Empat LED berwarna menyala berurutan, masing-masing memainkan nadanya sendiri, dan kamu mengulang urutan yang sama dengan menekan tombol. Setiap ronde yang benar menambahkan

4.8(46)
69
Game Lari LCD Arduino
#98Menengah
Premium

Game Lari LCD Arduino

Game lari tanpa akhir yang bergulir ke samping di layar LCD karakter 16x2. Sang jagoan berlari otomatis, dan pemain melompati rintangan dengan menekan tombol. Rintangan acak muncul di baris atas dan bawah. Buzzer pasif memainkan melodi pembuka, suara lompat, suara perayaan 50 poin, dan melodi akhir game. Layar pembuka "BlueGrays - Arduino Projects" tampil ketika mulai. Skor langsung diperbarui di pojok kanan atas layar.

4.6(32)
55
Tes Waktu Reaksi Arduino dengan Isyarat Suara: Ukur Refleksmu dalam Milidetik (LCD + Buzzer + 3 LED)
#97Menengah
Premium

Tes Waktu Reaksi Arduino dengan Isyarat Suara: Ukur Refleksmu dalam Milidetik (LCD + Buzzer + 3 LED)

Seberapa cepat refleksmu? Tekan tombol: LED merah berkata bersiap, LED kuning berkata siap, dan jeda acak 1,5 sampai 3,5 detik dimulai. Begitu LED hijau menyala dan buzzer berbunyi bip, tekan lagi; waktu reaksimu muncul di LCD I2C dalam milidetik, dengan rekor terbaik dan rata-rata sesimu di bawahnya. Tekan sebelum hijau dan layar menampilkan TOO EARLY! dengan suara kalah. Tahan tombol untuk pemain baru, agar semua orang beradu dengan urutan jeda yang sama.

4.6(33)
50