اذهب إلى المحتوى

يُطلب منك في هذا التمرين التطبيقي أن تستخدم تطبيق الكرات المرتدة الذي بنيناه في المقال السابق كنقطة انطلاق ومن ثم إضافة بعض الميزات المهمة اﻷخرى.

كما ننصحك قبل محاول العمل على هذا التمرين الاطلاع على سلسلة المقالات السابقة التي تشرح مبادئ البرمجة بالكائنات في جافا سكريبت.

نقطة الانطلاق

انسخ بداية الملفات index-finished.html و style.css و main-finished.js إلى حاسوبك -وهي ملفات تخص التمرين التطبيقي في المقال السابق- واحفظها في مجلد جديد. كما يمكنك استخدام محررات على اﻹنترنت مثل CodePen و JSFiddle و Glitch. إذ تسمح هذه المحررات بلصق شيفرة HTML و CSS ضمن المحرر، لكن إن لم يكن للمحرر الذي تستخدمه نافذة مخصصة شفرة جافا سكريبت، بإمكانك حينها وضع الشيفرة داخل عنصر <script> في صفحة HTML.

نصائح وتلميحات

إليك بعض المؤشرات التي تخص التطبيق قبل أن تبدأ:

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

موجز المشروعتأكل

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

تعرض الصورة التالية مثالًا عما سيكونه التطبيق عند إنجازه:

01 bouncing evil circle

ولتكوّن فكرة أوضح، ألقِ نظرة على التطبيق المكتمل لكن لا تختلس النظر إلى شيفرته!

الخطوات التي عليك إكمالها

تصف اﻷقسام التالية ما عليك إنجازه.

إنشاء الصنف shape

أنشئ قبل كل شيء الصنف Shape. ويمتلك هذا الصنف دالة بانية فقط تُعرّف الخاصيات x و y و velX و velY كما تعرّفها الدالة البانية للصنف ()Ball، لكن دون الخاصيتين color و size.

يجب أن يرث الصنف Ball الكائن Shape باستخدام التعليمة extends، وينبغي للدالة البانية الخاصة بالصنف Ball أن:

  • تأخذ نفس الوسطاء كما هو حالها سابقًا: x و y و velX و velY و size و color.
  • استدعاء الدالة البانية للصنف باستخدام اﻷمر ()super، وتمرير الوسطاء x و y و velX و velY إليها.
  • تهيئة الخاصيتين color و size من القيم التي تُمرر إليها.

وعلى الصنف Ball تعريف خاصية جديدة تُدعى exists التي تُستخدم لتتبع وضع الكرة إن كانت لا تزال في البرنامج (لم تؤكل بعد). وينبغي أن تأخذ الخاصية قيم منطقية (true / false) وتُهيأ بالقيمة true.

كما يحتاج التابع ()collisionDetect في الصنف Ball إلى تحديث طفيف، وهو ألا تؤخذ الكرة في الحسبان عند حساب التصادمات إن لم تكن موجودة (أي قيمة الخاصية exists لها هي true). لهذا عليك استبدال شيفرة الدالة ()collisionDetect

collisionDetect() {
  for (const ball of balls) {
    if (!(this === ball) && ball.exists) {
      const dx = this.x - ball.x;
      const dy = this.y - ball.y;
      const distance = Math.sqrt(dx * dx + dy * dy);

      if (distance < this.size + ball.size) {
        ball.color = this.color = randomRGB();
      }
    }
  }
}

وكما ناقشنا سابقًا، ستكون اﻹضافة الوحيدة للتابع هو التحقق من كون الكرة موجودة وذلك باستخدام العبارة ball.exists ضمن الشرط if. وينبغي ألا تغيّر في تعريف التابعين أي شيء ويجب أن يبقيا تمامًا كما عرّفناهما سابقًا.

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

تعريف الكرة الشريرة

حان الوقت للعمل مع الكرة الشريرة والصنف EvilCircle، وسيضم تطبيقنا كرة واحدة من هذا الصنف. سنعرّف الصنف باستخدام دالة بانية ترث من الصنف Shapeوذلك ﻹكسابك بعض المهارة. فقد ترغب لاحقًا بإضافة كرة شريرة ثانية يتحكم بها لاعب آخر، أو مجموعة من الكرات الشريرة التي يتحكم بها الحاسوب. وتذكر أننا نحتاج كرة شريرة واحدة فقط في تطبيقنا.

أنشئ إذًا تعريفًا للصنف EvilCircle واجعله يرث من الصنف Shape باستخدام extends.

الدالة البانية للصنف EvilCircle

ينبغي أن تتميز الدالة البانية بما يلي:

  • يُمرر لها الوسيطان xو y.
  • تُمرر الوسيطين إلى الدالة البانية للصنف اﻷب ()Shape مع القيمتين velx و velY واللتان تُمررا كقيم ثابتة 20، أي عليك كتابة الشيفرة كالتالي super(x,v,20,20).
  • اضبط اللون color على الأبيض white وقياس الكرة size على القيمة 10.

وأخيرًا لا بد للدالة البانية من إعداد الشيفرة ليتمكن المستخدم من تحريك الكرة على الشاشة:

window.addEventListener("keydown", (e) => {
  switch (e.key) {
    case "a":
      this.x -= this.velX;
      break;
    case "d":
      this.x += this.velX;
      break;
    case "w":
      this.y -= this.velY;
      break;
    case "s":
      this.y += this.velY;
      break;
  }
});

تضيف الشيفرة السابقة مترصّد لحدث الضغط على مفتاح keydown إلى الكائن window، وبالتالي، عندما يُضغط مفتاح يتفقد المتصفح قيمة الخاصية key لكائن الحدث لمعرفة الزر الذي ضُغط.فإن كان أحد المفاتيح اﻷربعة المخصصة، تتحرك الكرة إلى اليمين أو اليسار أو اﻷعلى أو الأسفل.

تعريف توابع الكائن EvilCircle

للصنف ثلاث توابع نشرحها تاليًا.

التابع ()draw

وله نفس غاية التابع ()draw في الصنف Ball الذي يرسم نسخة من الكائن على اللوحة. يعمل التابع في الصنف EvilCircle بنفس الطريقة، لهذا بإمكانك نسخ شيفرة التابع ()draw للصنف Ball ومن ثم إجراء التعديلات التالية:

  • لا نريد أن تملئ الكرة بأي لون بل إطار خارجي فقط. وتستطيع أن تفعل ذلك بتغيير التابعين fillstyle و fill إلى التابعين strokeStyle و stroke على الترتيب.
  • اجعل الإطار الخارجي أكثر ثخانة كي تُرى الكرة الشريرة بشكل أوضح. وتستطيع إنجاز الأمر بضبط قيمة lineWidth (على القيمة 3 مثلًا) وذلك بعد وضع هذه التعليمة في مكان مناسب بعد الدالة ()beginPath.

التابع ()checkBounds

ينفّذ هذا التابع ما ينفّذه الجزء الأول من التابع ()update في الصنف Ball وذلك بالتحقق من وصول حافة الكرة إلى حواف الشاشة ومنعها من ذلك. لهذا انسخ شيفرة هذه الدالة وأجر عليها التعديلات التالية:

  • احذف السطرين اﻷخيرين، فلا نريد تغيير موقع الكرة الشريرة تلقائيًا في كل إطار لأننا نريد تحريكها بطريقة أخرى كما سنرى لاحقًا.
  • إن أعاد الشرط داخل العبارة ()if القيمة true، فلن نريد في هذه الحالة تحديث قيمتي الخاصيتين velx و velY بل نريد تغيير قيمتي x و y كي ترتد الكرة الشريرة قليلًا عن الشاشة. قد يفي بالغرض إضافة أو طرح (بما يناسب) قياس الكرة الشريرة.

التابع ()collisionDetect

يعمل هذا التابع بنفس الطريقة التي يعمل فيها التابع المقابل في الصنف Ball لهذا بإمكانك نسخ شيفرته واستخدامها كأساس للتابع الجديد. لكن بالطبع مع إجراء التغييرات التالية:

  • لا حاجة في الشرط if الخارجي للتحقق من أن الكرة المختبرة هي نفسها الكرة التي وصل إليها التكرار لأنها لم تعد كرة عادية بل الكرة الشريرة. وما عليك فعله هو التأكد من أن الكرة التي نختبرها موجودة (ما هي الخاصية التي نستخدمها؟). فإن لم تكن موجودة فإن الكرة قد أُكلت مسبقًا من قبل الكرة الشريرة، فلا حاجة لاختبارها مجددًا.
  • لا حاجة في الشرط if الداخلي إلى تغيير لون الكرة عند التقاط حالة تصادم، بل عليك فقط أن تجعل الكرة التي تصطدم بالكرة الشريرة غير موجودة (كيف ستفعل ذلك؟)

إدخال الكرة الشريرة إلى البرنامج

بعد أن عرّفنا الكرة الشريرة، لا بد من إظهارها فعليًا في المشهد. وﻹنجاز اﻷمر، عليك إجراء بعض التعديلات على الدالة ()loop.

  • أنشئ بداية نسخة جديدة عن كائن الكرة الشريرة (اضبط المعاملات اللازمة). عليك فعل ذلك مرة واحدة، وليس في كل تكرار للدالة ()loop.
  • في النقطة التي نتنقل فيها بين الكرات ونستدعي التوابع ()draw و ()update و ()collisionDetect لكل كرة، استدع التوابع السابقة فقط إن كانت الكرة التي وصلنا إليها في التكرار موجودة.
  • استدع التوابع ()draw و ()checkBounds و ()collisionDetect لنسخة الكرة الشريرة في كل تكرار للحلقة.

إنجاز شيفرة عداد النتيجة

اتبع الخطوات التالية:

  1. أضف <p> تحت العنصر <h1> في ملف HTML وفيها النص Ball count.
  2. أضف القاعدة التالية في آخر ملف CSS:
p {
  position: absolute;
  margin: 0;
  top: 35px;
  right: 5px;
  color: #aaa;
}

قم بإجراء التعديلات التالية في ملف جافا سكريبت:

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

الخلاصة

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

ترجمة -وبتصرف لمقال Adding features to our bouncing ball demo

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...