Thursday, March 10, 2016

Jquery ile Yılan Oyununun Gerçeklenmesi - Implementing Snake Game with Jquery

Bu yazıda telefonlarda eskiden sıklıkla oynanan yılan oyununu javascript ve jquery ile oluşturacağız. Yılan oyununun ana fikri yılanı oluşturan parçalarının tren katarları gibi birbirini izlemesidir. Yem alındıkça puan güncellenmeli ve yılan büyümelidir. Yılan kendine veya duvarlara çarptığında oyun bitecektir. İlk olarak CSS kodlarımızı oluşturalım:

<style>

    .SGContainer
    {
        width:204px;
        height:auto; 
    }

    .SGCanvas
    {
        background-color:lightblue;
        width:204px;
        height:204px;
        position:relative;
       
    }

    .SGCanvas:focus
    {
        border:solid;
        border-width:2px;
        border-color:darkgreen;
    }

    .SGSnakePart
    {
        background-color:black;
        position:absolute;
        width:5px;
        height:5px;
    }

    .SGSnakeFood
    {
        background-color:darkred;
        position:absolute;
        width:5px;
        height:5px;
    }

    .SGFinish
    {
        font-weight:bold;
        background-color:brown;
        color:white;
        z-index:1;
        position:absolute;
        top:50%;
        left:50%;
        margin-top:-25px;
        margin-left:-50px;
        width:100px;
        height:50px;
        justify-content: center;
        align-items: center;   
        display:flex;   
    }
</style>

Yukarıdaki CSS kodlarında önemli nokta flex kutusu (flex box) kullanımıdır. Flex kutusu klasik kutuya göre daha esnek özellikler sunmaktadır. Burada "align-items" ile flex kutusunun içeriği dikey olarak, "justify-content" ile de yatay olarak konumlandırılmıştır. Sonraki aşamada javascript kodlarını tanımlayalım:

var Snake = [];
var Food = [];

var Direction = "Right";
var MoveNumber = 5
var Canvas = null;
var Interval = null;
var IsGameStarted = false;
var Score = 0;

var SnakePart = function () {

    this.X = -1;
    this.Y = -1;
    this.DomObject = null;
}

function StartGame() {

    IsGameStarted = false;
    Canvas = $(".SGCanvas");
    $(Canvas).children().remove();
    Snake = [];
    Food = [];
    Direction = "Right";
    Score = 0;

    StopAnimation();

    for (var i = 10; i > -1; i--) {

        var part = new SnakePart();
        part.X = i * 5;
        part.Y = 100;

        var domPart = $('<div class="SGSnakePart"></div>');
        part.DomObject = domPart;

        part.DomObject.css({ top: part.Y, left: part.X });

        Snake.push(part);

        AddPartToCanvas(part.DomObject)
    }

    $(Canvas).focus();

    $(Canvas).keydown(function (event) {

        event.preventDefault();
        SGUpdateDirection(event.keyCode);
    });

    $(Canvas).focusout(function () {

        if (IsGameStarted)
            StopAnimation();
    });

    $(Canvas).focusin(function () {

        if (IsGameStarted)
            StartAnimation();
    });
    
    GenerateFood();

    StartAnimation();
    IsGameStarted = true;
}

function StartAnimation() {

    if (Interval == null)
        Interval = setInterval(SGUpdate, 50);
}

function StopAnimation() {

    if (Interval != null)
        clearInterval(Interval);
    Interval = null;
}

function AddPartToCanvas(part)
{
    $(Canvas).append(part);
}

function GenerateFood() {

    var x = parseInt((Math.random() * 100) % 40) * 5;
    var y = parseInt((Math.random() * 100) % 40) * 5;

    var part = new SnakePart();
    part.X = x;
    part.Y = y;

    var domPart = $('<div class="SGSnakeFood"></div>');
    part.DomObject = domPart;
    part.DomObject.css({ top: part.Y, left: part.X });

    Food.push(part);

    AddPartToCanvas(part.DomObject);
}

function EatFood() {

    if(Snake[0].Y == Food[0].Y && Snake[0].X == Food[0].X)
    {
        Score++;
        Snake.push(Food.pop());
        GenerateFood();
    } 
}

function SGUpdateDirection(key) {
  
    if (key == 39 && Direction != "Left") {

        Direction = "Right";
    }

    if (key == 37 && Direction != "Right") {

        Direction = "Left";
    }

    if (key == 38 && Direction != "Down") {

        Direction = "Up";
    }

    if (key == 40 && Direction != "Up") {

        Direction = "Down";
    }
}

function SGUpdate() {


    for (var i = Snake.length - 1; i > 0; i--) {

        Snake[i].X = Snake[i - 1].X;
        Snake[i].Y = Snake[i - 1].Y;
    }

    if (Direction == "Right") {

        Snake[0].X += MoveNumber;
    }

    if (Direction == "Left") {

        Snake[0].X -= MoveNumber;
    }

    if (Direction == "Up") {

        Snake[0].Y -= MoveNumber;
    }

    if (Direction == "Down") {

        Snake[0].Y += MoveNumber;
    }

    if (SGIsGameOver())
    {
        SGGameOver();
        return;
    }

    EatFood();
    UpdateScore();
    SGUpdateCanvas();
}

function SGUpdateCanvas()
{
    for (var i = Snake.length - 1; i > -1; i--) {

        $(Snake[i].DomObject).css({ top: Snake[i].Y, left: Snake[i].X });
    }
}

function SGIsGameOver() {

    for (var i = Snake.length - 1; i > 0; i--) {

        if (Snake[0].X == Snake[i].X && Snake[0].Y == Snake[i].Y)
            return true;
        if (Snake[0].X == 200 || Snake[0].X < 0 || Snake[0].Y == 200 || Snake[0].Y < 0)
            return true;
    }

    return false;
}

function UpdateScore() {

    $("#SGPuan").text(Score);
}

function SGGameOver() {

    IsGameStarted = false;
    StopAnimation();
    $(Canvas).append($('<div class="SGFinish">OYUN BİTTİ!</div>'));
}

Yukarda yer alan fonksiyonların açıklamaları şöyledir:

* StartGame(): Oyunun sıfırlanmasını ve başlamasını sağlayan fonksiyondur. Burada başlangıç olarak 10 parça yılan oluşturulmakta, canvasla ilgili klavye olaylarına jquery ile fonksiyon atanmakta ve ilgili fonksiyonlar aracılığıyla animasyon başlatılmaktadır. Jquery keydown olayı bir klavye tuşuna basıldığında tetiklenmektedir. Bu fonksiyon içinde kullanılan "event.preventDefault()" ile ok tuşlarının ekranı oynatması engellenmekte yani tuşun ön tanımlı özelliği pasivize edilmektedir. Jquery focus() fonksiyonu ile herhangi bir elemente focus (odaklanma) yapılması sağlanmaktadır. Bu fonksiyonun düzgün çalışması için odaklanılacak elementin "tabindex" niteliği -1 olmalıdır. Jquery focusin() odaklanma olduğunda, focusout() ise odaklanma kaybedildiğinde tetiklenmektedir. İlgili olaylar tetiklendiğinde animasyon durdurulmakta veya devam ettirilmektedir.

* StartAnimation(): Oyun animasyonunun başlatılmasını sağlayan fonksiyondur. Javascript setInterval(çağrılacakfonksiyon, süre) fonksiyonu ile animasyon oynatılmaktadır. Bu fonksiyon ile parametre olarak gönderilen çağrılacak fonksiyon her süre dolduğunda çalıştırılmaktadır. Bu fonksiyon setTimeout() fonksiyondan farklı olarak devamlı olarak çağrıma devam etmektedir. Javascript setTimeout() fonksiyonu süre dolduğunda çağrılacak fonksiyonu çalıştırır ve bir daha çalıştırmaz. Süre parametresi millisecond (ms) olarak ifade edilmektedir. 1000 ms = 1 sec olarak kullanılır.

* StopAnimation(): Javascript clearInterval(interval) fonksiyonu ile animasyonun durdurulması sağlanmaktadır. Parametre olarak setInterval() fonksiyonu tarafından döndürülen değer kullanılmaktadır.

* AddPartToCanvas(): Jquery ile oluşturulan DOM nesnelerinin DOM'a eklenmesini sağlamaktadır.

* GenerateFood(): Rastgele bir pozisyona yem yerleştirilmesini sağlamaktadır. Bunu sağlarken Math.Random() fonskiyonunu kullanmaktadır. Bu fonksiyon 0 ile 1 arasında [0,1) bir reel sayı üretmektedir. Koordinatlar üretilirken kanvas uzunluğumuzun 200px ve aralıkların 5 px olduğu göz önünde bulundurularak elde edilen sayı 100 ile çarpılmakta ve 2 basamaklı rastgele bir sayı elde edilmektedir. Bu sayının 40'a göre modu alınarak rastgele 40'tan küçük bir sayı elde edilmiş olmaktadır. Elde edilen son değer 5 ile çarpılarak koordinat hesaplanmış olmaktadır. Yukarıdaki matematiksel işlemler incelendiğinde elde edilen rastgele sayının [0,200) aralığında olduğu görülebilmektedir.

* EatFood(): Bu fonksiyon ile yılanın baş parçasının konumunun yem konumuyla aynı olup olmadığı kontrol edilmekte ve aynı ise puan artırılmakta, yeni bir yem oluşturulmakta ve yılan büyütülmektedir.

* SGUpdateDirection(key): Okunan klavye tuşunun koduna göre yılanın yönünü belirleyen fonksiyondur.

* SGUpdate(): Diğer bütün fonksiyonların yönetimini sağlayan ve yılanın hareketinin mantıksal hesaplamasını yapan ana animasyon fonksiyonudur.

* SGUpdateCanvas(): SGUpdate() fonksiyonunda belirlenen oyunun yeni durumuna göre kanvası güncelleyen fonksiyondur.

* SGIsGameOver(): Yılanın kanvas dışına çıkıp çıkmadığına veya zaten işgal ettiği bir koordinata yeniden girmesine veya girmemesine göre oyunun bitip bitmediğinin anlaşılmasını sağlayan fonksiyondur.

* UpdateScore(): Kanvasın sağ üstünde bulunan mavi puan sayısını güncelleyen fonksiyondur.

* SGGameOver(): Oyun bittiğinde gerekli işlemlerin yapıldığı fonksiyondur. Oyun bittiğinde animasyon durdurulmakta ve "OYUN BİTTİ!" yazısını taşıyan flex kutusu kanvasa eklenmektedir.

Son aşama olarak HTML kodlarını tanımlayalım:

<div class="SGContainer">
    <div style="margin-bottom:10px">
        <input id="Button1" class="btn btn-success" type="button" value="BAŞLA" onclick="StartGame()"/>
        <span id="SGPuan" class="label label-info" style="float:right; font-size:large">0</span>
    </div>
    <div class="SGCanvas" tabindex="-1">

    </div>
</div>

Yukarıda yer alan kodların çalıştırılması ile aşağıdaki sonuç elde edilmektedir:
(Oyun ok tuşlarıyla oynanmaktadır)


0

No comments:

Post a Comment