يُطلب منك في هذا التمرين التطبيقي أن تستخدم تطبيق الكرات المرتدة الذي بنيناه في المقال السابق كنقطة انطلاق ومن ثم إضافة بعض الميزات المهمة اﻷخرى.
كما ننصحك قبل محاول العمل على هذا التمرين الاطلاع على سلسلة المقالات السابقة التي تشرح مبادئ البرمجة بالكائنات في جافا سكريبت.
نقطة الانطلاق
انسخ بداية الملفات index-finished.html و style.css و main-finished.js إلى حاسوبك -وهي ملفات تخص التمرين التطبيقي في المقال السابق- واحفظها في مجلد جديد. كما يمكنك استخدام محررات على اﻹنترنت مثل CodePen و JSFiddle و Glitch. إذ تسمح هذه المحررات بلصق شيفرة HTML و CSS ضمن المحرر، لكن إن لم يكن للمحرر الذي تستخدمه نافذة مخصصة شفرة جافا سكريبت، بإمكانك حينها وضع الشيفرة داخل عنصر <script>
في صفحة HTML.
نصائح وتلميحات
إليك بعض المؤشرات التي تخص التطبيق قبل أن تبدأ:
- التمرين يحمل تحديًا واضحًا، لهذا ننصحك بقراءة المطلوب بالكامل قبل أن تبدأ كتابة شيفرة كل مهمة، وانتقل من خطوة إلى أخرى ببطء وحذر.
- قد يكون من الجيد أن تنشئ نسخة مستقلة عن كل التمرين بعد أن تنجز كل مرحلة بنجاح كي تتمكن من العودة إليها إن واجهتك المشاكل لاحقًا.
موجز المشروعتأكل
لقد كان بناء تطبيق الكرات المرتدة ممتعًا، لكن عليك جعله اﻵن أكثر تفاعلًا بإضافة كرة شريرة يتحكم بها المستخدم الكرات إن التقطتها. كما نريد اختبار مهارتك في بناء إنشاء الكائنات بإنشاء كائن عام Shape
ترثه الكرات الملونة والكرة الشريرة. ونريد أخيرًًا عدادًا للنتيجة لتحديد عدد الكرات الباقية التي يجب التقاطها.
تعرض الصورة التالية مثالًا عما سيكونه التطبيق عند إنجازه:
ولتكوّن فكرة أوضح، ألقِ نظرة على التطبيق المكتمل لكن لا تختلس النظر إلى شيفرته!
الخطوات التي عليك إكمالها
تصف اﻷقسام التالية ما عليك إنجازه.
إنشاء الصنف 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
لنسخة الكرة الشريرة في كل تكرار للحلقة.
إنجاز شيفرة عداد النتيجة
اتبع الخطوات التالية:
-
أضف
<p>
تحت العنصر<h1>
في ملف HTML وفيها النصBall count
. - أضف القاعدة التالية في آخر ملف CSS:
p { position: absolute; margin: 0; top: 35px; right: 5px; color: #aaa; }
قم بإجراء التعديلات التالية في ملف جافا سكريبت:
- أنشئ متغيرًا لتخزين مرجع إلى الفقرة النصية.
- أظهر عدد الكرات على الشاشة بطريقة تجدها مناسبة.
- زد العداد وحدّث عدد الكرات الموجود على الشاشة في كل مرة تُضاف فيها كرة إلى المشهد.
- انقص العداد وحدّث عدد الكرات على الشاشة في كل مرة تأكل فيها الكرة الشريرة كرة (أي تجعلها غير موجودة).
الخلاصة
تعلمنا في هذا المقال كيفية تحسين تطبيق الكرات المرتدة الذي بنيناه في المقال السابق بإضافة ميزات جديدة مثل كرة شريرة يتحكم بها المستخدم حيث تتحرك هذه الكرة عبر الشاشة ويستطيع المستخدم التحكم بها باستخدام مفاتيح الأسهم كما أضفنا عدّاد للنتيجة ليُظهر عدد الكرات المتبقية على الشاشة وشرحنا طريقة إخفاء الكرات بحيث تُصبح الكرة غير موجودة عند اصطدامها بالكرة الشريرة.
ترجمة -وبتصرف لمقال Adding features to our bouncing ball demo
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.