سنشرح في هذا المقال وهو جزء من سلسلة دليل جودو، كيفية بناء واجهة المستخدم وعارض النتيجة في الألعاب ثنائية الأبعاد عبر محرك Godot.
آخر الأقسام الرئيسية التي علينا بناؤها في لعبتنا ثنائية الأبعاد هي واجهة المستخدم User Interface؛ إذ سنحتاج إلى طريقة لعرض نتيجة اللاعب وغيرها من المعلومات. ولتنفيذ الأمر، سنستخدم عقدةً مختلفة من النوع Control
التي يزودنا بها محرك الألعاب جودو لبناء واجهات المستخدم.
مشهد واجهة المستخدم
سنبدأ المشهد بعقدة من النوع MarginContainer
ونسميها UI
، حيث تضمن الحاوية MarginContainer
ألا يقترب العقد الأبناء كثيرًا من الحافة من خلال إضافة هوامش حولها.
سننقر الآن على العقدة UI
وننتقل إلى نافذة الفاحص Inspector، ثم نضبط قيم الهوامش الأربعة على 10 ضمن الخاصية Theme Overrides>Constants
.
ننتقل بعدها إلى نافذة العرض الثنائي وننقر على أيقونة تجهيزات المراسي Anchor Preset وأيقونة المرساة Anchor، ثم نختار موقع الحاوية ليكون في أعلى الشاشة وعلى اتساعها بالعرض بالأعلى Top Wide.
نضيف تاليًا عقدةً من النوع HBoxContainer
، وهي نوع من الحاويات التي تنظم الأبناء أفقيًا. سنضيف ضمن هذه العقدة عقدة ابن من النوع TextureProgressBar
، وهي شريط تقدم يمثل وضع درع المركبة الفضائية، وسنسمي هذه العقدة ShieldBar.
لا تضم مجموعة الصور التي نزّلناها في مشروعنا أية صورة مناسبة لشريط التقدم، لذلك سننزلّ الصورتين التاليتين ونحفظهما في مجلد اللعبة. ضع صورة الخلفية الخضراء كقيمة للخاصية
Texture>Progress
، والصورة البيضاء كقيمة للخاصية
Texture>Under
.
سنلاحظ مباشرة أن الشريط صغير جدًا، لذلك سنغير قيمة الخاصية Minimum Custom Size
لتصبح (80,60)
وسنرى أن المستطيل البرتقالي قد كبر.
وكما هو واضح، لن يكون تمدد الصورة جميلًا، وقد يبدو سيئًا أيضًا، لهذا سنفعّل الخيار Range>Nine Patch Stretch
ونضبط بعدها قيم الخاصية Stretch Margin
الأربعة التي ستظهر على 3
.
من المفترض أن نرى الآن شريطًا طويلًا فارغًا. ولمعرفة كيف سيبدو عندما يبدأ بالامتلاء، سنغير قيمة الخاصية Range>Value
إلى أي قيمة بين 0 و 100.
سنعرض إلى اليسار نتيجة اللاعب، وقد نستخدم عقدةً من النوع Label
وخط مناسب. وبالطبع لن تكون الخطوط التي يقدمها نظام التشغيل مناسبةً للعبة، ولهذا سنستخدم أيقونات الأرقام الموجودة في مجلد الصور، وذلك بعد معالجتها عن طريق الشيفرة لعرضها بالشكل الصحيح.
إنشاء عداد للنتيجة
سننشئ مشهدًا جديدًا أساسه عقدة من النوع HBoxContainer
ونسيمها ScoreCounter
. سنضبط هذه العقدة ليكون مركز ارتكاز العقدة بالعرض بالأعلى Top Wide، أي أن العقدة ستمتد أفقيًا وتتموضع عند الجزء العلوي من المشهد، ومحاذاتها نهاية الإنكماش End، أي ستكون محاذاة العقدة على الطرف الأيمن أو نهاية المساحة المتاحة باستخدام الزر المجاورة لأيقونة المرساة.
سنضبط أيضًا الخاصية Theme Overrides>Constants>Separation
على القيمة 0
، وهنا علينا تفعيل الخيار إلى جانب الخاصية.
سنضيف الآن مجموعةً من العقد النصية من النوع TextureRect
ضمن الحاوية الأفقية لعرض الأرقام، وسنضيف عقدةً واحدة أولًا ثم نضاعف العدد.
سنسمي العقدة النصية Digit0
ثم ننتقل إلى الفاحص، فالخاصية Texture
، ونختار إنشاء واحدة جديدة New AtlasTexture. سننقر بعد ذلك على هذا الخيار لتظهر لنا نافذة أسفلها الخاصية Atlas
. نسحب الآن الصورة Number_font (8 x 8).png
إلى هذه الخاصية ثم نضبط قيم الخاصية Region
على (32, 8, 8, 😎
. بعد ذلك ننقر مجددًا على الخاصية Texture
لتغلق النافذة، ثم اضبط قيمة الخاصية Stretch Mode
على Keep Aspect Centered
.
سنعود الآن إلى نافذة المشهد ونختار العقدة Digit0
، ثم نضغط على المفتاحين Ctrl+D
سبع مرات لإنشاء سبع عقد أخرى مماثلة للأولى من ناحية الشكل والخاصيات. حيث ستبدو نافذة العرض بعد هذه الخطوات كما يلي:
على الرغم من إنشاء ثمانية عقد TextureRect
للأرقام، إلا أن قيمة الخاصية Texture
تبقى نفسها، وهذه مشكلة، فعند تغيير الخاصية Region
لعرض الأرقام المختلفة ستعرض كل العقد صورة الرقم نفسه.
يعود السبب في ذلك إلى أن كائنات الموارد مثل العقدة Texture
ستحمّل في الذاكرة مرةً واحدة ثم تتشاركها العقدة المختلفة. وهذا أمر فعّال جدًا لأنه لا يهدر الذاكرة بتحميل نفس النسخة من المورد عدة مرات. إذًا، علينا تخصيص جزء من المورد لكل عقدة حتى تكون الصورة المعروضة في كل عقدة فريدة، وهنا سننقر في كل عقدة على السهم المجاور للخاصية AtlasTexture
ثم نضغط على خيار اجعله فريدًا Make Unique.
نضيف الآن سكريبت إلى العقدة ScoreCounter
، ونختار فيها المنطقة الصحيحة Region
من الصورة لكل رقم نريد عرضه:
extends HBoxContainer var digit_coords = { 1: Vector2(0, 0), 2: Vector2(8, 0), 3: Vector2(16, 0), 4: Vector2(24, 0), 5: Vector2(32, 0), 6: Vector2(0, 8), 7: Vector2(8, 8), 8: Vector2(16, 8), 9: Vector2(24, 8), 0: Vector2(32, 8) } func display_digits(n): var s = "%08d" % n for i in 8: get_child(i).texture.region = Rect2(digit_coords[int(s[i])], Vector2(8, 8))
تبدأ الشيفرة بتشكيل قائمة من الإحداثيات التي تمثل أماكن من الصورة يحدد كل منها مكان وجود رقم، ثم تنسّق الدالة ()display_digits
العدد المطلوب ليكون من 8 أرقام، بحيث لو كان أمامنا الرقم 285 مثلًا، فسيكتب بالشكل 00000258. بعد ذلك، سنضع الرقم المناسب في كل منزلة من العدد السابق اعتمادًا على مصفوفة الإحداثيات.
إضافة سكريبت واجهة المستخدم UI
نعود الآن إلى المشهد ui ثم نضيف المشهد ScoreCounter
إلى العقدة HBoxContainer
. نضيف بعد ذلك السكريبت التالي إلى العقدة UI:
extends MarginContainer @onready var shield_bar = $HBoxContainer/ShieldBar @onready var score_counter = $HBoxContainer/ScoreCounter func update_score(value): score_counter.display_digits(value) func update_shield(max_value, value): shield_bar.max_value = max_value shield_bar.value = value
سنستدعي هاتين الدالتين في المشهد الرئيسي Main في كل مرة نحتاج فيها إلى تغيير النتيجة أو قوة درع السفينة.
إضافة المشهد UI إلى المشهد الرئيسي Main
سنضيف الآن عقدةً من النوع CanvasLayer
إلى المشهد الرئيسي Main، ثم منسخ إليها المشهد UI كعقدة ابن. ستُنشئ العقدة CanvasLayer
طبقة رسم جديدة، ولهذا ستُرسم واجهة المستخدم فوق جميع مكونات اللعبة؛ ولحل هذه المشكلة سنغيّر التابع التالي في السكريبت main.gd
كما يلي:
func _on_enemy_died(value): score += value $CanvasLayer/UI.update_score(score)
درع اللاعب
يمكننا إضافة الدرع إلى سكريبت اللاعب، عبر إضافة الأسطر التالية إلى الملف player.gd
:
signal died signal shield_changed @export var max_shield = 10 var shield = max_shield: set = set_shield
سنستدعي الدالة ()set_shield
من خلال عملية الإسناد = set
في كل مرة يُضبط فيها المتغير shield
الذي يمثل قيمة درع المركبة كما يلي:
func set_shield(value): shield = min(max_shield, value) shield_changed.emit(max_shield, shield) if shield <= 0: hide() died.emit()
نستطيع أيضًا وصل إشارة المركبة area_entered
كي نلتقط اصطدام العدو بالمركبة:
func _on_area_entered(area): if area.is_in_group("enemies"): area.explode() shield -= max_shield / 2
لنضف الآن بعض الضرر إلى درع المركبة عندما تُصاب في سكريبت قذائف العدو enemy_bullet.gd
، وذلك على النحو الآتي:
func _on_area_entered(area): if area.name == "Player": queue_free() area.shield -= 1
في الأخير، علينا وصل إشارة اللاعب shield_changed
إلى الدالة التي تحدّث شريط الدرع في واجهة المستخدم، ولهذا سننتقل إلى الفاحص بعد اختيار عقدة اللاعب Player
في المشهد الرئيسي. سننقر بعد ذلك في نافذة العقدة Node نقرًا مزدوجًا على الإشارة، وذلك لفتح نافذة توصل إشارة إلى دالة Connect a Signal.
سنختار بعد ذلك العقدة UI
ثم نكتب update_shield
في صندوق الدالة المتلقية Receiver Method.
يمكننا الآن تشغيل اللعبة والتأكد من أن طاقة الدرع تنخفض عندما تصيبه قذيفة أو مركبة معادية.
ختامًا
بهذا نكون قد تعرفنا على كيفية بناء واجهة المستخدم وعارض النتيجة في الألعاب ثنائية الأبعاد عبر محرك الألعاب جودو، وسنتعرف على كيفية تطوير اللعبة أكثر بالمقالات الموالية. ترجمة -وبتصرف- للمقال: UI and Score.
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.