HTML5 oyunları için basit öğe yönetimi

Giriş

HTML5, tarayıcıda modern, duyarlı ve güçlü web uygulamaları oluşturmak için birçok faydalı API sunar. Çok iyi ama gerçekten oyun geliştirip oynamak istiyorsunuz! Neyse ki HTML5, eklenti kullanmadan doğrudan tarayıcınıza oyun sunmak için Canvas gibi API'leri ve güçlü JavaScript motorlarını kullanan oyun geliştirmede yeni bir çağa da öncülük etti.

Bu makale, HTML5 oyununuz için basit bir Öğe Yönetimi bileşeni oluşturma konusunda size yol gösterecektir. Öğe yöneticisi olmadan oyununuzun bilinmeyen indirme sürelerini ve eşzamansız resim yüklemesini dengelemesi zor olur. HTML5 oyunlarınıza yönelik basit bir öğe yöneticisi örneğini görmek için aşağıdaki talimatları uygulayın.

Sorun

HTML5 oyunları, öğelerin HTTP üzerinden indirilerek bir web tarayıcısında oynandığını ima ettiğinden resim veya ses gibi öğelerin oynatıcının yerel makinesinde olacağını varsayamaz. Ağ işiyle bağlantılı olduğu için tarayıcı, oyun öğelerinin ne zaman indirilip kullanılabilir olacağından emin değil.

Bir web tarayıcısına programlı bir şekilde resim yüklemenin temel yolu aşağıdaki koddur:

var image = new Image();
image.addEventListener("success", function(e) {
  // do stuff with the image
});
image.src = "/some/image.png";

Şimdi, oyun başladığında yüklenmesi ve görüntülenmesi gereken yüz adet resminiz olduğunu düşünün. 100 resmin tamamının hazır olduğunu nasıl anlarsınız? Tümü başarıyla yüklendi mi? Oyun aslında ne zaman başlamalı?

Çözüm

Öğelerin sıraya alınması işlemini bir öğe yöneticisinin yapmasına izin verin ve her şey hazır olduğunda oyuna geri bildirimde bulunun. Öğe yöneticisi, öğeleri ağ üzerinden yükleme mantığını geneller ve durumu kontrol etmek için kolay bir yol sağlar.

Basit öğe yöneticimiz aşağıdaki koşullara sahiptir:

  • indirmeleri sıraya al
  • indirme işlemlerini başlat
  • Başarı ve başarısızlığı izleme
  • işlem tamamlandığında sinyalini
  • öğeleri kolayca alma

Sıraya alınıyor

İlk şart, indirmeleri sıraya koymaktır. Bu tasarım, ihtiyacınız olan öğeleri gerçekten indirmeden tanımlayabilmenizi sağlar. Bu özellik, örneğin bir yapılandırma dosyasında bir oyun seviyesi için tüm öğeleri bildirmek istiyorsanız yararlı olabilir.

Oluşturucu ve sıralama için kod şöyle görünür:

function AssetManager() {
  this.downloadQueue = [];
}

AssetManager.prototype.queueDownload = function(path) {
    this.downloadQueue.push(path);
}

İndirme işlemlerini başlat

İndirilecek tüm öğeleri sıraya aldıktan sonra, öğe yöneticisinden her şeyi indirmeye başlamasını isteyebilirsiniz.

Neyse ki, web tarayıcısı indirmeleri paralel yapabilir; bu da genellikle ana makine başına en fazla 4 bağlantı içerir. Öğe indirme işlemini hızlandırmanın bir yolu öğe barındırmak için çeşitli alan adları kullanmaktır. Örneğin, assets.example.com adresindeki her şeyi yayınlamak yerine assets1.example.com, assets2.example.com, assets3.example.com ve benzeri yerleri kullanmayı deneyin. Bu alan adlarının her biri aynı web sunucusuna yapılan bir CNAME olsa bile, web tarayıcısı bunları ayrı sunucular olarak görür ve öğe indirmek için kullanılan bağlantı sayısını artırır. Web Sitenizi Hızlandırmak İçin En İyi Uygulamalar sayfasındaki Alanlar Arasında Bileşenleri Böl bölümünden bu teknik hakkında daha fazla bilgi edinebilirsiniz.

İndirmeyi başlatma yöntemimize downloadAll() adı verilir. Zamanla ekleyeceğiz. Şimdilik indirme işlemlerini başlatacak ilk mantığı burada bulabilirsiniz.

AssetManager.prototype.downloadAll = function() {
    for (var i = 0; i < this.downloadQueue.length; i++) {
        var path = this.downloadQueue[i];
        var img = new Image();
        var that = this;
        img.addEventListener("load", function() {
            // coming soon
        }, false);
        img.src = path;
    }
}

Yukarıdaki kodda görebileceğiniz gibi downloadAll(), downloadQueue aracılığıyla yinelenir ve yeni bir Resim nesnesi oluşturur. Yükleme etkinliği için bir etkinlik işleyici eklenir ve resmin src özelliği ayarlanır. Bu, gerçek indirme işlemini tetikler.

Bu yöntemle indirme işlemlerini başlatabilirsiniz.

Başarı ve Başarısızlığı İzleme

Başka bir gereklilik de hem başarıları hem de başarısızlıkları izlemektir. Ne yazık ki her şey her zaman kusursuz çalışmayabilir. Kod, şu ana kadar yalnızca başarıyla indirilen öğeleri izler. Hata etkinliği için bir etkinlik işleyici ekleyerek hem başarı hem de başarısız senaryoları yakalayabilirsiniz.

AssetManager.prototype.downloadAll = function(downloadCallback) {
  for (var i = 0; i < this.downloadQueue.length; i++) {
    var path = this.downloadQueue[i];
    var img = new Image();
    var that = this;
    img.addEventListener("load", function() {
        // coming soon
    }, false);
    img.addEventListener("error", function() {
        // coming soon
    }, false);
    img.src = path;
  }
}

Öğe yöneticimizin kaç başarı ve başarısızlıkla karşılaştığımızı bilmesi gerekir. Aksi takdirde oyunun ne zaman başlayacağını hiçbir zaman bilemez.

İlk olarak, sayaçları oluşturucudaki nesneye ekleyeceğiz. Bu nesne şu şekilde görünür:

function AssetManager() {
<span class="highlight">    this.successCount = 0;
    this.errorCount = 0;</span>
    this.downloadQueue = [];
}

Ardından, etkinlik işleyicilerdeki sayaçları artırın. Bunlar artık şu şekilde görünür:

img.addEventListener("load", function() {
    <span class="highlight">that.successCount += 1;</span>
}, false);
img.addEventListener("error", function() {
    <span class="highlight">that.errorCount += 1;</span>
}, false);

Öğe yöneticisi artık hem başarıyla yüklenen hem de başarısız olan öğeleri izlemektedir.

İşlem Tamamlandığında Sinyal Veriliyor

Oyun, öğelerini indirilmek üzere sıraya aldıktan ve öğe yöneticisinden tüm öğeleri indirmesini istedikten sonra, tüm öğeler indirildiğinde oyuna bilgi verilmesi gerekir. Öğe yöneticisi, oyun için tekrar tekrar öğe indirilip indirilmediğini sormak yerine oyuna geri dönebilir.

Öğe yöneticisinin öncelikle tüm öğelerin ne zaman tamamlandığını bilmesi gerekir. Şimdi bir isDone yöntemi ekleyeceğiz:

AssetManager.prototype.isDone = function() {
    return (this.downloadQueue.length == this.successCount + this.errorCount);
}

Öğe yöneticisi, successCount + errorCount değerini downloadQueue'nun boyutuyla karşılaştırarak her öğenin başarılı bir şekilde bitip bitmediğini veya bir hata yapıp yapmadığını bilir.

Bir şeyin tamamlanıp tamamlanmadığını bilmek işin sadece yarısıdır; varlık yöneticisinin de bu yöntemi kontrol etmesi gerekir. Bu denetimi, aşağıdaki kodda gösterildiği gibi her iki etkinlik işleyicimize de ekleyeceğiz:

img.addEventListener("load", function() {
    console.log(this.src + ' is loaded');
    that.successCount += 1;
    if (that.isDone()) {
        // ???
    }
}, false);
img.addEventListener("error", function() {
    that.errorCount += 1;
if (that.isDone()) {
        // ???
    }
}, false);

Sayaçlar artırıldıktan sonra, bunun sıramızdaki son öğe olup olmadığına bakacağız. Öğe yöneticisi indirmeyi gerçekten tamamlamışsa tam olarak ne yapmamız gerekir?

Öğe yöneticisi tüm öğeleri indirmeyi bitirmişse, tabii ki bir geri çağırma yöntemi çağırırız! downloadAll() öğesini değiştirelim ve geri çağırma için bir parametre ekleyelim:

AssetManager.prototype.downloadAll = function(downloadCallback) {
    ...

Etkinlik işleyicilerimizin içinde downloadCallback yöntemini çağıracağız:

img.addEventListener("load", function() {
    that.successCount += 1;
    if (that.isDone()) {
        downloadCallback();
    }
}, false);
img.addEventListener("error", function() {
    that.errorCount += 1;
    if (that.isDone()) {
        downloadCallback();
    }
}, false);

Öğe yöneticisi nihayet son gereksinim için hazırdır.

Öğeleri Kolay Alma

Oyunun başlatılabileceği sinyali verildikten sonra oyun, resimleri oluşturmaya başlar. Öğe yöneticisi yalnızca öğeleri indirmekten ve izlemekten değil, aynı zamanda bunları oyuna sağlamaktan da sorumludur.

Son şartımız bir tür getAsset yöntemini gerektirdiğinden bunu şimdi ekleyeceğiz:

AssetManager.prototype.getAsset = function(path) {
    return this.cache[path];
}

Bu önbellek nesnesi, oluşturucuda başlatılır ve şu şekilde görünür:

function AssetManager() {
    this.successCount = 0;
    this.errorCount = 0;
    this.cache = {};
    this.downloadQueue = [];
}

Önbellek, aşağıda gösterildiği gibi downloadAll() sonunda doldurulur:

AssetManager.prototype.downloadAll = function(downloadCallback) {
  ...
      img.addEventListener("error", function() {
          that.errorCount += 1;
          if (that.isDone()) {
              downloadCallback();
          }
      }, false);
      img.src = path;
      <span class="highlight">this.cache[path] = img;</span>
  }
}

Bonus: Hata Düzeltmesi

Hatayı fark ettiniz mi? Yukarıda yazıldığı gibi, isDone yöntemi yalnızca yükleme veya hata etkinlikleri tetiklendiğinde çağrılır. Peki, öğe yöneticisinin indirilmek üzere sıraya alınmış bir öğesi yoksa ne olur? IsDone yöntemi hiçbir zaman tetiklenmez ve oyun hiç başlamıyor.

Aşağıdaki kodu downloadAll() alanına ekleyerek bu senaryonun üstesinden gelebilirsiniz:

AssetManager.prototype.downloadAll = function(downloadCallback) {
    if (this.downloadQueue.length === 0) {
      downloadCallback();
  }
 ...

Sırada hiçbir öğe yoksa geri çağırma hemen çağrılır. Hata düzeltildi.

Örnek Kullanım

HTML5 oyununuzda bu öğe yöneticisini kullanmak oldukça basittir. Kitaplığı kullanmanın en temel yolu şöyledir:

var ASSET_MANAGER = new AssetManager();

ASSET_MANAGER.queueDownload('img/earth.png');

ASSET_MANAGER.downloadAll(function() {
    var sprite = ASSET_MANAGER.getAsset('img/earth.png');
    ctx.drawImage(sprite, x - sprite.width/2, y - sprite.height/2);
});

Yukarıdaki kod aşağıdakileri gösterir:

  1. Yeni bir öğe yöneticisi oluşturur
  2. İndirilecek öğeleri sıraya ekleyin
  3. downloadAll() ile indirme işlemlerine başlayın
  4. Geri çağırma işlevini çağırarak öğeler hazır olduğunda sinyal verin
  5. getAsset() ile öğe alın

Geliştirilebilecek Alanlar

Oyununuzu geliştirirken bu basit öğe yöneticisini aşmayacağınızdan şüpheniz olmasın. Umarım temel bir başlangıç sağlamıştır. Gelecekteki özellikler arasında şunlar bulunabilir:

  • hangi öğede hata olduğunun sinyalini verir
  • geri çağırma (geri çağırma)
  • File System API'den öğeler alınıyor

Lütfen iyileştirmeleri, çatalları ve kod bağlantılarını aşağıdaki yorumlar bölümüne yazın.

Tam Kaynak

Bu öğe yöneticisinin ve soyutlandığı oyunun kaynağı, Apache Lisansı kapsamında açık kaynaktır ve Bad Uzaylılar GitHub hesabında bulunabilir. Bad Aliens oyununu HTML5 uyumlu tarayıcınızda oynayabilirsiniz. Super Tarayıcı 2 Turbo HD Remix: HTML5 Oyun Geliştirmeye Giriş (slaytlar, video) başlıklı Google IO konuşmam bu oyundu.

Özet

Çoğu oyunun bir tür öğe yöneticisi vardır, ancak HTML5 oyunlarında öğeleri bir ağ üzerinden yükleyen ve hataları işleyen bir öğe yöneticisi gerekir. Bu makalede, bir sonraki HTML5 oyununuz için kolayca kullanabileceğiniz ve uyarlayabileceğiniz basit bir öğe yöneticisi ana hatlarıyla ele alınmıştır. İyi eğlenceler! Lütfen aşağıdaki yorumlar bölümünde düşüncelerinizi bizimle paylaşın. Teşekkürler.