إدارة بسيطة للأصول في ألعاب HTML5

مقدمة

وفّرت لغة HTML5 العديد من واجهات برمجة التطبيقات المفيدة لإنشاء تطبيقات ويب حديثة ومتوافقة مع الأجهزة الجوّالة وفعّالة في المتصفّح. هذا رائع، ولكنك تريد حقًا إنشاء ألعاب ولعبها. لحسن الحظ، أدّى HTML5 أيضًا إلى بدء حقبة جديدة من تطوير الألعاب التي تستخدم واجهات برمجة التطبيقات، مثل Canvas ومحركات JavaScript القوية، لعرض الألعاب مباشرةً في المتصفّح بدون الحاجة إلى الإضافات.

سترشدك هذه المقالة إلى كيفية إنشاء مكوّن إدارة مواد عرض بسيط للعبة HTML5. بدون مدير مواد العرض، ستواجه لعبتك صعوبة في تعويض أوقات التنزيل غير المعروفة وتحميل الصور غير المتزامن. تابِع معنا للاطّلاع على مثال لإدارة مواد عرض بسيطة لألعاب HTML5.

المشكلة

لا يمكن لألعاب HTML5 افتراض أنّ مواد العرض، مثل الصور أو المحتوى الصوتي، ستكون متوفّرة على جهاز اللاعب، لأنّ ألعاب HTML5 تشير إلى أنّها يتم تشغيلها في متصفّح ويب مع مواد عرض تم تنزيلها عبر بروتوكول HTTP. وبما أنّ الشبكة متورّطة في عملية التنزيل، لا يعرف المتصفّح متى سيتم تنزيل مواد عرض اللعبة ومدى توفّرها.

الطريقة الأساسية لتحميل صورة آليًا في متصفّح ويب هي الرمز التالي:

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

الآن، تخيل أنّ لديك مائة صورة يجب تحميلها وعرضها عند بدء تشغيل اللعبة. كيف يمكنك معرفة ما إذا كانت كل الصور المائة جاهزة؟ هل تم تحميلها كلها بنجاح؟ متى يجب أن تبدأ المباراة فعليًا؟

الحلّ

يمكنك أن تطلب من مدير مواد العرض ترتيب مواد العرض في قائمة الانتظار وإبلاغك عندما يصبح كل شيء جاهزًا. يُعمِّم مدير مواد العرض منطق تحميل مواد العرض عبر الشبكة، ويقدّم طريقة سهلة للتحقّق من الحالة.

يجب استيفاء المتطلبات التالية لاستخدام أداة إدارة مواد العرض البسيطة:

  • إضافة المحتوى إلى "قائمة المحتوى التالي"
  • بدء عمليات التنزيل
  • تتبُّع حالات النجاح والفشل
  • إرسال إشارة عند اكتمال كل شيء
  • إمكانية استرجاع مواد العرض بسهولة

الإضافة إلى قائمة المحتوى التالي

الشرط الأول هو إضافة المحتوى الذي تريد تنزيله إلى "قائمة المحتوى التالي". يتيح لك هذا التصميم الإفصاح عن مواد العرض التي تحتاج إليها بدون تنزيلها فعليًا. يمكن أن يكون ذلك مفيدًا إذا كنت تريد مثلاً الإفصاح عن جميع مواد العرض لمستوى لعبة في ملف إعدادات.

يظهر رمز الدالة الإنشائية ووضع الطلبات في قائمة الانتظار على النحو التالي:

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

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

بدء عمليات التنزيل

بعد إضافة جميع مواد العرض التي تريد تنزيلها إلى "قائمة المحتوى التالي"، يمكنك أن تطلب من مدير مواد العرض بدء تنزيل كل المحتوى.

لحسن الحظ، يمكن لمتصفّح الويب إجراء عمليات التنزيل بشكل موازٍ، وعادةً ما يصل إلى 4 عمليات اتصال لكل مضيف. من الطرق التي تُسرع من تنزيل مواد العرض استخدام مجموعة من أسماء النطاقات لاستضافة مواد العرض. على سبيل المثال، بدلاً من عرض كل المحتوى من assets.example.com، جرِّب استخدام assets1.example.com وassets2.example.com وassets3.example.com وما إلى ذلك. حتى إذا كان كل اسم من أسماء النطاقات هذه هو مجرد CNAME لخادم الويب نفسه، يعرِض متصفّح الويب هذه الأسماء كنواسخ خادم منفصلة ويزيد عدد عمليات الربط المستخدَمة لتنزيل مواد العرض. يمكنك الاطّلاع على مزيد من المعلومات حول هذه التقنية من خلال تقسيم المكوّنات على مستوى النطاقات في مقالة "أفضل الممارسات لتحسين سرعة موقعك الإلكتروني".

تُعرف طريقة بدء التنزيل باسم downloadAll(). سنعمل على تطويرها بمرور الوقت. في الوقت الحالي، إليك المنطق الأول لبدء عمليات التنزيل.

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

كما هو موضّح في الرمز أعلاه، يكرّر downloadAll() ببساطة قائمة التحميل وينشئ عنصر صورة جديدًا. تتم إضافة أداة معالجة أحداث لحدث التحميل ويتم ضبط src للصورة، ما يؤدي إلى بدء عملية التنزيل الفعلية.

باستخدام هذه الطريقة، يمكنك بدء عمليات التنزيل.

تتبُّع حالات النجاح والفشل

من المتطلبات الأخرى تتبُّع كلّ من حالات النجاح والفشل، لأنّه لا تنجح كلّ الخطط دائمًا على النحو المطلوب. لا يتتبّع الرمز حتى الآن سوى مواد العرض التي تم تنزيلها بنجاح. من خلال إضافة مستمع أحداث لحدث الخطأ، ستتمكّن من تسجيل سيناريوهات النجاح والإخفاق.

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

يحتاج مدير مواد العرض إلى معرفة عدد مرات النجاح والفشل التي واجهناها، وإلا لن يعرف أبدًا متى يمكن بدء اللعبة.

أولاً، سنضيف العدادات إلى الكائن في الدالة الإنشائية، التي تبدو الآن على النحو التالي:

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

بعد ذلك، عليك زيادة العدّادات في أدوات معالجة الأحداث، التي ستبدو الآن على النحو التالي:

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

يتتبّع مدير مواد العرض الآن كلّ من مواد العرض التي تم تحميلها بنجاح وتلك التي تعذّر تحميلها.

إرسال إشارة عند الانتهاء

بعد أن تضيف اللعبة مواد العرض إلى "قائمة المحتوى التالي" لتنزيلها، تطلب من مدير مواد العرض تنزيل كل مواد العرض، ويجب إبلاغ اللعبة عند تنزيل كل مواد العرض. بدلاً من أن تسأل اللعبة مرارًا وتكرارًا عما إذا تم تنزيل مواد العرض، يمكن لمدير مواد العرض إرسال إشارة إلى اللعبة.

يجب أن يعرف مدير مواد العرض أولاً وقت اكتمال كل مادة عرض. سنضيف الآن طريقة isDone:

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

من خلال مقارنة عدد عمليات التحميل الناجحة بعدد الأخطاء بحجم "قائمة التحميل"، يعرف مدير مواد العرض ما إذا كانت كل مادة عرض قد اكتملت بنجاح أو حدث بها نوع من الأخطاء.

بالطبع، لا يكفي معرفة ما إذا كان قد تمّ الانتهاء من ذلك، بل يجب أن يتحقّق مدير مواد العرض أيضًا من هذه الطريقة. سنضيف هذا التحقّق داخل كلتا معالجتَي الأحداث، كما يوضّح الرمز البرمجي أدناه:

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

بعد زيادة العدّادات، سنرى ما إذا كانت هذه هي مادة العرض الأخيرة في قائمة الانتظار. إذا انتهى مدير مواد العرض من التنزيل، ما الذي يجب علينا فعله بالضبط؟

إذا انتهى مدير مواد العرض من تنزيل جميع مواد العرض، سنستدعي بالطبع طريقة طلب معاودة الاتصال. لنغيّر downloadAll() ونضيف مَعلمة لدالة ردّ الاتصال:

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

سنستدعي طريقة downloadCallback داخل أدوات معالجة الأحداث:

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

أصبح مدير مواد العرض مستعدًا أخيرًا لاستيفاء الشرط الأخير.

استرداد مواد العرض بسهولة

بعد أن يتم إرسال إشارة إلى اللعبة بأنّه يمكنها البدء، ستبدأ اللعبة في عرض الصور. لا يتحمّل مدير مواد العرض مسؤولية تنزيل مواد العرض وتتبُّعها فقط، بل يتحمّل أيضًا مسؤولية توفيرها للّعبة.

يشير الشرط النهائي إلى نوع من طريقة getAsset، لذا سنضيفها الآن:

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

يتمّ إعداد عنصر ذاكرة التخزين المؤقت هذا في الدالة المُنشئ التي تبدو الآن على النحو التالي:

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

تتم تعبئة ذاكرة التخزين المؤقت في نهاية downloadAll()، كما هو موضّح أدناه:

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

ميزة إضافية: إصلاح الأخطاء

هل لاحظت الخطأ؟ كما هو موضّح أعلاه، لا يتمّ استدعاء طريقة isDone إلا عند بدء أحداث load أو error. ولكن ماذا لو لم يكن لدى مدير مواد العرض أي مواد عرض في انتظار التنزيل؟ لا يتم تشغيل الطريقة isDone مطلقًا، ولا تبدأ اللعبة أبدًا.

يمكنك تلبية هذا السيناريو عن طريق إضافة الرمز التالي إلى downloadAll():

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

إذا لم يتم إدراج أي مواد عرض في "قائمة الانتظار"، يتمّ استدعاء دالة "الردّ على المكالمة" على الفور. تم إصلاح الخطأ.

مثال على الاستخدام

إنّ استخدام أداة إدارة مواد العرض هذه في لعبة HTML5 أمر بسيط للغاية. في ما يلي الطريقة الأساسية لاستخدام المكتبة:

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

يوضّح الرمز البرمجي أعلاه ما يلي:

  1. لإنشاء مدير مواد عرض جديد
  2. إضافة مواد العرض إلى "قائمة المحتوى التالي" لتنزيلها
  3. بدء عمليات التنزيل باستخدام downloadAll()
  4. الإشارة إلى جاهزية مواد العرض من خلال استدعاء دالة ردّ الاتصال
  5. استرداد مواد العرض باستخدام getAsset()

مجالات التحسين

لا شكّ أنّك ستحتاج إلى أداة أكثر تقدّمًا لإدارة مواد العرض أثناء إنشاء لعبتك، ولكن آمل أن تكون هذه الأداة قد وفّرت لك بداية أساسية. يمكن أن تتضمّن الميزات المستقبلية ما يلي:

  • الإشارة إلى مادة العرض التي حدث بها خطأ
  • عمليات معاودة الاتصال للإشارة إلى مستوى التقدّم
  • استرداد مواد العرض من واجهة برمجة التطبيقات File System API

يُرجى نشر التحسينات وعمليات التطوير والروابط إلى الرموز البرمجية في التعليقات أدناه.

المصدر الكامل

إنّ مصدر أداة إدارة مواد العرض هذه واللعبة المستندة إليها مفتوح المصدر بموجب ترخيص Apache، ويمكن العثور عليه في حساب Bad Aliens على GitHub. يمكن تشغيل لعبة Bad Aliens في متصفّح متوافق مع HTML5. كانت هذه اللعبة موضوع محاضرتي في Google IO بعنوان Super Browser 2 Turbo HD Remix: Introduction to HTML5 Game Development (العروض التقديمية، الفيديو).

ملخّص

تحتوي معظم الألعاب على نوع من مدراء مواد العرض، ولكن تتطلّب ألعاب HTML5 مدير مواد عرض يحمّل مواد العرض عبر شبكة ويتولّى التعامل مع حالات الفشل. لقد وضّحت هذه المقالة أداة إدارة مواد عرض بسيطة من المفترض أن يكون من السهل عليك استخدامها وتعديلها لتناسب لعبتك التالية المستندة إلى HTML5. نتمنّى لك تجربة ممتعة، ويُرجى إعلامنا برأيك في التعليقات أدناه. شكرًا!