Pengelolaan aset yang sederhana untuk game HTML5

Pengantar

HTML5 telah menyediakan banyak API yang berguna untuk membuat aplikasi web yang modern, responsif, dan canggih di browser. Ini bagus, tetapi Anda benar-benar ingin membuat dan memainkan game. Untungnya, HTML5 juga telah mengantarkan era baru pengembangan game yang menggunakan API seperti Canvas dan mesin JavaScript yang canggih untuk menghadirkan game langsung ke browser Anda tanpa memerlukan plugin.

Artikel ini akan memandu Anda membuat komponen Pengelolaan Aset sederhana untuk game HTML5. Tanpa pengelola aset, game Anda akan kesulitan mengimbangi waktu download yang tidak diketahui dan pemuatan gambar asinkron. Ikuti langkah-langkah berikut untuk melihat contoh pengelola aset sederhana untuk game HTML5 Anda.

Masalah

Game HTML5 tidak dapat mengasumsikan bahwa asetnya seperti gambar atau audio akan berada di komputer lokal pemain, karena game HTML5 menyiratkan bahwa game tersebut diputar di browser web dengan aset yang didownload melalui HTTP. Karena jaringan terlibat, browser tidak yakin kapan aset untuk game akan didownload dan tersedia.

Cara dasar untuk memuat gambar secara terprogram di browser web adalah kode berikut:

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

Sekarang bayangkan Anda memiliki seratus gambar yang perlu dimuat dan ditampilkan saat game dimulai. Bagaimana cara mengetahui kapan 100 gambar sudah siap? Apakah semuanya berhasil dimuat? Kapan game sebenarnya harus dimulai?

Solusi

Izinkan pengelola aset menangani antrean aset dan melaporkan kembali ke game saat semuanya sudah siap. Pengelola aset mengeneralisasi logika untuk memuat aset melalui jaringan, dan menyediakan cara mudah untuk memeriksa status.

Pengelola aset sederhana kami memiliki persyaratan berikut:

  • mengantrekan download
  • memulai download
  • melacak keberhasilan dan kegagalan
  • memberi sinyal saat semuanya selesai
  • pengambilan aset yang mudah

Antrean

Persyaratan pertamanya adalah mengantrekan download. Desain ini memungkinkan Anda mendeklarasikan aset yang diperlukan tanpa benar-benar mendownloadnya. Hal ini dapat berguna jika, misalnya, Anda ingin mendeklarasikan semua aset untuk level game dalam file konfigurasi.

Kode untuk konstruktor dan antrean terlihat seperti:

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

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

Mulai Download

Setelah mengantrekan semua aset yang akan didownload, Anda dapat meminta pengelola aset untuk mulai mendownload semuanya.

Untungnya, browser web dapat melakukan paralelisasi download—biasanya hingga 4 koneksi per host. Salah satu cara untuk mempercepat download aset adalah dengan menggunakan berbagai nama domain untuk hosting aset. Misalnya, alih-alih menayangkan semuanya dari assets.example.com, coba gunakan assets1.example.com, assets2.example.com, assets3.example.com, dan seterusnya. Meskipun setiap nama domain tersebut hanyalah CNAME ke server web yang sama, browser web melihatnya sebagai server terpisah dan meningkatkan jumlah koneksi yang digunakan untuk mendownload aset. Pelajari teknik ini lebih lanjut dari Memisahkan Komponen di Seluruh Domain di Praktik Terbaik untuk Mempercepat Situs Anda.

Metode kami untuk inisialisasi download disebut downloadAll(). Kami akan membangunnya dari waktu ke waktu. Untuk saat ini, berikut adalah logika pertama untuk memulai download.

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;
    }
}

Seperti yang dapat Anda lihat dalam kode di atas, downloadAll() hanya melakukan iterasi melalui downloadQueue dan membuat objek Image baru. Pemroses peristiwa untuk peristiwa pemuatan ditambahkan dan src gambar ditetapkan, yang memicu download sebenarnya.

Dengan metode ini, Anda dapat memulai download.

Melacak Keberhasilan dan Kegagalan

Persyaratan lainnya adalah melacak keberhasilan dan kegagalan, karena sayangnya tidak semuanya selalu berjalan dengan sempurna. Sejauh ini, kode hanya melacak aset yang berhasil didownload. Dengan menambahkan pemroses peristiwa untuk peristiwa error, Anda akan dapat merekam skenario keberhasilan dan kegagalan.

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;
  }
}

Pengelola aset perlu mengetahui jumlah keberhasilan dan kegagalan yang kita alami, atau ia tidak akan pernah tahu kapan game dapat dimulai.

Pertama, kita akan menambahkan penghitung ke objek di konstruktor, yang sekarang terlihat seperti ini:

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

Selanjutnya, tambahkan penghitung di pemroses peristiwa, yang sekarang terlihat seperti ini:

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

Pengelola aset kini melacak aset yang berhasil dimuat dan aset yang gagal.

Memberi Sinyal Saat Selesai

Setelah game mengantrekan asetnya untuk didownload, dan meminta pengelola aset untuk mendownload semua aset, game harus diberi tahu saat semua aset didownload. Alih-alih game terus-menerus menanyakan apakah aset telah didownload, pengelola aset dapat memberikan sinyal kembali ke game.

Pengelola aset harus mengetahui terlebih dahulu kapan setiap aset selesai. Sekarang kita akan menambahkan metode isDone:

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

Dengan membandingkan successCount + errorCount dengan ukuran downloadQueue, pengelola aset mengetahui apakah setiap aset berhasil diselesaikan atau mengalami error.

Tentu saja, mengetahui apakah aset telah ditransfer atau belum hanyalah setengah dari perjuangan; pengelola aset juga perlu memeriksa metode ini. Kita akan menambahkan pemeriksaan ini di dalam kedua pengendali peristiwa, seperti yang ditunjukkan kode di bawah:

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);

Setelah penghitung bertambah, kita akan melihat apakah itu adalah aset terakhir dalam antrean. Jika pengelola aset memang sudah selesai didownload, apa yang harus kita lakukan?

Jika pengelola aset selesai mendownload semua aset, tentu saja kita akan memanggil metode callback. Mari kita ubah downloadAll() dan tambahkan parameter untuk callback:

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

Kita akan memanggil metode downloadCallback di dalam pemroses peristiwa:

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

Pengelola aset akhirnya siap untuk persyaratan terakhir.

Pengambilan Aset yang Mudah

Setelah game diberi sinyal bahwa game dapat dimulai, game akan mulai merender gambar. Pengelola aset tidak hanya bertanggung jawab untuk mendownload dan melacak aset, tetapi juga menyediakannya ke game.

Persyaratan akhir kita menyiratkan semacam metode getAsset, jadi kita akan menambahkannya sekarang:

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

Objek cache ini diinisialisasi di konstruktor, yang sekarang terlihat seperti ini:

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

Cache diisi di akhir downloadAll(), seperti yang ditunjukkan di bawah ini:

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: Perbaikan Bug

Apakah Anda melihat bug-nya? Seperti yang ditulis di atas, metode isDone hanya dipanggil saat peristiwa pemuatan atau error dipicu. Namun, bagaimana jika pengelola aset tidak memiliki aset yang diantrekan untuk didownload? Metode isDone tidak pernah dipicu, dan game tidak pernah dimulai.

Anda dapat mengakomodasi skenario ini dengan menambahkan kode berikut ke downloadAll():

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

Jika tidak ada aset yang diantrekan, callback akan segera dipanggil. Bug telah diperbaiki.

Contoh Penggunaan

Menggunakan pengelola aset ini di game HTML5 Anda cukup mudah. Berikut adalah cara paling dasar untuk menggunakan library:

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);
});

Kode di atas menggambarkan:

  1. Membuat pengelola aset baru
  2. Mengantrekan aset yang akan didownload
  3. Mulai download dengan downloadAll()
  4. Memberi sinyal saat aset siap dengan memanggil fungsi callback
  5. Mengambil aset dengan getAsset()

Area yang Perlu Ditingkatkan

Anda pasti akan membutuhkan pengelola aset yang lebih canggih saat membuat game, meskipun kami harap pengelola aset sederhana ini dapat menjadi awal yang baik. Fitur mendatang dapat mencakup:

  • memberi tahu aset mana yang mengalami error
  • callback untuk menunjukkan progres
  • mengambil aset dari File System API

Posting peningkatan, fork, dan link ke kode di komentar di bawah.

Sumber Lengkap

Sumber untuk pengelola aset ini, dan game yang di-abstrak, adalah open source berdasarkan Apache License dan dapat ditemukan di akun GitHub Bad Aliens. Game Bad Aliens dapat dimainkan di browser yang kompatibel dengan HTML5. Game ini adalah subjek untuk presentasi Google IO saya yang berjudul Super Browser 2 Turbo HD Remix: Pengantar Pengembangan Game HTML5 (slide, video).

Ringkasan

Sebagian besar game memiliki semacam pengelola aset, tetapi game HTML5 memerlukan pengelola aset yang memuat aset melalui jaringan dan menangani kegagalan. Artikel ini menguraikan pengelola aset sederhana yang mudah digunakan dan disesuaikan untuk game HTML5 Anda berikutnya. Selamat mencoba, dan sampaikan pendapat Anda melalui komentar di bawah. Terima kasih.