Processing Flappy Bird Oyunu Yapımı

Processing Flappy Bird Oyunu Yapımı

 Merhaba arkadaşlar bu sıralar okulda dersini almam sebebi ile Processing e sarmış durumdayım ve hocamızın da ödev vermesi ile Processing dilinde flappy bird tarzı bir oyun kodladım ve bu kodları da sizlerle paylamak istedim, oldukça bir yazı olacağa benziyor ki zaten kodlar 200 satır civarında bu yüzden daha da uzatmadan direkt olarak kodlamaya geçelim.

Processing Flappy Bird Oyunu Yapımı

Processing Flappy Bird Oyunu Yapımı

Processing Flappy Bird Oyunu Yapımı

Oyunun Çalışma Mantığı

İlk olarak oyunda görsel olarak karakterimizi yuvarlaktan oluşturuyoruz, ardından engeller için dik dörtgenler oluşturuyoruz. 

Yuvarlaktan oluşturduğumuz karakterimizi yukarı aşağı hareket ettirerek engellerden kaçmasını sağlıyoruz, engel için oluşturduğumuz dikdörtgen objeleri de sürekli olarak sola doğru kaydırıyoruz.

Oyunun oynanış kısmı bu mantık ile yapılıyor tabi buna ek olarak oyuna başla ekranı, bitiş ekranı ve engellerden kaçtığı zaman ilerlemesini anlaması için bir puan sistemi de ekliyoruz.

Blokların konumlarını hesaplayıp ona göre topun konumu ile kıyaslayıp çarpışma algılama sistemi de kodluyoruz ki karakter engellere çarptığı zaman oyun bitsin.

Not : Ben oyunu kodlarken engelleri sola doğru kaydırdım çünkü bu benim kolayıma gitti, siz eğer isterseniz engeller yerine karakteri sağa doğru kaydırabilirsiniz. Yazılımda hiç bir şeyin tek bir yolu yoktur.

Oyunu Yaparken Kullandığımız Değişkenler ve Açıklamaları

/** Değişkenler **/
/** Sahne Değişkeni **/
// 0 Giriş ekranı
// 1 Oyun ekranı
// 2 Kaybettin ekranı
int oyunEkrani = 0; // Hangi oyun ekranında olduğumuzun değişkeni 
/** 1. Tekrar edilebilirlik için sahne 0 kontrolü **/
boolean classCagir = true; // Eğer 1 den fazla oynanırsa sahneyi sıfırlanmasındaki kontrol için.
/** Puan **/
float puan; // Puanı tuttuğumuz değişken
/** Kuş Değişkenleri **/
Kus k; // Kuş classını k ile çağırıyoruz.
boolean zipladimi = false; // Kuş zıpladımı kontrol ediyoruz.
float baslangicZamani; // Kuşun zıplamaya başadığı millis değerini kaydediyoruz.
/** Duvar Değişkenleri **/
int duvarSayisi = 100, duvarlarArasiAralik; // Oyundaki altlı üstlü duvar sayısı (Çift sayı olmak zorunda), duvarlar arası mesafe hesaplamak için olan değişken
float duvarlarArasiYukseklik; // Alt ve üst duvarların arasındaki yükseklik farkı
Duvar[] duvarlar = new Duvar[duvarSayisi]; // Duvar classını çağırdığımız dizi

Hangi değişkeni ne amaç ile kullandığımıza gelecek olursak, 
  • oyunEkrani bu değişken ile sahneler arası geçişleri sağlıyoruz.
  • classCagir bu değişken ile oyuncu oyunu kapat aç yapmadan yeniden oynamasını sağlıyoruz ve bool kontrolü ile oyunu resetliyoruz.
  • puan değişkeni ile oyuncunun puanını kaydediyoruz.
  • Kus k ve Duvar[] değişkenleri ile kuş classını ve duvar classını çağırıyoruz.
  • zipladimi değişkeni ile karakterin zıplayıp zıplamadığını kontrol ediyoruz.
  • baslangicZamani ile kuşun ne zaman zıpladığını algılıyor ve ona göre havada tutuyoruz.
  • duvarSayisi altlı üstlü oyunda göterilecek toplam engel sayısını gösteriyor. (100 yaptığınız zaman altlı üstlü olacağı için 50 tane engelden kaçacaktır.)
  • duvarlarArasiAralik yatay enlemde duvarların birbirine olan uzaklıgı, duvarlarArasiYukseklik dikey enlemde duvarların birbirine olan uzaklıgı için oluşturduk.

Sabit Fonksiyonlar ve Açıklamaları

/** Sabit fonksiyonlar **/
void setup() {
  size(300, 600);
}
void draw() {
  if (oyunEkrani == 0) {
    GirisEkrani();
  } else if (oyunEkrani == 1) {
    OyunEkrani();
  } else if (oyunEkrani == 2) {
    KaybettinEkrani();
  }
}

Burada start() uygulama açıldığı zaman 1 kere çalışan kodları yazmak için olan yerdir, size(x,y) ile ekran boyutunu belirledik.

draw() sürekli olarak çalışan kodları yazmak için olan standart fonksiyondur, oyun içinde gerçekleşecek olan bütün olaylar burada yazılır. Biz sahneleri bir if kontrolü kullanarak buranın içine çektik.

Mekanik ve Diğer Fonksiyonların Açıklamaları

/** Fonksiyonlar **/
/** Çarpışma Algılama Fonksiyonu **/
void CarpismaAlgila(Duvar duvar, Kus kus) {
  if (duvar.poz.x < 120 && duvar.poz.x > 60) {
    if ((duvar.poz.y <= 0) && kus.poz.y < (duvar.poz.y + 410)) {
      println("kaybettin üste çarptın");
      KaybettinEkraniGec();
    } else if ((duvar.poz.y > 0) && kus.poz.y > (duvar.poz.y -10)) {
      println("Kaybettin alta carptin");   
      KaybettinEkraniGec();
    } else if(duvar.poz.x == 116){
        puan+= 0.5;
    }
  }
}

/** Kontrolcü Fonksiyonları **/
void mousePressed() {
  if (oyunEkrani == 0) {
    OyunuBaslatEkraniGec();
  }
  if (oyunEkrani == 2) {
    GirisEkraniGec();
  }
}
void keyPressed() {
  if (key == ' ') {
    k.Ziplat();
  }
}
/** Sahne Değiştirme Fonksiyonları **/
void GirisEkraniGec() {
  classCagir = true;
  oyunEkrani = 0;
}
void OyunuBaslatEkraniGec() {
  oyunEkrani = 1;
}
void KaybettinEkraniGec() {
  oyunEkrani = 2;
}

/** Oyun Ekranları Fonksiyonları **/
/** Giriş Ekranı Sahnesi **/
void GirisEkrani() {
  background(0, 255, 0);
  textAlign(CENTER);
  textSize(28);
  text("Tıkla ve oyuna başla", width/2, height/2);
  if (classCagir) {
    duvarlarArasiAralik = 0;
    duvarlarArasiYukseklik = 0;
    puan = 0;
    k = new Kus();
    for (int i = 0; i < duvarSayisi; i++) {
      duvarlar[i] = new Duvar();


      if (i%2 == 0) {
        duvarlar[i].Yerlestir(duvarlarArasiYukseklik, duvarlarArasiAralik);
      } else {
        duvarlar[i].Yerlestir((duvarlarArasiYukseklik + random(550, 600)), duvarlarArasiAralik);
        float randomAralik = random(200,300);
        if(int(randomAralik) % 2 != 0){
        randomAralik += 1;
        }
        duvarlarArasiAralik += randomAralik;
        duvarlarArasiYukseklik = random(-300, 0);
      }
    }
    classCagir = false;
  }
}
/** Puan Sistemi **/
void PuanGoster(){
fill(255,255,255);
textAlign(CENTER);
text("Puan : " + int(puan), width/2, 100);
}
/** Oyun Alanı Sahnesi **/
void OyunEkrani() {
  background(155, 55, 55);
  k.Goster();
  k.Guncelle();
  for (int i = 0; i < duvarSayisi/2; i++) {
    duvarlar[i].HareketEttir();
    duvarlar[i].Goster();
    CarpismaAlgila(duvarlar[i], k);
  }
  for (int i = duvarSayisi/2; i < duvarSayisi; i++) {
    duvarlar[i].HareketEttir();
    duvarlar[i].Goster();
    CarpismaAlgila(duvarlar[i], k);
  }
  PuanGoster();
}
/** Kaybedince Açılan Sahne **/
void KaybettinEkrani() {
  background(255, 0, 0);
  textAlign(CENTER);
  fill(255, 255, 255);
  text("Puan : "+ int(puan) + "\nTıkla ve başa dön", width/2, height/2);
}

Oyunumuzun 2 kalbinden birisi burası işte buradaki fonksiyonlar oyunun temelini oluşturan fonksiyonlar burada yer alıyor zaten ikinci kalbi ise classlarda yer alıyor.

  • CarpismaAlgila() fonksiyonu buraya duvarların boyutlarını bildiğimiz için konumlarını hesaplıyoruz ve sonrasında kuş o konumun içine girdiği zaman oyunu kaybetmesini sağlıyoruz, eğer o alanın içine girmez ise puan kazanmasını sağlıyoruz. Puanı float olarak 0.5 olarak veriyoruz çünkü fonksiyonuz her engelde 1 kere çalışıyor ve bize altlı üstlü 2 engel geldiği için 2 engelden 0.5 puan alıyor bu da toplanınca 1 puan ediyor.
  • mousePressed fonksiyo ile fareye(sol) sahne0 da tıklantığında sahne1 e geçmesini sağlıyoruz, kaybettin ekranında fareye tıklandığı zaman sahne0 geçmesini sağlıyoruz.
  • keyPressed() bu fonksiyon ile boşluk tuşuna tıklandığı zaman karakterin zıplaması için kuş classındaki Ziplat() fonksiyonunu çağırıyoruz.
  • GirisEkraniGec() , OyunuBaslatEkraniGec() , KaybettinEkraniGec() ile sahneleri değiştiriyoruz.
  • GirisEkrani() ile oyun ilk açıldığı zaman olan ekranı ve oyunu yeniden oynanacağı zaman değişkenleri sıfırlama işlemlerini gerçekleştiriyoruz. Aynı zamanda kuş oluşturma, 2. oyunda duvarlar arası mesafe gibi ayarları da bu fonksiyonda gerçekleştiriyoruz.
  • PuanGoster() oyun ekranında kullanıcının puanını görmesini bu fonksiyon içerisinde ayarlıyoruz.
  • OyunEkrani() oyuncunun oyunu oynarken gördüğü sahneyi bu ekranda ayarlıyoruz. Karakteri gösteriyoruz, hareket olaylarını gerçekleştiriyoruz, duvarları oluşturup sola doğru kaydırıyoruz. Çarpışmayı algılıyoruz.
  • KaybettinEkrani() oyuncunun engele çarptığı zaman açılacağı sahne kodlarını burada yazıyoruz.

Classlar ve Açıklamaları

/** Classlar **/
/** Duvarlar **/
class Duvar {
  PVector poz;
  PVector boyut;
  float hiz;

  Duvar() {
    poz = new PVector(500, random(height));  
    hiz = 2;
    boyut = new PVector(50, 400);
  }
  void Yerlestir(float yukseklik, float aralik) {
    poz.y = yukseklik;
    poz.x += aralik;
  }
  void HareketEttir() {
    poz.x -= hiz;
  }
  void Goster() {
    fill(0, 102, 0);
    rect(poz.x, poz.y, boyut.x, boyut.y);
  }
}


/** Kuş Class **/
class Kus {
  PVector poz;
  float yerCekimi, ivme, ziplamaGucu, boyut, kutle;
  Kus() {
    poz = new PVector(100, 100);
    boyut = 40;
    yerCekimi = 3;
    ivme = 0;
    ziplamaGucu = -6;
  }
  void Goster() {
    fill(255, 51, 51);
    ellipse(poz.x, poz.y, boyut, boyut);
  }

  void Guncelle() {
    if(zipladimi){
    ivme += ziplamaGucu;
     //print("anlik zaman" + millis() + " baslangic+ " + (baslangicZamani+0.5));
    if(millis() > baslangicZamani+300){
     
    zipladimi = false;
    }
    
    }else{
    ivme += yerCekimi;
    }
 
    poz.y = ivme;
    if (poz.y>height) {
      poz.y = height;
      ivme = 0;
    }
  }
  void Ziplat() {
    baslangicZamani = millis();
  
    zipladimi = true;
  }
}
Karakterimizi oluşturduğumuz, kontrol ettiğimiz aynı zamanda az kod ile çok duvar oluşturmak için oluşturduğumuz duvar kodlarımızı da barındıran kodlarımız burada yer alıyor gelin bu kodları açıklayalım.
  • class Duvarlar burada sürekli duvar oluşturmak için aynı kodları yüzlerce kez yazmak yerine bir sınıf oluşturuyoruz ve 1 adet duvar kodluyoruz sonrasında bu sınıfı çekerek kullanacağız neyse sınıfımızın içine geçelim.
  • PVector poz ve boyut ile duvarımızın konumu ve eni boyunu belirlemek için sınıf içi değişken oluşturuyoruz. hiz ile duvarın sola doğru hangi hızda kayacağını belirlemek için oluşturuyoruz.
  • Duvar() ile sınıf çağırıldığı zaman hizi, boyutunu ve konum değerlerini atadığımız yer.
  • Yerlestir() çağırıldığı zaman verilen değerlere göre duvarın konumunu ayarladığımız fonksiyon.
  • Hareket Ettir() duvarı sola doğru kaydırdığımız fonksiyon.
  • Goster() duvarın her yeri değiştiği zaman ekrana yeniden çizilmesini sağlayan kod.

  • class Kus burası kuşu aynı duvar sınıfında duvarı oluşturduğumuz gibi oluşturduğumuz ve sınıf içi fonksiyonlar ile kontrol ettiğimiz yer.
  • PVector poz ile kuşun konumunu belirliyoruz, float yerCekimi, ivme, ziplamaGucu, boyut gibi değişkenler ile de kuşun yere düşme hızı, yukarı çıkma hızı gibi şeyleri kontrol ediyoruz.
  • Kus() ile kuşun başlangıç konumu, boyutu, yer çekimi gücü, ivmenin değeri, zıplama gücü gibi değerleri belirliyoruz
  • Goster() ile kuşun hareket ettiği zamanlarda ekranda yeniden çizilmesini ve kullanıcının görmesini sağlıyoruz.
  • Guncelle() ile kuşun zıpladığı zaman hareket etmesi, yere düşmesi gibi işlemleri gerçekleştiriyoruz
  • Ziplat() ile kuşun zıplamasını aktif hale getiriyoruz.
Not: Zıplamayı ben millis fonksiyonu ile 300 ms yukarı çıkacak şekilde ayarladım böylelikle kuş ışınlanmak yerine süzülerek yukarı çıkıyor.

Tüm Kodlar

/** Değişkenler **/
/** Sahne Değişkeni **/
// 0 Giriş ekranı
// 1 Oyun ekranı
// 2 Kaybettin ekranı
int oyunEkrani = 0; // Hangi oyun ekranında olduğumuzun değişkeni 
/** 1. Tekrar edilebilirlik için sahne 0 kontrolü **/
boolean classCagir = true; // Eğer 1 den fazla oynanırsa sahneyi sıfırlanmasındaki kontrol için.
/** Puan **/
float puan; // Puanı tuttuğumuz değişken
/** Kuş Değişkenleri **/
Kus k; // Kuş classını k ile çağırıyoruz.
boolean zipladimi = false; // Kuş zıpladımı kontrol ediyoruz.
float baslangicZamani; // Kuşun zıplamaya başadığı millis değerini kaydediyoruz.
/** Duvar Değişkenleri **/
int duvarSayisi = 100, duvarlarArasiAralik; // Oyundaki altlı üstlü duvar sayısı (Çift sayı olmak zorunda), duvarlar arası mesafe hesaplamak için olan değişken
float duvarlarArasiYukseklik; // Alt ve üst duvarların arasındaki yükseklik farkı
Duvar[] duvarlar = new Duvar[duvarSayisi]; // Duvar classını çağırdığımız dizi

/** Sabit fonksiyonlar **/
void setup() {
  size(300, 600);
}
void draw() {
  if (oyunEkrani == 0) {
    GirisEkrani();
  } else if (oyunEkrani == 1) {
    OyunEkrani();
  } else if (oyunEkrani == 2) {
    KaybettinEkrani();
  }
}


/** Fonksiyonlar **/
/** Çarpışma Algılama Fonksiyonu **/
void CarpismaAlgila(Duvar duvar, Kus kus) {
  if (duvar.poz.x < 120 && duvar.poz.x > 60) {
    if ((duvar.poz.y <= 0) && kus.poz.y < (duvar.poz.y + 410)) {
      println("kaybettin üste çarptın");
      KaybettinEkraniGec();
    } else if ((duvar.poz.y > 0) && kus.poz.y > (duvar.poz.y -10)) {
      println("Kaybettin alta carptin");   
      KaybettinEkraniGec();
    } else if(duvar.poz.x == 116){
        puan+= 0.5;
    }
  }
}

/** Kontrolcü Fonksiyonları **/
void mousePressed() {
  if (oyunEkrani == 0) {
    OyunuBaslatEkraniGec();
  }
  if (oyunEkrani == 2) {
    GirisEkraniGec();
  }
}
void keyPressed() {
  if (key == ' ') {
    k.Ziplat();
  }
}
/** Sahne Değiştirme Fonksiyonları **/
void GirisEkraniGec() {
  classCagir = true;
  oyunEkrani = 0;
}
void OyunuBaslatEkraniGec() {
  oyunEkrani = 1;
}
void KaybettinEkraniGec() {
  oyunEkrani = 2;
}

/** Oyun Ekranları Fonksiyonları **/
/** Giriş Ekranı Sahnesi **/
void GirisEkrani() {
  background(0, 255, 0);
  textAlign(CENTER);
  textSize(28);
  text("Tıkla ve oyuna başla", width/2, height/2);
  if (classCagir) {
    duvarlarArasiAralik = 0;
    duvarlarArasiYukseklik = 0;
    puan = 0;
    k = new Kus();
    for (int i = 0; i < duvarSayisi; i++) {
      duvarlar[i] = new Duvar();


      if (i%2 == 0) {
        duvarlar[i].Yerlestir(duvarlarArasiYukseklik, duvarlarArasiAralik);
      } else {
        duvarlar[i].Yerlestir((duvarlarArasiYukseklik + random(550, 600)), duvarlarArasiAralik);
        float randomAralik = random(200,300);
        if(int(randomAralik) % 2 != 0){
        randomAralik += 1;
        }
        duvarlarArasiAralik += randomAralik;
        duvarlarArasiYukseklik = random(-300, 0);
      }
    }
    classCagir = false;
  }
}
/** Puan Sistemi **/
void PuanGoster(){
fill(255,255,255);
textAlign(CENTER);
text("Puan : " + int(puan), width/2, 100);
}
/** Oyun Alanı Sahnesi **/
void OyunEkrani() {
  background(155, 55, 55);
  k.Goster();
  k.Guncelle();
  for (int i = 0; i < duvarSayisi/2; i++) {
    duvarlar[i].HareketEttir();
    duvarlar[i].Goster();
    CarpismaAlgila(duvarlar[i], k);
  }
  for (int i = duvarSayisi/2; i < duvarSayisi; i++) {
    duvarlar[i].HareketEttir();
    duvarlar[i].Goster();
    CarpismaAlgila(duvarlar[i], k);
  }
  PuanGoster();
}
/** Kaybedince Açılan Sahne **/
void KaybettinEkrani() {
  background(255, 0, 0);
  textAlign(CENTER);
  fill(255, 255, 255);
  text("Puan : "+ int(puan) + "\nTıkla ve başa dön", width/2, height/2);
}

/** Classlar **/
/** Duvarlar **/
class Duvar {
  PVector poz;
  PVector boyut;
  float hiz;

  Duvar() {
    poz = new PVector(500, random(height));  
    hiz = 2;
    boyut = new PVector(50, 400);
  }
  void Yerlestir(float yukseklik, float aralik) {
    poz.y = yukseklik;
    poz.x += aralik;
  }
  void HareketEttir() {
    poz.x -= hiz;
  }
  void Goster() {
    fill(0, 102, 0);
    rect(poz.x, poz.y, boyut.x, boyut.y);
  }
}


/** Kuş Class **/
class Kus {
  PVector poz;
  float yerCekimi, ivme, ziplamaGucu, boyut, kutle;
  Kus() {
    poz = new PVector(100, 100);
    boyut = 40;
    yerCekimi = 3;
    ivme = 0;
    ziplamaGucu = -6;
  }
  void Goster() {
    fill(255, 51, 51);
    ellipse(poz.x, poz.y, boyut, boyut);
  }

  void Guncelle() {
    if(zipladimi){
    ivme += ziplamaGucu;
     //print("anlik zaman" + millis() + " baslangic+ " + (baslangicZamani+0.5));
    if(millis() > baslangicZamani+300){
     
    zipladimi = false;
    }
    
    }else{
    ivme += yerCekimi;
    }
 
    poz.y = ivme;
    if (poz.y>height) {
      poz.y = height;
      ivme = 0;
    }
  }
  void Ziplat() {
    baslangicZamani = millis();
  
    zipladimi = true;
  }
}

Oyun İçinden Video