Rehberler · 14 dk okuma

Arduino Oyun Yapımı: Pong, T-Rex, Yılan ve Simon Oyunu Kodları

Arduino oyun yapımı adım adım: OLED'de Pong ve T-Rex, LED matriste yılan, Simon, LCD koşucu ve reaksiyon testi; oyun döngüsü, çarpışma ve EEPROM rekoru.

Arduino oyun yapımı, elektronik ve programlama öğrenmenin en eğlenceli yoludur: bir OLED ekran, birkaç buton ve bir joystick ile Pong, T-Rex koşucu, yılan ve Simon hafıza oyunu gibi klasikleri kendi ellerinle yapabilir, arkadaşlarınla rekor yarışına girebilirsin. Bir oyun yazarken programlamanın en önemli konularını da kendiliğinden öğrenirsin: zamanlama, durum makinesi, koordinat sistemi, çarpışma hesabı, rastgele sayı üretimi ve kalıcı bellek. Bu oyunların hepsi tek bir Arduino Uno'da çalışır; 2 KB RAM yeter, yeter ki belleği akıllıca kullan.

Bu rehberde altı oyunu baştan sona yapacağız: OLED ekranda Pong ve T-Rex koşucu, 8x8 ve 16x16 NeoPixel LED matriste yılan, dört LED ve dört butonla Simon, 16x2 LCD'de koşucu oyunu ve milisaniye hassasiyetinde reaksiyon testi. Her oyun için parça listesi, bağlantı ve derlenebilir kod var. Hepsinin ortak iskeletini, yani oyun döngüsünü, çarpışma kontrolünü, puanı ve EEPROM'da rekor saklamayı önce bir kez öğrenip sonra her oyunda yeniden kullanacağız. Yazının sonunda ESP32 ve Raspberry Pi Pico uyarlamalarını da bulacaksın.

8x8 NeoPixel LED matris ile Arduino yılan oyunu
8x8 WS2812B NeoPixel LED matris: 64 renkli piksel ve tek veri kablosu; yılan oyunu için ideal bir mini ekran.

Hangi oyunu hangi parçalarla yaparsın?

Arduino ile oyun yaparken ilk karar ekrandır, çünkü ekran oyunun türünü belirler. Yalnızca birkaç LED'le bile Simon ya da reaksiyon testi gibi oyunlar yapılabilir; 128×64 piksellik bir OLED ise hareketli grafik ve fizik isteyen Pong ile T-Rex'i mümkün kılar. Aşağıdaki tablo bu rehberdeki oyunları kolaydan zora doğru özetler:

OyunEkranGirişDiğer parçalarZorluk
Reaksiyon testi1 LED + Seri Monitör1 buton220 Ω direnç, isteğe bağlı buzzerKolay
Simon hafıza oyunu4 renkli LED4 buton4 adet 220 Ω direnç, pasif buzzerKolay–orta
LCD koşucu16x2 I2C LCD1 butonPasif buzzerOrta
Pong0,96" SSD1306 OLEDJoystick ya da 10 kΩ potansiyometrePasif buzzerOrta
T-Rex koşucu0,96" SSD1306 OLED1 buton ya da joystick butonuPasif buzzerOrta
Yılan (8x8)8x8 NeoPixel matrisJoystick330 Ω direnç, 1000 µF kondansatörOrta–ileri
Yılan (16x16)16x16 NeoPixel matrisJoystick5 V 3–4 A harici adaptörİleri
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

Ortak bağlantı şeması: tek devrede oyun konsolu

Simon dışındaki oyunları aynı devrede çalıştırabilmen için pinleri ortak seçtik. OLED ve I2C LCD aynı iki kabloyu (SDA ve SCL) paylaşır; joystick analog girişlere, aksiyon butonu D3'e, buzzer D8'e bağlanır. Böylece bir kez kurduğun bu "oyun konsoluna" yalnızca farklı bir kod yükleyerek oyun değiştirebilirsin. Kartının tam pin haritasını Pinout aracında görebilirsin.

Parça / pinArduino UnoESP32Raspberry Pi Pico
OLED ya da I2C LCD: SDAA4GPIO 21GP4
OLED ya da I2C LCD: SCLA5GPIO 22GP5
Joystick VRx (X ekseni)A0GPIO 34GP26
Joystick VRy (Y ekseni)A1GPIO 35GP27
Joystick SW (buton)D2GPIO 25GP15
Aksiyon butonu (diğer ucu GND)D3GPIO 26GP14
Pasif buzzer (+)D8GPIO 27GP16
NeoPixel DIN (330 Ω üzerinden)D6GPIO 13GP22
ESP32 ve Pico'da joystick'i ve OLED'i 3,3 V ile besle; 5 V ile beslenen bir joystick analog pinlere 3,3 V'un üzerinde gerilim verir ve kartı bozabilir. 5 V isteyen I2C LCD modülünü ESP32'ye bağlarken araya bir seviye dönüştürücü koy. Butonların hepsi dahili pull-up direnciyle çalışır: bir ucu pine, diğer ucu GND'ye gider ve basınca pin LOW okunur.

Oyun döngüsü (game loop) nasıl çalışır?

Her video oyunu, saniyede onlarca kez tekrarlanan dört adımlık bir döngüden oluşur: girişi oku (buton, joystick), dünyayı güncelle (konum, hız, puan), çarpışmaları kontrol et ve ekrana çiz. Döngünün bir turuna kare (frame) denir. Saniyede 30 kare (30 FPS) akıcı görünmeye yeter; bu da her karenin yaklaşık 33 ms sürmesi demektir. Arduino'nun loop() fonksiyonu zaten bir döngüdür ama çok hızlı ve düzensiz döner; bu yüzden kareleri millis() ile sabit aralıklara bölmek gerekir.

Burada delay() kullanmamak çok önemlidir. delay(33) yazarsan Arduino o 33 ms boyunca hiçbir şey yapmaz, kısa bir buton basışını kaçırabilir ve çizim süresi değiştikçe oyunun hızı da değişir. millis() ile ise loop() her turda girişi okur ama oyunu yalnızca kare zamanı geldiğinde ilerletir. İki yöntemin farkını Arduino millis ve delay farkı yazısında ayrıntılı bulabilirsin. Aşağıdaki iskelet, bu rehberdeki bütün oyunların temelidir:

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}

Koddaki ikinci önemli fikir durum makinesidir. Oyun her an üç durumdan birindedir: MENU, PLAYING ve GAME_OVER. Her durum aynı butona farklı tepki verir: menüde basmak oyunu başlatır, oyun sırasında karakteri zıplatır, oyun bitince yeni turu açar. Kodu böyle bölmek iç içe geçmiş if bloklarını ortadan kaldırır ve yeni bir durum (örneğin PAUSE) eklemeyi kolaylaştırır. Konuyu Arduino durum makinesi yazısında daha geniş ele alıyoruz.

Buton ve joystick ile giriş okuma

Butonları INPUT_PULLUP ile bağlamak en temiz yöntemdir: harici direnç gerekmez, pin boştayken HIGH, basınca LOW okunur. Ancak mekanik bir buton basıldığı ya da bırakıldığı anda birkaç milisaniye boyunca titreşir (bounce) ve Arduino bunu art arda birkaç basış olarak görür. Oyunda bu, bir basışta iki kez zıplamak ya da Simon'da yanlış renk sayılmak demektir. İskeletteki buttonPressed() fonksiyonu, pin 15 ms boyunca aynı kalmadan yeni durumu kabul etmez ve yalnızca basıldığı anda bir kez true döndürür. Devre şeması ve diğer yöntemler için Arduino'ya buton nasıl bağlanır yazısına göz at.

Joystick ise iki potansiyometre ve bir butondan oluşur. Uno'da her eksen 0–1023 arası değer verir ve kol serbestken orta değer yaklaşık 512'dir, ama bu değer modülden modüle 490–530 arasında değişebilir. Bu yüzden ızgaralı oyunlarda kesin değer yerine eşik kullanırız: 300'ün altı sol ya da yukarı, 700'ün üstü sağ ya da aşağı demektir. Aradaki geniş bölge (ölü bölge), kol bırakıldığında karakterin kendi kendine kaymasını engeller. Pong'da ise analog değeri doğrudan rakete eşleriz: map() fonksiyonu 0–1023 aralığını raketin 0–48 piksellik yoluna çevirir. Kalibrasyon ve yön tablosu joystick modülü yazısında.

Çarpışma kontrolü nasıl yapılır?

Çarpışma kontrolü, iki nesnenin aynı anda aynı yerde olup olmadığını sorar. Ekranlı oyunlarda en yaygın yöntem AABB (eksene hizalı sınırlayıcı kutu) testidir: her nesneyi bir dikdörtgen olarak düşünürsün. İki dikdörtgen yalnızca şu dört koşul birlikte doğruysa çakışır: A'nın sol kenarı B'nin sağ kenarından solda, A'nın sağ kenarı B'nin sol kenarından sağda ve aynı iki koşul dikey eksende de geçerli. Kodda bu tek satırdır: ax < bx + bw && ax + aw > bx && ay < by + bh && ay + ah > by. Izgaralı oyunlarda (yılan, LCD koşucu) iş daha da kolaydır: iki hücrenin koordinatları eşitse çarpışma vardır.

OyunNe neyle çarpışır?Yöntem
PongTop ile duvarlar ve raketlerAABB + hız yönünü ters çevirme
T-RexDinozor ile kaktüslerAABB (biraz küçültülmüş kutuyla)
YılanBaş ile duvar, gövde ve yemIzgara hücresi eşitliği
LCD koşucuKoşucu ile kaktüsAynı sütun ve aynı satır
SimonBasılan renk ile dizideki renkDizi elemanı karşılaştırma
Reaksiyon testiBasış anı ile işaret anıZaman karşılaştırma (erken basış)
Hızlı hareket eden küçük nesneler bir karede engelin içinden geçip çarpışmayı atlayabilir; buna tunneling denir. Pong'da top bir karede 4 pikselden fazla ilerlerse 3 piksellik raketin kontrol bölgesini hiç değmeden geçer. Çözüm, nesnenin kare başına hızını kontrol bölgesinin genişliğinden küçük tutmaktır; Pong kodundaki MAX_SPEED = 2.8 sınırı tam olarak bunu yapar.

Puan ve EEPROM'da rekor saklama

Puan RAM'deki bir değişkende tutulur ve Arduino'nun fişini çektiğin anda silinir. Rekorun kalıcı olması için EEPROM kullanılır: Arduino Uno'nun ATmega328P işlemcisinde elektrik kesilince silinmeyen 1024 baytlık bir EEPROM vardır. Bir rekor için uint16_t türünde 2 bayt yeter (0–65535). EEPROM'un tek zayıf noktası yazma ömrüdür: her hücre yaklaşık 100.000 kez yazılabilir. Bu çok gibi görünse de rekoru her karede, yani saniyede 30 kez yazarsan hücre yaklaşık 55 dakikada yıpranır. Doğru yöntem yalnızca oyun bittiğinde ve rekor kırıldıysa yazmaktır; günde 10 rekor kırsan bile hücre 27 yıl dayanır.

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

Kodun iki ince noktası var. Birincisi, hiç yazılmamış bir EEPROM'un her baytı 0xFF'tir; bu yüzden ilk açılışta rekor 65535 görünür ve 0xFFFF kontrolü bunu 0'a çevirir. Reaksiyon testinde ise tersine, 0xFFFF "henüz kayıt yok" anlamına gelir çünkü orada kısa süre daha iyidir. İkincisi, EEPROM.put() yazmadan önce her baytı okur ve yalnızca değişen baytları yazar; bu da ömrü ayrıca uzatır. Diğer EEPROM komutları için Arduino EEPROM kullanımı yazısına bakabilirsin. Bu rehberdeki oyunları aynı Uno'ya sırayla yükleyebilmen için her oyuna ayrı bir adres verdik:

OyunEEPROM adresiSaklanan değer
T-Rex koşucu0–1En yüksek puan
Yılan2–3En çok yenen yem
LCD koşucu4–5En yüksek puan
Reaksiyon testi6–7En kısa süre (ms)
Simon8–9Ulaşılan en yüksek seviye

Pong oyunu: OLED ekran ve joystick

Pong, 1972'de çıkan ve video oyunu çağını başlatan masa tenisi oyunudur. Bizim sürümümüzde sol raketi joystick'in Y ekseniyle (ya da A1'e bağlı bir potansiyometreyle) sen yönetirsin, sağ raketi ise Arduino oynar. Gereken parçalar: Arduino Uno, 0,96 inç SSD1306 I2C OLED, joystick ve pasif buzzer. Kütüphane Yöneticisi'nden Adafruit SSD1306 kütüphanesini kurarken bağımlılığı olan Adafruit GFX kütüphanesini de kur. Ekranın bağlantısı ve çizim komutları OLED ekran SSD1306 yazısında ayrıntılı anlatılıyor.

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}

Pong'u eğlenceli yapan şey hitPaddle() fonksiyonudur. Top raketin ortasına çarparsa neredeyse düz döner, kenarına çarparsa sert bir açıyla sekip rakibi zorlar. Bunu, topun raketin merkezine olan uzaklığını -1 ile +1 arasına ölçekleyip dikey hıza çevirerek yaparız. Her vuruşta top %5 hızlanır ve MAX_SPEED ile sınırlanır. Arduino'nun raketi kare başına en fazla 1,2 piksel hareket edebilir; topa sert bir açı verirsen yetişemez ve sayıyı alırsın. Oyunu zorlaştırmak için bu değeri 1,5'e çıkar; iki kişilik oynamak için CPU satırlarını sil ve ikinci raketi A2'ye bağlı bir potansiyometreyle yönet.

Kare süresi neden 30 ms? Uno, 1024 baytlık görüntü tamponunu OLED'e 400 kHz I2C hızında yaklaşık 25 ms'de gönderir; bu yüzden ekran saniyede 35–40 kareden fazlasını gösteremez. Kare süresini bu gönderim süresinin biraz üstünde tutmak oyunun hızını sabitler. Derleyicinin "Global variables use 28%" raporu da seni yanıltmasın: SSD1306 tamponu çalışma anında ayrılır ve gerçek RAM kullanımı yaklaşık %78'e çıkar. Bellek yetmezse SSD1306 allocation failed hatasını görürsün; çözümleri Arduino bellek yetersiz yazısında topladık.

T-Rex koşucu: zıplama fiziği ve rekor

Chrome tarayıcısının internet yokken açtığı dinozor oyununu OLED ekrana taşıyoruz. Dinozor yerinde koşar, kaktüsler sağdan sola akar ve sen butona basarak üzerlerinden atlarsın. Zıplama gerçekçi görünsün diye basit bir fizik kullanırız: butona basınca dikey hız -4,2 piksel/kare olur, her karede yer çekimi bu hıza 0,3 ekler ve konum hız kadar değişir. Dinozor böylece yavaşlayarak yükselir, tepede bir an durur ve hızlanarak düşer; yaklaşık 27 piksel yükselir ve 27 kare, yani yaklaşık 0,8 saniye havada kalır. Parçalar Pong ile aynıdır; zıplamak için D3'teki butonu ya da joystick butonunu kullanırsın.

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}
AyarDeğerEtkisi
JUMP_V-4.2Zıplama gücü; -5 yaparsan dinozor yaklaşık 40 piksel yükselip ekranın tepesine yaklaşır
GRAVITY0.3Büyütürsen zıplama kısalır ve sertleşir
speed2,0 → 5,0Kaktüslerin hızı; her 200 karede 0,25 artar
random(70, 140)pikselİki kaktüs arasındaki mesafe; küçültmek oyunu zorlaştırır
Çarpışma kutusu10×12 pikselÇizilen 14×14 dinozordan küçük; kıl payı kaçışlar ölüm sayılmaz

Rekor, oyun bittiği anda ve yalnızca kırıldıysa EEPROM'a yazılır ve sağ üst köşede HI olarak görünür. Kartı açarken D3 butonunu basılı tutarsan rekor sıfırlanır; arkadaşlarınla yeni bir turnuvaya başlarken işine yarar. Kaktüslerin boyu ve genişliği random() ile belirlenir. Oyunun her açılışta farklı başlaması için randomSeed(analogRead(A3)) satırı, hiçbir yere bağlı olmayan bir analog pinin gürültüsünü tohum olarak kullanır.

LED matriste yılan oyunu (8x8 ve 16x16)

Eski Nokia telefonlarla ünlenen yılan oyunu için en uygun ekran bir LED matristir, çünkü her LED bir oyun hücresidir. Biz renkli WS2812B NeoPixel matris kullanıyoruz: 8x8 sürüm 64, 16x16 sürüm 256 piksel içerir ve hepsi tek bir veri kablosuyla sürülür. Yılanın gövdesi iki dizide (snakeX ve snakeY) tutulur ve 0 numaralı eleman baştır. Her adımda gövde bir hücre kaydırılır ve başa yeni konum yazılır; yem yenirse kuyruk silinmez, böylece yılan bir hücre uzar. Kodu derlemek için Adafruit NeoPixel kütüphanesini kur; bağlantı ayrıntıları WS2812B NeoPixel yazısında.

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}

Kodda üç kritik kural var. Geri dönüş yasağı: sağa giden yılan birden sola dönerse kendi boynuna çarpar; readJoystick() yeni yönü bekleyen yöne göre değil, yılanın şu anki yönüne göre kontrol eder, böylece iki hızlı hareketle bile ters dönülemez. Kuyruk istisnası: yem yenmeyen adımda kuyruğun ucu boşalacağı için başın oraya girmesi çarpışma sayılmaz. Yem yerleştirme: placeFood() yem yılanın üstüne düşmeyene kadar yeniden dener. Her yemde adım süresi 10 ms kısalır (300 ms'den 120 ms'ye); oyun bitince yediğin yem sayısı kadar LED mavi yanar ve rekor EEPROM'a yazılır.

Matrisindeki LED'lerin nasıl dizildiğini bilmek de önemlidir. Sert 8x8 kartların çoğunda LED'ler satır satır aynı yönde ilerler (SERPENTINE = false). Esnek 16x16 panellerde ise genellikle zikzak dizilirler: bir satır soldan sağa, sonraki sağdan sola gider. Yılan çapraz atlıyor ya da bir satırda ters gidiyorsa SERPENTINE değerini true yap; yönler ters çıkarsa joystick'i çevir ya da 300 ile 700 eşiklerinin yerini değiştir. 16x16 için W ve H değerlerini 16 yapman yeterlidir; bu durumda yılan dizileri 512, NeoPixel tamponu 768 bayt tutar ve Uno'nun RAM'inin yaklaşık %75'i dolar.

16x16 NeoPixel LED matris
16x16 NeoPixel matris: 256 piksellik oyun alanı daha uzun bir yılan ve daha zorlu oyun demek; harici 5 V besleme ister.
Tek bir WS2812B tam beyazda yaklaşık 60 mA çeker; 16x16 matrisin 256 LED'i en kötü durumda 15 A'e çıkar ve ne USB ne de Arduino'nun 5V pini bunu karşılayabilir. Matrisi harici bir 5 V 3–4 A adaptörle besle, adaptörün GND'sini Arduino'nun GND'sine bağla, veri hattına 330 Ω direnç ve besleme girişine 1000 µF kondansatör koy. Koddaki setBrightness(30) parlaklığı yaklaşık %12'ye indirir; yılan oyununda yanan LED sayısı az olduğu için akım birkaç yüz mA'de kalır. Daha az RAM harcayan tek renkli bir seçenek için MAX7219 LED matris yazısına bak.

Simon hafıza oyunu: 4 LED ve 4 buton

Simon, 1978'de çıkan efsanevi bir hafıza oyunudur: cihaz her turda renk dizisine bir renk ekler ve sen diziyi aynı sırayla tekrarlarsın. Gereken parçalar: dört renkli LED (yeşil, kırmızı, sarı, mavi), her biri için 220 Ω direnç, dört buton ve bir pasif buzzer. LED'leri D9–D12'ye, butonları D2–D5'e, buzzer'ı D8'e bağla; bu oyun ortak konsoldan ayrı bir devredir. LED direncini kendi LED'ine göre LED direnci hesaplama aracıyla seçebilirsin. Orijinal Simon'daki dört ses A majör akorunun notalarından oluşur; kodda da aynı notaları (330, 440, 554 ve 659 Hz) kullandık, bu yüzden hızlı basışlarda bile kulağa hoş gelir.

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}

Simon'da rastgelelik her şeydir: dizi her oyunda aynı olursa oyuncu ezberler. Boştaki bir pini okuyan randomSeed(analogRead(A0)) yöntemi pratikte zayıftır, çünkü pin çoğu zaman birbirine yakın değerler okur. Bu kodda daha sağlam bir yöntem kullanıyoruz: oyun, oyuncunun ilk basışını bekler ve o anın micros() değerini tohum yapar. Hiç kimse basış anını mikrosaniye hassasiyetinde tekrarlayamayacağı için her oyun farklı başlar. Gösterim süresi her seviyede 20 ms kısalır ve 180 ms'de sabitlenir; 3 saniye içinde basmazsan ya da yanlış renge basarsan oyun biter ve ulaştığın seviye rekorsa EEPROM'a yazılır.

LCD koşucu oyunu: 16x2 ekranda özel karakter

16x2 karakter LCD'de piksel çizemezsin ama createChar() ile 5×8 piksellik en fazla 8 özel karakter tanımlayabilirsin. Koşucu oyununda üç karakter yeter: koşucunun iki adım pozu ve bir kaktüs. Ekranın alt satırı zemindir; kaktüsler sağdan sola kayar, butona basınca koşucu üst satıra çıkar ve birkaç adım boyunca havada kalır. Bu oyun ızgaralı olduğu için çarpışma kontrolü tek satırdır: koşucu yerdeyken onun sütununda kaktüs varsa oyun biter. Kütüphane ve bağlantı için I2C LCD ekran yazısına bakabilirsin.

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}

Dünyanın kayması, ground dizisindeki her elemanın bir sola kopyalanmasıdır; en sağdaki sütuna ise yüzde 35 olasılıkla yeni bir kaktüs gelir. MIN_GAP = 4 kuralı iki kaktüs arasında en az dört boş sütun bırakır ve böylece her engel aşılabilir olur. Her 25 puanda adım süresi 15 ms kısalır ve 300 ms'den 120 ms'ye kadar iner. Ekranı her adımda lcd.clear() ile silmek yerine iki satırı baştan yazmak titremeyi önler; clear() komutu tek başına yaklaşık 2 ms sürer. Ekranda yalnızca kareler görüyorsan modülün arkasındaki kontrast ayarını çevir, hiçbir şey görmüyorsan adresi 0x27 yerine 0x3F yap.

Reaksiyon testi: tepki süreni milisaniyeyle ölç

En basit ama en bağımlılık yapan oyun budur: LED rastgele bir anda yanar, sen olabildiğince hızlı butona basarsın ve Arduino geçen süreyi milisaniye cinsinden Seri Monitör'e yazar. D7'ye 220 Ω dirençle bağlanmış bir LED, D3'teki buton ve isteğe bağlı buzzer yeter. Bekleme süresi 2 ile 5 saniye arasında rastgeledir; bekleme sırasında basarsan "erken bastın" uyarısı alırsın, yani tahminle hile yapılamaz. Süre micros() ile ölçülür, ortalaman hesaplanır ve en iyi süren EEPROM'da saklanır.

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}

İnsanların ışığa ortalama tepki süresi yaklaşık 200–250 ms'dir; iyi odaklanmış bir oyuncu bunun biraz altına inebilir. USE_SOUND değerini true yaparsan LED'le birlikte bip sesi de çalar. Sese tepki genellikle ışığa göre biraz daha hızlıdır, bu yüzden iki modun rekorlarını karşılaştırmak ilginç bir deney olur. Süreyi Seri Monitör yerine bir 7 segment display ya da OLED ekranda gösterirsen bilgisayarsız, pille çalışan bir cihaz elde edersin.

ESP32 ile oyun yapımı

Bu rehberdeki kodların çoğu ESP32'de küçük değişikliklerle çalışır: Adafruit SSD1306 ve NeoPixel kütüphaneleri ESP32'yi destekler, tone() da ESP32 Arduino çekirdeğinin 2.x ve 3.x sürümlerinde vardır. Değişmesi gereken üç şey var: pinler (ortak tablodaki ESP32 sütunu), joystick eşikleri ve rekor saklama. ESP32'nin ADC'si 12 bittir ve 0–4095 arası değer verir; bu yüzden 300 ve 700 yerine yaklaşık 1200 ve 2800 kullan. Joystick'i GPIO 32–39 arasındaki ADC1 pinlerine bağla, çünkü ADC2 pinleri Wi-Fi açıkken okunamaz.

ESP32'de gerçek bir EEPROM yoktur; EEPROM kütüphanesi flash belleği taklit eder ve EEPROM.begin() ile EEPROM.commit() çağrıları ister. Daha temiz yol Preferences kütüphanesidir: değerleri bir ad alanı (namespace) ve anahtar adıyla flash'taki NVS bölümüne yazar ve aşınmayı kendisi dağıtır. Aşağıdaki "tıklama yarışı" oyunu, 5 saniyede butona kaç kez basabildiğini sayar ve rekoru Preferences ile saklar:

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) kayıt yoksa ikinci parametreyi döndürür; böylece EEPROM'daki 0xFFFF sorunuyla hiç uğraşmazsın. Ad alanı ve anahtar adları en fazla 15 karakter olabilir. ESP32'nin işlemcisi Uno'dan kat kat hızlı olsa da OLED aynı I2C hattını kullandığı için kare hızı pek değişmez; çok daha akıcı ve renkli oyunlar için SPI bağlantılı bir ILI9341 TFT ekran kullanabilirsin. ESP32'nin Bluetooth'u ise telefonu oyun kumandasına çevirmek için iyi bir başlangıç noktasıdır.

Raspberry Pi Pico ve MicroPython ile oyun

Raspberry Pi Pico'yu MicroPython ile programlıyorsan oyun mantığı aynıdır, yalnızca söz dizimi değişir: millis() yerine time.ticks_ms() ve time.ticks_diff(), analogRead() yerine 0–65535 arası değer veren read_u16(), tone() yerine de PWM kullanılır. Pico'da EEPROM yoktur, ama MicroPython'un dosya sistemi sayesinde rekoru flash'ta küçük bir metin dosyasına yazmak çok kolaydır. Aşağıdaki kod reaksiyon testinin Pico sürümüdür ve rekoru rekor.txt dosyasında saklar; Thonny ile karta main.py adıyla kaydedersen her açılışta kendiliğinden başlar. MicroPython'a yeniysen önce Raspberry Pi Pico nedir yazısına göz at.

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)

Zaman ölçerken ticks_diff() kullanmak önemlidir: sayaçlar belli bir değerden sonra başa döner ve iki değeri doğrudan çıkarmak o anda yanlış sonuç verir; ticks_diff() bu taşmayı doğru hesaplar. OLED'li oyunlar için Thonny'nin paket yöneticisinden kurulan ssd1306 sürücüsü, Adafruit GFX'e benzeyen fill_rect(), text() ve show() komutları sunar. NeoPixel matris için gereken neopixel modülü ise MicroPython ile birlikte gelir. Böylece Pong'u ve yılanı da aynı mantıkla Pico'ya taşıyabilirsin.

Sık yapılan hatalar ve çözümleri

BelirtiSebepÇözüm
Ekran boş kalıyorSeri Monitör'de "SSD1306 allocation failed" yazıyorsa RAM yetmiyor; yazmıyorsa adres ya da kablolama yanlışSabit metinleri F() içine al, büyük dizileri küçült; mesaj yoksa SDA/SCL kablolarını kontrol et ve adresi 0x3D dene
Oyun her açılışta aynı başlıyorrandomSeed() yok ya da tohum hep aynıOyuncunun ilk basış anındaki micros() değeriyle tohumla
Bir basışta iki kez zıplıyorButon sıçraması (bounce)15 ms kararlılık bekleyen buttonPressed() fonksiyonunu kullan
Rekor 65535 görünüyorHiç yazılmamış EEPROM 0xFF okunur0xFFFF değerini 0 kabul et
Joystick bırakılınca karakter kayıyorOrta değer 512 değil, ölü bölge dar300/700 eşiklerini koru ya da açılışta orta değeri ölçüp kalibre et
Yılan çapraz atlıyorMatris zikzak dizilimliSERPENTINE = true yap
Parlaklık artınca Arduino resetleniyorLED matris fazla akım çekiyorsetBrightness() değerini düşür, harici 5 V adaptör kullan
tone() çalınca 3 ya da 11 numaralı pindeki PWM bozuluyorUno'da tone() Timer2'yi kullanırPWM gereken parçayı 5, 6, 9 ya da 10 numaralı pine al
Oyun ilerledikçe yavaşlıyorHer karede Serial.print ya da gereksiz çizimSeri çıktıyı kaldır, kare süresini ekranın gönderim süresinin üstünde tut
LCD'de yalnızca kareler varKontrast ayarı ya da yanlış adresArkadaki potu çevir, adresi 0x27 ya da 0x3F olarak dene
Bir oyun beklediğin gibi davranmıyorsa önce mantığı ekransız test et: konumu, hızı ve durumu her karede Seri Monitör'e yazdır ve birkaç kareyi elle takip et. Hatayı bulunca bu satırları sil, çünkü Serial.print her karede birkaç milisaniye harcar ve oyunun hızını bozar. Kod karta hiç yüklenmiyorsa Arduino kod yüklenmiyor rehberine bak.

Oyunlarını geliştirme fikirleri

  • İki kişilik Pong: CPU satırlarını sil, ikinci raketi A2'deki bir potansiyometreyle yönet ve iki kumandayı uzun kablolarla masanın iki ucuna taşı.
  • Eğim kontrolü: Joystick yerine bir MPU6050 kullanıp kartı eğerek yılanı ya da raketi yönet.
  • Müzik ve efektler: Bir DFPlayer Mini ile oyun müziği çal; kısa efektler için pasif buzzer yeterlidir.
  • Telefonla oynama: HC-05 Bluetooth modülüyle telefonu kablosuz bir kumandaya çevir.
  • Bilgisayar için oyun kolu: Arduino Leonardo USB klavye gibi davranabildiği için joystick ve butonlarla bilgisayar oyunlarına kumanda yapabilirsin.
  • Skor tabelası: Rekorları 4'lü MAX7219 modülünde kayan yazıyla göster.
  • Şans oyunları: WS2812B halkayla bir rulet çarkı ya da LED'lerle elektronik zar yap; random() kullanımını pekiştirmenin en eğlenceli yolu.

Arduino oyun yapımı, kodla donanımı aynı anda öğrenmenin en keyifli yoludur. Oyun döngüsünü, çarpışma kontrolünü ve EEPROM rekorunu bir kez kavradığında kendi oyununu tasarlamak yalnızca bir fikir meselesine dönüşür; daha fazla ilham için Arduino proje fikirleri listesine göz atabilirsin. BlueGrays uygulamasında Pong, OLED T-Rex, 8x8 ve 16x16 matris yılan, Simon, LCD koşucu ve reaksiyon testi projelerini adım adım devre şemaları ve hazır kodlarıyla bulabilirsin.

Sıkça sorulan sorular

Arduino ile hangi oyunlar yapılabilir?

Arduino Uno ile Pong, T-Rex koşucu, Flappy Bird benzeri oyunlar, yılan, basit blok oyunları, Simon hafıza oyunu, reaksiyon testi ve zar ya da rulet gibi şans oyunları yapılabilir. Grafik isteyen oyunlar için 128×64 OLED, renkli ızgara oyunları için NeoPixel LED matris yeterlidir. Simon ve reaksiyon testi gibi oyunlar ise yalnızca LED ve butonlarla, hiç ekran olmadan oynanır.

Arduino oyunu için hangi ekran en iyisi?

Yeni başlayan için en iyi seçim 0,96 inç SSD1306 OLED ekrandır: ucuzdur, yalnızca iki veri kablosuyla bağlanır ve 128×64 pikselle hareketli grafik çizer. Renkli ve büyük görüntü istiyorsan ILI9341 ya da ST7789 TFT ekran, piksel sanatı tarzı oyunlar için 8x8 ya da 16x16 LED matris uygundur. 16x2 LCD de özel karakterlerle basit koşucu oyunlarına yeter.

Arduino'da yüksek skor (rekor) nasıl kaydedilir?

Rekoru EEPROM'a yazarsan elektrik kesildiğinde de kaybolmaz. EEPROM.put(adres, rekor) ile yazar, EEPROM.get(adres, rekor) ile okursun; Arduino Uno'da bunun için 1024 bayt yer vardır. Her hücre yaklaşık 100.000 kez yazılabildiği için yalnızca oyun bitip rekor kırıldığında yaz.

Arduino ile oyun kolu (gamepad) yapılabilir mi?

Evet. Arduino Leonardo, Micro ya da Pro Micro gibi ATmega32U4 işlemcili kartlar bilgisayara USB klavye, fare ya da oyun kolu olarak tanıtılabilir; joystick ve butonları okuyup tuş basışı gönderirler. Arduino Uno bunu doğrudan yapamaz, ama joystick değerlerini seri port üzerinden bilgisayardaki bir programa gönderebilir.

Arduino oyunum neden yavaş çalışıyor ya da takılıyor?

En sık sebepler delay() ile kurulmuş döngüler, her karede yapılan Serial.print çağrıları ve ekranın gönderim süresidir. Uno'da OLED ekranın tamamını göndermek yaklaşık 25 ms sürdüğü için 30 ms'lik bir kare süresi gerçekçidir. delay() yerine millis() ile sabit kare süresi kullan, seri çıktıyı kaldır ve yalnızca gerekenleri çiz.

Yeni başlayan için en kolay Arduino oyunu hangisi?

Reaksiyon testi en kolayıdır: bir LED, bir buton ve kısa bir kodla yapılır ama zamanlama, rastgele sayı ve EEPROM gibi temel kavramları öğretir. Ardından dört LED ve dört butonla Simon'a, sonra da OLED ekranla Pong ve T-Rex oyunlarına geçmek iyi bir öğrenme sırasıdır.

Tüm projeleri gör
Raket Topu Oyunu - SSD1306
#86Orta
Premium

Raket Topu Oyunu - SSD1306

Bir top, iki raket ve sen: video oyunlarının en eskilerinden masa tenisini küçük bir OLED ekranda oynarsın. İki butonla sağdaki raketini yukarı aşağı oynatırsın, soldaki raketi ise topu gözüyle izleye

4.5(47)
65
OLED T-Rex Run: Arduino Dinozor Oyunu
#87İleri
Premium

OLED T-Rex Run: Arduino Dinozor Oyunu

İnternet kesilince çıkan dinozor oyunu artık masanda: küçük OLED ekranda dinozor koşar, kaktüsler üstüne gelir ve sen tek butonla zıplarsın. Puanın yandaki 4 haneli ekranda canlı olarak artar, her 100

4.7(33)
53
Piksel Yılan: 8x8 Matris
#88İleri
Premium

Piksel Yılan: 8x8 Matris

Klasik yılan oyununu kendi ellerinle kur ve oyna. Renkli 8x8 ışık matrisinde yeşil yılanı oyun çubuğuyla yönlendirip kırmızı elmaları yersin; her elmada yılan uzar, biraz daha hızlanır ve puanın küçük

4.7(39)
57
Matris Yılan 16x16
#89İleri
Premium

Matris Yılan 16x16

Klasik yılan oyunu, bu kez 256 ışıktan oluşan kocaman bir LED ekranda. Yeşil yılanı oyun koluyla yönlendirip kırmızı elmaları toplarsın; her elmada yılan uzar, puanın küçük rakam ekranında artar ve oy

4.8(52)
67
Arduino Hafıza Oyunu (4 LED + 4 Buton + Buzzer)
#93İleri
Premium

Arduino Hafıza Oyunu (4 LED + 4 Buton + Buzzer)

Hafızana güveniyor musun? Dört renkli LED sırayla yanar ve her biri kendi notasını çalar; sen de aynı sırayı butonlara basarak tekrar edersin. Her doğru turda sıraya yeni bir renk eklenir, küçük bir z

4.8(46)
69
Arduino LCD Koşucu Oyunu
#98Orta
Premium

Arduino LCD Koşucu Oyunu

16x2 karakter LCD ekranda çalışan yandan kaydırmalı sonsuz koşucu oyunu. Kahraman otomatik olarak koşar, oyuncu butona basarak engellerin üzerinden zıplar. Alt ve üst sırada rastgele engeller oluşur. Pasif buzzer ile açılış melodisi, zıplama sesi, 50 puan kutlama sesi ve oyun sonu melodisi çalar. Açılışta "BlueGrays — Arduino Projects" splash ekranı gösterilir. Skor ekranın sağ üst köşesinde anlık olarak güncellenir.

4.6(32)
55
Arduino Ses Uyarılı Reaksiyon Testi: Refleksini Milisaniyeyle Ölç (LCD + Buzzer + 3 LED)
#97Orta
Premium

Arduino Ses Uyarılı Reaksiyon Testi: Refleksini Milisaniyeyle Ölç (LCD + Buzzer + 3 LED)

Refleksin ne kadar hızlı? Butona bas: kırmızı LED hazırlan, sarı LED dikkat der ve 1,5 ile 3,5 saniye arası rastgele bir bekleme başlar. Yeşil LED yanıp buzzer öttüğü an yeniden bas; tepki süren I2C LCD'de milisaniye olarak çıkar, altında oturum rekorun ve ortalaman durur. Yeşilden önce basarsan ekranda TOO EARLY! yazar ve kayıp sesi çalar. Butonu basılı tutunca yeni oyuncu başlar, herkes aynı bekleme sırasıyla adil yarışır.

4.6(33)
50