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

بناء لعبة ثنائية البعد عبر محرك الألعاب Godot - الجزء الأخير: ربط مشاهد اللعبة ووضع اللمسات اﻷخيرة


ابراهيم الخضور

آخر ما يلزمنا ﻹكمال اللعبة ثنائية الأبعاد "تفادي الزواحف" التي بدأنا العمل عليها في مقال سابق هو بناء واجهة المستخدم user interface المخصصة عرض أشياء مهمة مثل النتيجة أو عبارة "انتهت اللعبة" وزر ﻹعادة اللعب.

لهذا سننشىء في مقال اليوم مشهدًا جديدًا ثم نضيف عقدة من النوع CanvasLayer (واجهة مستخدم) باسم HUD وهي اختصار للكلمات "Heads-up display" بمعنى "شاشة المقدمة" والتي تعرض معلومات عن اللعبة وتظهر على شكل طبقة فوق نافذة عرض اللعبة.

إنشاء واجهة اللعبة

تسمح لك العقدة CanvasLayer في محرك الألعاب جودو GODOT برسم عناصر واجهة المستخدم على طبقة فوق بقية مشاهد اللعبة، وبالتالي لن تغطيها أية عناصر أخرى كاللاعب أو اﻷعداء.

في لعبتنا الحالة، نحتاج لأن تعرض الواجهة HUD المعلومات التالية:

  • النتيجة وتغيّرها ScoreTimer.
  • رسالة مثل "انتهت اللعبة" أو "استعد!".
  • زر البداية "ابدأ" لكي تبدأ اللعب.

إن الصنف اﻷساسي لهذه العقدة هو Control، وسنحتاج في واجهتنا لنوعين من عناصر التحكم Control هما عنوان Label وزر Button، لهذا عليك إنشاء ثلاث عقد أبناء للعقدة HUD كالتالي:

  • عنوان Label باسم ScoreLabel.
  • عنوان Label باسم Message.
  • زر Button باسم StartButton.
  • مؤقت Timer باسم MessageTimer.

انقر على العنوان ScoreLabel ثم اكتب رقمًا في الحقل Text ضمن الفاحص، وكما تلاحظ، فإن الخط المستخدم افتراضيًا للعقدة Control صغير وغير مناسب، لذلك سنستخدم خطًا موجودًا ضمن المجلد "font" يُدعى "Xolonium-Regular.ttf" كالتالي: في نافذة الفاحص وتحت اللوحة انقر على Theme Overridesثم Fonts ثم Font ثم اختر "تحميل" واختر الملف "Xolonium-Regular.ttf":

01 add font

زد حجم الخط إلى 64 في الحقل "Font Size" ثم كرر نفس العملية على العنوان اﻵخر وعلى الزر في المشهد.

ملاحظة: للعقدة Control موقع وأبعاد لكنها تمتلك أيضًا ما يُعرف بالمربط Anchor الذي يحدد نقطة اﻷصل، وهي النقطة المرجعية لحواف العقدة.

رتب العقد كما في لقطة الشاشة التالية، وبإمكانك سحب العقد إلى المكان الذي تريده يدويًا أو استخدم المربط لضبط الموقع بدقة أكبر:

03 arranging ui

العنوان ScoreLabel

  1. أضف النص 0
  2. اضبط المحاذاة اﻷفقية Horizontal Alignment والعمودية Vertical Alignment على Center.
  3. اختر القيمة Center Top لضبط المربط (الخاصية Anchor Preset).

الرسالة Message

  1. أضف النص "تفادي الزواحف".
  2. اضبط المحاذاة اﻷفقية Horizontal Alignment والعمودية Vertical Alignment على Center.
  3. اضبط قيمة "نمط الالتفاف التلقائي Autowrap Mode" على Word وإلا سيبقى النص على نفس السطر.
  4. اضبط قيمة الخاصية Size X الموجودة على المسار Control>Layout>Transform على القيمة 480
  5. اختر القيمة Center لضبط المربط (الخاصية Anchor Preset).

الزر StartButton

  1. أضف النص "ابدأ".
  2. اضبط قيمة الخاصية Size X الموجودة على المسار Control>Layout>Transform على القيمة 200 والخاصية Size Y على القيمة 100.
  3. اختر القيمة Center Bottom لضبط المربط (الخاصية Anchor Preset).
  4. ا ضبط قيمة الخاصية Position Y الموجودة على المسار Control>Layout>Transform على القيمة 580.

وأخيرًا، اضبط الخاصية Wait Time في المؤقت MessageTimer على 2، والخاصية One Shot على "فعّال ON" ثم أضف السكربت التالي إلى HUD:

extends CanvasLayer

# Notifies `Main` node that the button has been pressed
signal start_game

نريد اﻵن عرض رسالة مؤقتة مثل " استعد!" لهذا سنضيف الشيفرة التالية:

func show_message(text):
    $Message.text = text
    $Message.show()
    $MessageTimer.start()

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

func show_game_over():
    show_message("Game Over")
    # Wait until the MessageTimer has counted down.
    await $MessageTimer.timeout

    $Message.text = "Dodge the\nCreeps!"
    $Message.show()
    # Make a one-shot timer and wait for it to finish.
    await get_tree().create_timer(1.0).timeout
    $StartButton.show()

تُستدعى الدالة اﻷخيرة عندما يخسر اللاعب.

ملاحظة: إن أردت أن توقف اللعبة لفترة وجيزة، بإمكانك استخدام الدالة ()create_timer العائدة لشجرة المشاهد بدلًا من استخدام عقدة مؤقت. وهذه الدالة مفيدة جدًا في إضافة تأخير زمني في الحالات المشابهة لحالتنا التي نرغب فيها الانتظار قليلًا قبل عرض زر "إبدأ".

أضف الشيفرة التالية اﻵن لتحديث النتيجة:

func update_score(score):
    $ScoreLabel.text = str(score)

صل اﻹشارة ()timeout العائدة للمؤقت MessageTimer واﻹشارة ()pressed العائدة للزر StartButton ثم عدل الشيفرة لتصبح كالتالي:

func _on_start_button_pressed():
    $StartButton.hide()
    start_game.emit()

func _on_message_timer_timeout():
    $Message.hide()

ربط المشهد HUD بالمشهد الرئيسي Main

بعد أن انتهينا من إنشاء المشهد HUD سنعود إلى المشهد الرئيسي Main. انشئ نسخة من المشهد HUDضمن المشهد الرئيسي كما نسخنا مشهد اللاعب سابقًا. ويجب أن تبدو شجرة المشاهد كالتالي حتى يكون كل شيء في مكانه الصحيح:

04 comlpete main scene

علينا اﻵن وصل وظائف المشهد HUD إلى السكربت الرئيسي، ويتطلب ذلك بعد اﻹضافات إلى المشهد الرئيسي. صل اﻹشارة start_game للمشهد HUD إلى الدالة ()new_game في المشهد الرئيسي بالنقر على نسخة HUD في المشهد الرئيسي ثم الانتقال إلى "عقدة" في الشريط الجانبي واختيار اﻹشارة start_game ثم النقر عليها نقرًا مزدوجًا لتظهر نافذة "قم بوصل اﻹشارة إلى الدالة" ثم النقر على زر "Pick" في أسفلها واختيار الدالة ()new_game. تأكد من وجود اﻷيقونة الخضراء إلى جوار ()func new_game في السكربت الرئيسي، ولا تنس إزالة الاستدعاء ()new_game من الدالة ()ready_ ﻷننا لا نريد أن تبدأ اللعبة تلقائيًا.

عدّل الشيفرة الموجودة ضمن الدالة ()new_game لتعرض الرسالة "استعد!":

$HUD.update_score(score)
$HUD.show_message("Get Ready")

كما عليك استدعاء دالة HUD الموافقة للدالة ()game_over في السكربت الرئيسي:

$HUD.show_game_over()

أضف أخيرًا الشيفرة التالية إلى الدالة ()on_score_timer_timeout_ كي يبقى عداد النتيجة متزامنًا مع التغيرات:

$HUD.update_score(score)

أصبحت اﻵن جاهزًا للعب! انقر على زر تشغيل المشهد، وإذا طُلب منك اختيار المشهد اﻷساسي اختر main.tscn.

إزالة الزواحف القديمة

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

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

05 mobs group

أضف السطر التالي إلى الدالة ()new_game في السكربت الرئيسي:

get_tree().call_group("mobs", "queue_free")

تستدعي الدالة ()call_group تابعًا محددًا (المعامل الثاني لها) باسمه وتطبقه على كل عناصر المجموعة المختارة (المعامل الأول)، وفي حالتنا يحذف كل زاحف نفسه.

وضع اللمسات اﻷخيرة على اللعبة

اكتملت اللعبة حاليًا من ناحية الوظيفة، وما سنفعله تاليًا هو بعض اﻹضافات لتحسين المظهر العام لها.

الخلفية

قد يجد البعض أن الخلفية الرمادية غير جذابة لذا دعونا نغير اللون. ومن الطرق المتبعة نجد العقدة ColorRect التي يجب وضعها تحت العقدة Main مباشرة كي تُرسم خلف جميع العقد. ولهذا العقدة خاصية واحدة هي Color. اختر اللون الذي تريده ثم انقر على Layout>Anchor Preset واختر "على كامل المستطيل"، كما يمكنك تنفيذ العملية من خلال شريط اﻷدوات أعلى نافذة العرض.

بإمكانك أيضًا استخدام صورة في الخلفية إن أردت لكنك ستحتاج إلى عقدة من نوع TextureRect.

إضافة تأثيرات صوتية للعبة

مقاطع الصوت والموسيقى من أكثر العوامل التي تزيد من جاذبية  تصميم الألعاب الإلكترونية، لهذا وضعنا في المجلد "art" الخاص بلعبتنا ملفين صوتيين الأول هو "House In a Forest Loop.ogg" لموسيقى الخلفية واﻵخر "gameover.wav" لخسارة اللاعب. وعلينا اﻵن إضافة عقدتين من النوع AudioStreamPlayer كأبناء للعقدة Main وتسميتهما باسم Music و DeathSound. انقر بعد ذلك على الخاصية Stream لكل منهما ثم "تحميل" واختر المقطع الصوتي الموافق.

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

ولتشغيل الموسيقى أضف التابع ()Music.play$ إلى الدالة ()new_game والتابع ()Music.stop$ إلى الدالة ()game_over ثم أضف أخيرًا التابع ()DeathSound.play$ إلى الدالة ()game_over.

اختصارات لوحة المفاتيح

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

انقر على مشروع>إعدادات الشروع ثم انقر على النافذة الفرعية "خريطة اﻹدخال" في النافذة التي تظهر. انشئ بعد ذلك وبنفس الطريقة التي أنشأت بها سابقًا إجراءات الحركة اﻷربعة إجراءًا جديدًا باسم start_game واربطه بالمفتاح Enter.

وإن أردت أيضًا ربط أزرار أي أداة تحكم أخرى باﻹجراءات السابقة، تأكد من توصيل هذه اﻷداة ثم انقر اﻷيقونة "+" الموجودة إلى يمين كل إجراء ثم اضغط زر اﻷداة الذي تريد ربطه بهذا اﻹجراء.

عد إلى الواجه HUD وانقر على الزر StartButton ثم ابحث عن الخاصية "Shortcut" ضمن "الفاحص" ثم انشئ اختصارًا جديدًا بالنقر على مربع النص المجاور واختيار "جديدة Shortcut" ثم النقر على الاختصار الجديد وفتح "Events" وإضافة حدث جديد بالنقر على "Array[InputEvent]".

06 add shortcut element

أنشئ حدث جديدة InputEventAction باسم start_game:

07 input event action

وهكذا عندما يظهر زر "ابدأ" يمكن النقر عليه أو الضغط على المفتاح "Enter" لتبدأ اللعب!

08 dodge preview

الخلاصة

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

لا زال هناك الكثير لتتعلمه بالطبع لتكون مطور ألعاب إلكترونية محترف، لكن خذ الآن قسطًا من الراحة واستمتع بإنجازك!

ترجمة -وبتصرف- للمقالين: Heads_up display            و Finishing up

اقرأ أيضًا

 


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

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

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



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

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

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

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   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.


×
×
  • أضف...