Arduino ile Chrome Dino Oyunu
İnternetsiz kaldığımızda karşımıza çıkan Chrome Dino oyunu, artık Arduino ile de yapılabilir! Bu projede, Dino’yu buton ile kontrol ederek engellerden kaçmasını sağlayacağız. Skor sistemi, oyun bitti ekranı ve yeniden başlatma özellikleri de dahil.
Proje İçin Gerekli Malzemeler
- Arduino Uno veya Arduino Nano
- Mini Breadboard
- 16x2 LCD Ekran (I2C modüllü)
- 1x Push Buton
- 1x Pasif Buzzer
- Jumper Kablolar
Devre Kurulumu
LCD ekran I2C arayüzü ile bağlanmalı ve adresi kontrol edilmelidir. Buton dijital pine, buzzer ise ses efektleri için ayrı pine bağlanır. Tüm bağlantılar breadboard üzerinde yapılır.

I2C Adres Kontrolü
LCD ekranı kullanmadan önce I2C adresini tespit etmemiz gerekir. Bunun için aşağıdaki kod çalıştırılır ve Seri Monitör’den adres okunur.
#include <Wire.h>
void setup() {
Wire.begin();
Serial.begin(9600);
Serial.println("I2C adresleri taraniyor...");
}
void loop() {
for (byte address = 1; address < 127; address++) {
Wire.beginTransmission(address);
if (Wire.endTransmission() == 0) {
Serial.print("Aygit bulundu! Adres: 0x");
Serial.println(address, HEX);
}
}
Serial.println("Tarama tamamlandi.");
delay(5000);
}
Arduino Kodları
Aşağıdaki kod, Dino karakterini ve engelleri LCD üzerinde gösterir. Butona basıldığında Dino zıplar, engellerden kaçmaya çalışır. Skor artar ve çarpışma durumunda oyun biter.
Devre Kodu
#include <Wire.h> //I2C haberleşme için gerekli kütüphane
#include <LiquidCrystal_I2C.h> // I2C uyumlu LCD ekran kütüphanesi
LiquidCrystal_I2C lcd(0x27, 16, 2); // LCD ekran nesnesi tanımlanıyor (I2C Adresi, Sütun Sayısı, Satır Sayısı)
const int buttonPin = 2; // Butonun bağlı olduğu pin
const int buzzerPin = 3; // Buzzer'ın bağlı olduğu pin
int buttonState = 0; //buttonState değişkeni tanımlanıp 0 değeri veriliyor
int lastButtonState = 0; //lastButtonState değişkeni tanımlanıp 0 değeri veriliyor
//Karakterlerin ve engellerin bit dizilimleri
byte dino[8] = {
0b00000,
0b00111,
0b00111,
0b10110,
0b11111,
0b01010,
0b01010,
0b00000
};
byte smallCactus[8] = {
B00000,
B00000,
B00000,
B00100,
B00101,
B10111,
B11100,
B00100
};
byte mediumCactus[8] = {
B00000,
B00000,
B00100,
B00101,
B00111,
B10110,
B11100,
B00100
};
byte largeCactus[8] = {
B00100,
B00101,
B10101,
B11101,
B00111,
B00110,
B11100,
B00100
};
byte bird[8] = {
0b00000,
0b00100,
0b01100,
0b11110,
0b00111,
0b00110,
0b00100,
0b00000
};
int dinoPos = 1; // Dino'nun y pozisyonu (0: zıplıyor, 1: yerde)
int obstaclePos = 16; //Engel konumu değişkeni belirleniyor
int obstacleType = 0; // 0: küçük kaktüs, 1: orta kaktüs, 2: büyük kaktüs, 3: kuş
unsigned long lastMoveTime = 0;
int score = 0;
bool gameOver = false;
int jumpCounter = 0;
int gameSpeed = 300; // Başlangıç hızı (ms)
int levelCounter = 0;
void setup() {
lcd.init(); //LCD başlatılıyor
lcd.backlight(); //LCD arka ışıkları açılıyor
//Belirlenen bitler ile özel karakterler yaratılıyor
lcd.createChar(0, dino);
lcd.createChar(1, smallCactus);
lcd.createChar(2, mediumCactus);
lcd.createChar(3, largeCactus);
lcd.createChar(4, bird);
pinMode(buttonPin, INPUT_PULLUP); //Dijital pindeki dahili direnç aktifleştirilerek buton girişi tanımlanıyor
pinMode(buzzerPin, OUTPUT);
lcd.setCursor(0, 0); //LCD imleci 1. satır, 1. sütuna getiriliyor
lcd.print("Dino Game");
lcd.setCursor(0, 1); //LCD imleci 2. satır, 1. sütuna getiriliyor
lcd.print("Press to start");
while (digitalRead(buttonPin) == HIGH) { //Buton basılı olmadığı sürece 50 ms bekle
delay(50);
}
//Butona basıldığında while döngüsünden çıkılır
playStartSound(); //playStartSound fonksiyonu çağrılıyor
lcd.clear(); //LCD temizleniyor
}
void loop() {
if (!gameOver) { //Oyun bitmiş değilse
unsigned long currentTime = millis();
buttonState = digitalRead(buttonPin);
if (buttonState == LOW && lastButtonState == HIGH && dinoPos == 1) { //Butona basılmış, butonun önceki durumu basılmamış ve Dino yerde ise
dinoPos = 0; // Zıpla
jumpCounter = 3; // 3 kare boyunca zıplamada kal
playJumpSound(); //playJumpSound fonksiyonu çağrılıyor
}
lastButtonState = buttonState; //Mevcut buton durumu lastButtonState değişkenine atanır
if (currentTime - lastMoveTime > gameSpeed) { // Geçen süre oyun hızından büyükse (yani hareket zamanı geldiyse)
lastMoveTime = currentTime; //Son hareket zamanını güncelle
lcd.clear();
if (jumpCounter > 0) { Zıplama sayacı 0'dan büyük (devam ediyor) ise
jumpCounter--; //Değişkenin değerini bir azalt
} else if (dinoPos == 0) { //Zıplama sayacı 0'dan büyük değil ve Dino havadaysa
dinoPos = 1; // Yere in
}
lcd.setCursor(2, dinoPos); //LCD imleci Dino'nun konumuna getiriliyor
lcd.write(byte(0)); // Dino konumuna yazdırılıyor
obstaclePos--; //Engel bir sütun sola kaydırılıyor
if (obstaclePos < 0) { //Engel konumu 1. satırdan solda (0'dan küçük) ise
obstaclePos = 16; //obstaclePos değişkenine 16 değerini ata
score++; //Skoru bir arttır
levelCounter++; //Seviye sayacını bir arttır
// Her 10 puanda bir hızı arttır
if (levelCounter % 10 == 0 && gameSpeed > 100) {
gameSpeed -= 20; // Hız 20 ms artırılıyor (hareket bekleme süresi azalıyor)
}
// Engel seçimi
int randomObstacle = random(100); //0-99 arası rastgele sayı üretiliyor
if (score > 10 && randomObstacle < 30) { // %30 ihtimalle kuş
obstacleType = 3;
} else if (randomObstacle < 50) { // %20 ihtimalle küçük kaktüs
obstacleType = 0;
} else if (randomObstacle < 80) { // %30 ihtimalle orta kaktüs
obstacleType = 1;
} else { // %20 ihtimalle büyük kaktüs
obstacleType = 2;
}
}
if (obstacleType == 3) { // Engel kuş ise
lcd.setCursor(obstaclePos, 0); //LCD imleci üst satıra getiriliyor
lcd.write(byte(4)); //Kuş karakteri yazdırılıyor
} else { // Engel kuş değilse (kaktüsler)
lcd.setCursor(obstaclePos, 1); //LCD imleci alt satıra getiriliyor
lcd.write(byte(obstacleType + 1)); //Özel karakterler ile engel türleri eşleşmediği için 1 ekleyerek özel karakter dizinini yazdırıyoruz
}
lcd.setCursor(12, 0);
lcd.print(score);
// Çarpışma kontrolü
bool collision = false; // Çarpışma durumunu kontrol etmek için değişken (false: çarpışma yok, true: çarpışma)
if (obstaclePos == 2) { // Engel 2. sütunda ise (Dino'nun bulunduğu sütun)
if (obstacleType == 3 && dinoPos == 0) { // Engel kuş ise ve Dino havada ise
collision = true;
} else if (obstacleType < 3 && dinoPos == 1) { //Engel kaktüs ise ve Dino yerde ise
collision = true;
}
}
if (collision) { //collision değişkeni true ise (çarpışma varsa)
gameOver = true;
playGameOverSound(); //playGameOverSound fonksiyonu çağrılıyor
}
}
} else { //Oyun bitmişse
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Game Over!");
lcd.setCursor(0, 1);
lcd.print("Score: ");
lcd.print(score);
delay(3000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Press to restart");
while (digitalRead(buttonPin) == HIGH) { //Butona basılmadıkça 50 ms bekle
delay(50);
}
//Butona basıldığında while döngüsünden çıkılır ve değişken değerleri ilk duruma getirilir
gameOver = false;
dinoPos = 1;
obstaclePos = 16;
obstacleType = 0;
score = 0;
gameSpeed = 300;
levelCounter = 0;
playStartSound();
lcd.clear();
}
}
void playStartSound() {
tone(buzzerPin, 523, 100); // Buzzer'ı 523 Hz (Do) frekansında 100 ms çal
delay(100);
tone(buzzerPin, 659, 100); // Buzzer'ı 659 Hz (Mi) frekansında 100 ms çal
delay(100);
tone(buzzerPin, 784, 100); // Buzzer'ı 784 Hz (Sol) frekansında 100 ms çal
delay(100);
}
void playJumpSound() {
tone(buzzerPin, 1000, 50);
}
void playGameOverSound() {
tone(buzzerPin, 200, 200);
delay(200);
tone(buzzerPin, 150, 200);
delay(200);
tone(buzzerPin, 100, 400);
}
Projenin Çalışma Mantığı
- Dino butona basıldığında zıplar.
- Kaktüs ve kuş engelleri rastgele belirir.
- Her engel geçildiğinde skor artar.
- Çarpışma olduğunda Game Over ekranı çıkar.
- Butona basarak oyun yeniden başlatılabilir.
Sonuç
Bu proje sayesinde Arduino ile eğlenceli bir mini oyun yapmayı öğrendik. Hem LCD ekran kullanımı hem de buton-buzzer gibi giriş/çıkış elemanlarını bir arada deneyimlemek için harika bir örnektir.
Daha fazla Arduino projesi için Arduino kategorimize göz atabilirsiniz.
Hazırlayan: Kirpilab | Maker topluluğu için Arduino projeleri