سنتعرف في هذا المقال على طريقة بناء أزرار عد تنازلي countdown لتحقيق ميزة الانتظار في لعبتنا كي لا تتفعّل ميزة ما إلا بعد مضي فترة زمنية معينة، كما سنشرح طريقة بناء قائمة دائرية Radial menu تعرض عدة خيارات موزعة على شكل حلقة مما يسهّل الوصول لخيارات هذه القائمة ويضيف طابعًا فريدًا لواجهة الاستخدام.
بناء أزرار العد التنازلي
قد نرغب في إضافة عدة أزرار تمنح اللاعب مهارات أو قدرات خاصة ability buttons مع توفير ميزة الانتظار لفترة معينة قبل تمكين اللاعب من النقر على كل زر منها واكتساب القدرة المطلوبة، يمكننا تحقيق ذلك من خلال ميزة العد التنازلي countdown، وفي حال احتجنا لأيقونات ورسومات مناسبة لاستخدامها مع هذه الأزرار فستجد كمًا كبيرًا من التصاميم المناسبة في موقع Game-icons.net وسنستخدم بعضها في مقالنا.
إعداد مشهد اللعبة
يضم المشهد الذي سنعمل عليه العقد nodes التالية:
لنوضح بإيجاز دور كل عقدة منها:
العقدة | النوع | الوظيفة |
---|---|---|
AbilityButton |
TextureButton
|
زر ينشط قدرة خاصة للاعب عند الضغط عليه |
Sweep |
TextureProgress
|
شريط تقدم يظهر عد تنازلي بعد الضغط على الزر |
Timer |
Timer
|
مؤقت يتحكم في فترة التهدئة Cooldown قبل التمكن من إعادة استخدام القدرة الخاصة |
Counter |
MarginContainer
|
حاوية خاصة تتيح إضافة هوامش Margins بين عناصرها |
Value |
Label
|
مكون تسمية توضيحية يعرض التوقيت |
سنحدد الأيقونة الخاصة بكل زر من خلال الخاصية Textures
ثم Normal
لزر القدرة AbilityButton
حيث يمكننا من هنا تحديد الأيقونة الافتراضية للزر عندما لا يكون مضغوطًا، ثم نختار القيمة Full Rect
في العقدة Sweep
من القائمة Presets
لتحديد تأثيرات التعبئة أو المسح التدريجي ليكون على كامل الزر. بعد ذلك، نضبط الخاصية FillMode
للعقدة Counter
بالقيمة clockwise
.
نريد تغيير إضاءة زر الانتظار بشكل تدريجي وفق زاوية قطرية. لتحقيق ذلك، نختار الخاصية Visibilty
للزر ثم Modulate
ونختار قيمتها لتكون بلون رمادي قاتم مع إضافة بعض الشفافية لجعل الزر يبدو باهتًا في وضع الانتظار.
نضبط عقدة المؤقت Timer
على القيمة One Shot
لجعلها تعمل مرة واحدة فقط، وفيما يخص العقدة Counter
وهي حاوية تحتوي النص وتحاذيه، ينبغي ضبط تخطيطها على Bottom Wide
وضبط الخاصيتين Margin Right
و Margin Left
للمسافات الجانبية على القيمة 5
وذلك ضمن القسم Theme Overrides
ثم Constants.
بالنسبة للعقدة value
سنضبط خاصية المحاذاة الأفقية Horizontal Alignment
على القيمة Right
، وخاصية اقتصاص النص Clip Text
على القيمة on
لتجنب تجاوز النص لحدود الحاوية. ونختار الخط المناسب من القسم Theme Overrides
ثم Font
ونضع قيمة 0.0
في الحقل النصي. وطالما أن اﻷيقونة التي نستخدمها سوداء، فمن الجيد ضبط قيمة خاصية حجم الحدود Theme Outline Size
من القسم Overrides
ثم Constants بالقيمة 1
لجعل الأيقونة أكثر وضوحًا.
إضافة كود برمجي لزر العد التنازلي
نضيف سكريبت إلى عقدة زر القدرة AbilityButton
. ثم نربط إشارة timeout
الخاصة بالمؤقت Timer وإشارة pressed
الخاصة بزر القدرة. وبالتالي عند النقر على الزر، سيبدأ العد التنازلي وعندما ينتهي العد يمكننا تنفيذ إجراء معين.
extends TextureButton class_name AbilityButton @onready var time_label = $Counter/Value @export var cooldown = 1.0 func _ready(): time_label.hide() $Sweep.value = 0 $Sweep.texture_progress = texture_normal $Timer.wait_time = cooldown set_process(false)
يبدأ السكريبت بتصدير المتغير cooldown
الذي يحدد طول فترة الانتظار قبل تفعيل الزر، ومن ثم نضبط المؤقت Timer
داخل التابع ()ready_
لاستخدام هذه القيمة.
سنحتاج بعد ذلك لخامة texture لنسندها إلى TextureProgress
، سنستخدم نفس خامة الزر، ويمكن استخدام أي خامة أخرى نفضلها.
أخيرًا، لنتأكد من أن العمليات الخاصة بالمتغير Sweep
قد انتهت بشكل صحيح، سنتأكد إن كانت قيمة Sweep
هي 0
ونضبط قيمة معالجة العقدة processing
على false
. وبما أننا ننفذ التحريك ضمن التابع ()process_
لذا لا نحتاج لتنفيذ هذا التابع إن لم نكن في فترة التهدئة CoolDown.
func _process(delta): time_label.text = "%3.1f" % $Timer.time_left $Sweep.value = int(($Timer.time_left / cooldown) * 100)
نلاحظ في الكود السابق أننا استخدمنا الخاصية time_left
للمؤقت Timer
لضبط الخاصية text
للعقدة labe
والخاصية value
للعقدة Sweep
.
func _on_AbilityButton_pressed(): disabled = true set_process(true) $Timer.start() time_label.show()
عندما يُنقر الزر سيبدأ كل شيء:
func _on_Timer_timeout(): print("ability ready") $Sweep.value = 0 disabled = false time_label.hide() set_process(false)
كما يعود كل شيء إلى وضعه عندما ينتهي المؤقت من العد.
بإمكاننا وضع عدة أزرار ضمن عقدة حاوية من النوع HBoxContainer
وسنحصل على شريط أفقي من أزرار القدرة كما يلي:
بناء قائمة دائرية منبثقة
تُستخدم القوائم في العديد من اﻷلعاب للوصول إلى ميزات أو وظائف معينة، كأن نحدد من خلالها المهمة المطلوب تنفيذها في اللعبة حاليًا مثل التحدث أو التفتيش أو الهجوم وهكذا. ينبغي أن يكون مظهر وسلوك القائمة متلائمًا مع لعبتنا، لكننا سنركز في هذا المثال على آلية بناء قوائم دائرية Radial Menu ونترك لك حرية تنسيقها. توضح الصورة التالية قائمة العقد المطلوبة لتنفيذ القائمة:
نحتاج لاستخدام عقدة TextureButton
من النوع RadialMenuButton
لتكون عقدة جذر وهي تمثل الزر الرئيسي الذي سننقره لفتح أو إغلاق القائمة الدائرية، وعقدة Buttons
من النوع control
كحاوية تتضمن كافة الأزرار التي نريد عرضها في القائمة الدائرية، ونتأكد من ضبط قيمة الخاصية Mouse
ثم Filter
على القيمة Ignore
كي لا تعترض أفعال النقر على الفأرة. كما سنستخدم تسعة أزرار لعرض القدرات الخاصة من نوع العداد التنازلي Cooldown
.
الخطوة التالية هي إضافة السكريبت التالي للعقدة الجذر:
extends TextureButton class_name RadialMenuButton export var radius = 120 export var speed = 0.25 var num var active = false
يمثل المتغير radius
حجم القائمة وهو قطر الدائرة التي سنوزع عليها اﻷزرار، بينما يُستخدم المتغير speed
في تحديد سرعة تحريك أزرار القائمة فالقيم اﻷصغر هي اﻷسرع. ويحدد المتغير num
عدد اﻷزرار في القائمة، بينما يمثل المتغير active
راية flag تدل على إغلاق أو فتح القائمة.
func _ready(): $Buttons.hide() num = $Buttons.get_child_count() for b in $Buttons.get_children(): b.position = position
نبدأ بإعداد منطق القائمة في التابع ()ready_
وذلك بإخفاء جميع أزرار القائمة افتراضيًا وضبط المسافة بينها وبين الزر الرئيسي للقائمة. ثم نربط اﻹشارة pressed
للزر الرئيسي:
func _on_pressed(): disabled = true if active: hide_menu() else: show_menu()
سيخفي النقر على الزر القائمة أو يظهرها، ونحتاج أيضًا إلى تعطيل الزر أثناء عملية تحريك الرسومات، وإلا سيعيد النقر عليه توليد اﻹطارات البينية tween وإعادة التحريك من جديد:
func _on_tween_finished(): disabled = false if not active: $Buttons.hide()
عندما ينتهي تحريك اﻹطارات البينية، ننقل حالة الزر إلى تمكين مجددًا.
لنلقِ نظرة على الدالة ()show_menu
:
func show_menu(): $Buttons.show() var spacing = TAU / num active = true var tw = create_tween().set_parallel() tw.finished.connect(_on_tween_finished) for b in $Buttons.get_children(): #لوضع الزر اﻷول في اﻷعلى PI/2 اطرح var a = spacing * b.get_position_in_parent() - PI / 2 var dest = Vector2(radius, 0).rotated(a) tw.tween_property(b, "position", dest, speed).from(Vector2.ZERO).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT) tw.tween_property(b, "scale", Vector2.ONE, speed).from(Vector2(0.5, 0.5)).set_trans(Tween.TRANS_LINEAR)
نحسب في هذه الدالة المسافة spacing
أو بالأصح الزاوية التي نريدها بين كل عنصرين على القائمة، ومن ثم نتنقل بين اﻷزرار ونحدد وجهة كل زر dest
وفقًا للزاوية المحسوبة وقيمة نصف القطر radius
. ونولد لكل زر خاصيتين هما position
و scale
لإعطاء اﻷثر المرغوب عند توليد إطارات التحريك tween أثناء تحرك الزر. وتنفذ الدالة ()hide_menu
العكس تمامًا:
func hide_menu(): active = false var tw = create_tween().set_parallel() tw.finished.connect(_on_tween_finished) for b in $Buttons.get_children(): tw.tween_property(b, "position", Vector2.ZERO, speed).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_IN) tw.tween_property(b, "scale", Vector2(0.5, 0.5), speed).set_trans(Tween.TRANS_LINEAR)
ستبدو القائمة كالتالي:
الخاتمة
شرحنا في هذا المقال كيفية إنشاء أزرار عد تنازلي في محرك الألعاب جودو، والتي تتيح لنا ضبط فترات انتظار قبل إعادة استخدام الأزرار، وهي ميزة ضرورية للألعاب التي تعتمد على منح قدرات أو ميزات خاصة بعد فترة انتظار وتحديد فترة انتظار بعد كل استخدام. كما تناولنا آلية بناء قوائم دائرية توفر تجربة تفاعلية سلسة لعرض الأزار من خلال توزيع الأزرار بشكل منظم حول نقطة مركزية لتسهيل الوصول للخيارات المختلفة داخل اللعبة.
ترجمة -وبتصرف- للمقالين: CoolDown Button و Radial Popup Menu
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.