نشرح في هذا المقال طريقة بناء قائمة تتضمن مجموعة خيارات تظهر في بداية اللعبة وتساعد اللاعب على تحديد مستوى اللعبة. سيكون تصميم القائمة على شكل شبكة قابلة للتمرير وضمنها صناديق تمثل كل مستوى، بحيث يمكن التنقل بينها واختيار المرحلة التي يرغب اللاعب في اللعب فيها كما توضح الصورة التالية.
مراحل بناء القائمة
سنبني القائمة بتصميم شبكة قابلة للتمرير مكونة من صناديق لتحديد المستوى بحيث يمكن للاعب أن يختار فيما بينها. سنبدأ أولًا ببناء صندوق المستوى LevelBox بشكل مستقل.
بناء صندوق المستوى LevelBox
ستكون هيكلية العقدة اللازمة لبناء هذا الصندوق على النحو التالي:
LevelBox: PanelContainer Label MarginContainer TextureRect
استخدمنا العناصر التالية لتشكيل الصندوق:
- حاوية PanelContainer لتنظيم وعرض العناصر داخلها
- عقدة تسمية نصية Label لعرض رقم المستوى
- حاوية MarginContainer لإضافة هوامش حول المحتوى
-
عقدة
LevelBox
من نوع TextureRect لعرض صورة قفل عندما يكون المستوى مغلق، ورقم عندما يكون مفتوح
نتأكد من ضبط الخاصية Layout
في القسم Custom Minimum Size
للعقدة LevelBox
على القيمة (110,110)
ويمكن اختيار أي حجم آخر مناسب لتخطيط القائمة.
نضيف سكريبت إلى العقدة التي تمثل صندوق المستوى من أجل وصل اﻹشارة gui_input
التي تُطلق عندما تتلقى العقدة حدث إدخال مثل النقر بالفأرة أو الضغط على لوحة المفاتيح. حيث يمكن أن يكون المستوى مغلقًا أو مفتوحًا، وعند النقر على الصندوق سنرسل إرسال إشارة لاختيار المستوى.
@tool extends PanelContainer signal level_selected # إشارة تُطلق عند تحديد المستوى @export var locked = true: # يحدد إذا كان المستوى مغلقًا set = set_locked @export var level_num = 1: # رقم المستوى set = set_level @onready var lock = $MarginContainer/Lock # صورة القفل @onready var label = $Label # رقم المستوى # دالة لضبط حالة القفل وإظهارأو إخفاء العناصر func set_locked(value): locked = value if not is_inside_tree(): # ننتظر حتى يكتمل تحميل العنصر داخل المشهد await ready lock.visible = value # نعرض صورة القفل إذا كان المستوى مغلقًا label.visible = not value # نظهر النص إذا كان المستوى غير مغلق # دالة لضبط رقم المستوى وتحديث النص المعروض func set_level(value): level_num = value if not is_inside_tree(): # ننتظر تحميل العنصر في المشهد await ready label.text = str(level_num) # تحديث النص برقم المستوى # دالة لمعالجة مدخلات المستخدم func _on_gui_input(event): if locked: # إذا كان المستوى مغلقًا، نتجاهل النقر return if event is InputEventMouseButton and event.pressed: # التحقق من النقر بزر الفأرة level_selected.emit(level_num) # إطلاق الإشارة مع رقم المستوى print("Clicked level ", level_num) # طباعة رسالة لاختبار النقر
نستخدم في شيفرتنا التوجيه tool@
حتى نتمكن من تغيير قيم الخاصيات عبر نافذة الفاحص inspector ونرى التأثير مباشرة دون الحاجة لتشغيل المشهد. لنجرب اﻵن النقر على الخاصية Locked
ونتحقق من رؤية صورة القفل تظهر وتختفي. وطالما أن اللعبة لا تضم مستويات فعلية لتحميلها، ستساعدنا الدالة ()print
على اختبار التقاط حدث النقر.
بناء الشبكة
بعد إنهاء مشهد صندوق المستوى، سنضيف مشهدًا جديدًا يضم العقدة GridContainer
ثم نضع ضمنها العدد الذي نريده من نسخ العقدة LevelBox
، ونتأكد من ضبط قيمة الخاصية Columns
على القيمة المناسبة.
ننتقل للقسم Theme Overrides
ثم Seperation
ونضبط قيمتي الخاصيتين V Seperation
و H Seperation
على 10
، ونحفظ المشهد باسم LevelGrid
. سنستخدم في القائمة عدة نسخ عن عقدة الشبكة لعرض العدد المطلوب من المستويات.
شاشة القائمة
بإمكاننا اﻵن تجميع القائمة النهائية. توضح الصورة التالية التخطيط اﻷولي الذي سننفذه:
ننشئ المشهد انطلاقًا من العقد التالية:
LevelMenu: MarginContainer VBoxContainer Title: Label HBoxContainer BackButton: TextureButton ClipControl: Control NextButton: TextureButton
نضبط خواص العقد كالتالي:
-
العقدة
LevelMenu
: نضبطTheme Overrides
ثمConstants
ثمMargins
على القيمة20
-
العقدة
VBoxContainer
:نضبطTheme Overrides
ثمConstants
ثمMargins
على القيمة50
-
العقدة
Title
: ننسقها بالطريقة التي نريدها -
نضبط العقدتين
BackButton
وNextButton
كالتالي:-
Ignore Texture Size: On
لضبط حجم الزر وفقًا لحجمه داخل واجهة المستخدم -
Stretch Mode: Keep Centered
للحفاظ على محاذاة المحتوى داخل الأزرار -
Layout/Container: On
ليعمل الزر كحاوية -
Sizing/Horizontal/Expand: On
لتوسيع الأزرار أفقيًا داخل المساحة المتاحة
-
-
نضبط العقدة
ClipControl
كما يلي:-
Layout/Clip Contents:On
لاقتصاص أي محتوى يتجاوز حجم الإطار -
Layout>Custom Minimum Size:(710, 350)
لتحديد الحجم الأدنى للعقدة
-
بعد ذلك نضع الشبكة ضمن العقدة ClipControl
، بما أننا تمكين الخاصية Clip Content
سيقتص محتوى العقدة إن كانت أكبر من عنصر التحكم. وسنتمكن اﻵن من بناء شبكة من صناديق المستويات قابلة للتمرير، لهذا نضيف عقدة من النوع HBoxContainer
تُدعى GridBox
إلى ClipControl
إضافة إلى ثلاث نسخ من العقدة LevrlGrid
أو أكثر إن أردنا. ونتأكد من ضبط الخاصية Theme Overrides
ثم Constants
ثم Separation
على القيمة 0
.
ينبغي أن يبدو تخطيط المشهد اﻵن كما في الشكل التالي تقريبًا، مع العلم أننا عطلنا الخاصية Clip content
لنعرض ما يحدث بشكل أوضح:
الشبكات الثلاثة موجودة ضمن Clip Content
لكن لا يمكن للمتحكم ClipControl
عرض سوى شبكة واحدة كل مرة. لهذا ولكي ننتقل إلى الشبكتين الباقيتين عن طريق التمرير، لا بد من إزاحة GridBox
مقدار 710
بكسل يمينًا أو يسارًا
110 (width of each LevelBox) * 6 (grid columns) + 10 (grid spacing) * 5 == 710
قد يتبادر للذهن سؤال عن عدم استخدام العقدة ScrollCointainer
. هنا، بالتأكيد يمكن ذلك، لكننا لا نريد التنقل بين الشبكات باستمرار، ولا نريد أيضًا رؤية شريط تمرير.
نضيف السكربت التالي إلى العقدة LevelMenu
لوصل إشارتي pressed
الخاصتين بكل زر:
extends MarginContainer var num_grids = 1 var current_grid = 1 var grid_width = 710 @onready var gridbox = $VBoxContainer/HBoxContainer/ClipControl/GridBox func _ready(): # ترقيم جميع صناديق المستويات وإلغاء قفلها # يمكن استبداله بما يتناسب مع نظام المستويات في اللعبة # يمكن أيضًا ربط إشارات "level_selected" هنا num_grids = gridbox.get_child_count() for grid in gridbox.get_children(): for box in grid.get_children(): var num = box.get_position_in_parent() + 1 + 18 * grid.get_position_in_parent() box.level_num = num box.locked = false func _on_BackButton_pressed(): if current_grid > 1: current_grid -= 1 gridbox.rect_position.x += grid_width func _on_NextButton_pressed(): if current_grid < num_grids: current_grid += 1 gridbox.rect_position.x -= grid_width
ننقر على زر التالي Next والسابق Back عند تشغيل المشهد ونتأكد من تمرير العناصر كما هو متوقع. من المفترض أن يطبع النقر على صندوق المستوى شيئًا في شاشة الطرفية.
الخاتمة
بهذا نكون قد انتهينا من مقالنا الذي يشرح طريقة بناء شبكة قابلة للتمرير يمينًا ويسارًا تضم صناديق لتحديد مرحلة أو مستوى اللعبة، ويمكن تحميل المثال بالكامل لرؤية كل شيء يعمل كما يجب بما في ذلك اﻹجراءات وبعض عمليات توليد اﻷطر البينية tweens لتجميل عملية التمرير.
ترجمة -وبتصرف- للمقال: Level Select Menu
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.