React Native İle Yılan Oyunu ( Bölüm 3)

Yusuf Zeren
Oct 2, 2017 · 5 min read

Bu yazı bir kaç bölümden oluşmaktadır.

Birinci bölüm

İkinci bölüm.

Üçüncü bölümün source kodlarına şuradaki branch’den erişebilirsiniz:https://github.com/ysfzrn/crazysnake/tree/thirdpart


İlk olarak gameScreen ekranını registerScreen.js içinde register edelim.

Daha sonra root.js içinde gameScreen için durum yazalım.

Aşağıdaki gibi board’u oluşuralım.

gameStore’a dönelim ve oyunun nasıl state’lere ihtiyacı olduğuna bakalım.

Yılan her bir parçası Segment.js olarak isimlendirdiğim, component’ten oluşacak. Segment component’ine bakalım. Aşağıda görüldüğü bir absolute position’a sahip bir component. Ve dışarıdan left ve top, propslarını alıyor. Yani üstte verdiğimiz yılan başlangıç koordinatlarına göre yılanımız şekil alıyor. Herbir segment sharedStyle’dan gelen 10px lik, yükseklik ve genişliğe sahip. Böylece gameStore’da bulunan segmentRate’e göre uygun bir hareket sergileyecek.

Şimdi yılanımızı, gameStore’daki snake array’ini map ederek board’umuzun içine koyalım.

Peki bu yılan nasıl hareket edecek ?

React Native’de oyun yazmak gerçek anlamda bir challenge olabilir. Çünkü burada biz durmadan render edilecek bir yılanın segmentlerinden bahsediyoruz. Bu yılan büyüdüğünde mesela 100 segment’lik bir boya sahip olduğunda her bir milisaniyede her bir segmentin bir birim hareket etmesi zaten 100 defa render edilmesi demek. Bunu durmaksızın tüm board boyunca sonsuz kere tekrar edebilmesi, react native’in bridge yapısına ters gelebilir. Bu soru benim de aklımı bir hayli meşgul etti ve ilk denemem olan yılanı setInterval ile yapmam ve yılan hareket ettikçe yavaşlaması uygulamanın performansının yerlere düşmesine sebep oldu. Sonra başka hangi timer’ı kullanırım diye düşünürken, requestAnimationFrameimdadıma yetişti. requestAnimationFrame, saniyede 1000 kez bir kodu baştan aşağı çalıştırabiliyor.

Yılanımız başlangıç olarak sağa doğru hareket ediyor. Yani snake array’in içindeki ilk elemanın x koordinat +10 olarak artacak ve bir sonraki segment kendinden önceki segmentin koordinat değerlerine sahip olacak. Algoritmanın özeti bu. Şimdi gameStore’da ilgili action methodunu oluşturalım.

Buradaki kodu üste yazdığımız paragrafa göre inceleyelim.

İlk önce var olan snake array’inin bir kopyasını oluşturdum. Javascript’in , çoklu levelli array’lerde eşitlemede referans verip, asıl array’i değiştirmemesi için lodash’ın cloneDeep methodunu kullandım.

let temp = _.cloneDeep(this.snake.slice());

for döngüsü ile tüm array’i dönüyoruz. Eğer array’in ilk elemanı, yani yılanımızın başı değilse segment, yılanın yönü farketmeksizin kendisinden bir önceki segmentin değerlerini almasını istiyoruz. lastSegment değerini bu yüzden tutuyoruz.

if (i !== 0) { 
this.lastSegment = temp[i - 1];
}

Yılanın yönü, sağa veya sola doğru ise sadece x koordinat düzleminde işlem yapacağız demek. Sağa giderken yılanın başı, segmentRate kadar ilerleyecek. Eğer board’un sonuna geldiyse, board’un tekrar başına gidecek. Ve diğer segmentler de kendinden önceki segmentin değerlerini alacak.

if (this.currentDirection === "right") {
if (i === 0) {
if (this.snake[i].x + segmentRate >= boardWidth ) {
this.snake[i].x = 0;
}else{
this.snake[i].x = this.snake[i].x + segmentRate;
}
} else {
this.snake[i].x = this.lastSegment.x;
this.snake[i].y = this.lastSegment.y;
}
}

Bu işlemleri sürekli tekrar etmemizi sağlayacak kod parçacığına gelelim. requestAnimationFrame, saniyede 1000 kez frame geçişi yapabiliyor. Yılanımızın hızını kontrol etmek için requestAnimationFrame’i setTimeout ile beraber kullanıyoruz. Böylece frame geçiş hızını ayarlayabiliyoruz.

...
globalID = setTimeout(()=>{
requestAnimationFrame(this.handleMoveSnake);
}, 1000 / this.intervalRate);

Yılanın yönü sağa doğru iken yaptığımız işlemleri, sola, yukarı, aşağı olarak da aynı mantıkla if koşulları ekleyip çoğaltıyoruz. Kalabalık etmesin diye tekrar eden işlemleri buraya yazmak istemiyorum.

Daha sonra gameScreen ekranının componentDidMount methodua aşağıdaki kod parçacığını ekleyelim. Yılanın hareket ettiğini görelim. ( Tabi burada .gif almak için kullandığım tool’da tam bir smooth animation göremeyebilirsiniz, ama nasıl performanslı çalıştığını performans monitor’de görebilirsiniz. )

...
componentDidMount() {
const { gameStore } = this.props;
gameStore.handleMoveSnake();
}
...

Şimdi yön kontrollerini ekleyelim. Yön kontrollerinin mantığı, ilk bölümde belirttiğimiz gibi eski Nokia telefonlarda 3 ve 7 tuşlarıyla oynar gibi olacak. Sağdaki yön tuşu sağa ve yukarı götürecek, soldaki yön tuşu sola ve aşağı götürecek. Bunun için gameStore’a 2 tane daha action ekleyelim, handleRightButton ve handleLeftButton.

Daha sonra gameScreen ekranına da butonları ekleyelim.

Yılan, özgürce hareket etmeye başladı. Şimdi bu yılanı doyurmak için board’da random şekilde belirecek olan elma componentini yaratalım. Bunun için gameStore’da aşağıdaki işlemleri yapalım. elma board içinde segmentRate’in katları ( yani 10'nun katları ) koordinatlarda belirmesi lazım ki, yılanımız elmayı yiyebilsin.

Elma componentini de gameScreen ekranında board içine koyalım.

Yılan elmayı yedi mi kontrolü ekleyelim. Bu methodu yılan her bir segment ilerleyişinde çağırıp kontrol edelim. Yılan her elmayı yediğinde handleMakeFood methodunu çağırıp, yeni bir elma oluşturalım ve score state’ini +1 arttıralım ve snake array’ini bir tane daha eleman ekleyelim. Son olarak da yılan her 3 elma yediğinde hızını arttıralım. Aşağıdaki method bunları yapacak.

Şimdi yılan kendi kuyruğuna çarptığında oyunun bitmesini sağlayalım. Bunun için handleMoveSnake action methoduna bir kontrol daha ekleyelim. Burada da yapacağımız yılanın başı yani ilk segment, yılanın başka herhangi segmentiyle aynı koordinat değerlerine sahip olup olmadığını kontrol etmek. Eğer kuyruğuna çarpmışsa, requestAnimationFrame’i, cancelAnimationFrame ile sonlandıracağız. Elde edilen score, var olan en yüksek skordan daha büyük ise telefon hafızasına AsyncStorage ile yeni skoru set edeceğiz. Ve NavigationStore.handleChangeRoute('gameOverScreen'); ile de gameOver ekranına yönlendirme yapacağız.

Burada dikkat etmenizi istediğim 2 tane husus var.

  • cancelAnimationFrame, requestAnimationFrame’i durdurması için mutlaka sonunda return yapmanız gerekmektedir. Eğer yapmazsanız kod aşağı doğru derlenmeye devam edecektir.
  • MobX’de store’ları birbiri içinde import edip , birbirlerinin methodlarını çağırmasını sağlayabilirsiniz.

En son ekranımızın üzerinden de kısa bir şekilde geçelim. Sayfanın başındaki tasarıma bakarsanız, gameOver ekranında oyunu yeniden başlatacak bir tane buton var. Burada yapacağımız tek şey, NavigationStore.handleChangeRoute(‘gameScreen’); ile ekranı tekrar gameScreen’e çekmek ve başlangıçtaki state’lere geri dönmek. Aşağıdaki gibi;

Hepsi bu kadar. Bir sonraki bölümde bu oyunun Google Play Store’a ve AppStore’a deploy edilmesinden ve aşamalarından bahsedeceğiz. O zamana kadar görüşmek üzere.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade