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

سنتعرف في هذا المقال على طريقة بناء أزرار عد تنازلي countdown لتحقيق ميزة الانتظار في لعبتنا كي لا تتفعّل ميزة ما إلا بعد مضي فترة زمنية معينة، كما سنشرح طريقة بناء قائمة دائرية Radial menu تعرض عدة خيارات موزعة على شكل حلقة مما يسهّل الوصول لخيارات هذه القائمة ويضيف طابعًا فريدًا لواجهة الاستخدام.

بناء أزرار العد التنازلي

قد نرغب في إضافة عدة أزرار تمنح اللاعب مهارات أو قدرات خاصة ability buttons مع توفير ميزة الانتظار لفترة معينة قبل تمكين اللاعب من النقر على كل زر منها واكتساب القدرة المطلوبة، يمكننا تحقيق ذلك من خلال ميزة العد التنازلي countdown، وفي حال احتجنا لأيقونات ورسومات مناسبة لاستخدامها مع هذه الأزرار فستجد كمًا كبيرًا من التصاميم المناسبة في موقع Game-icons.net وسنستخدم بعضها في مقالنا.

001 زر القدرة الخاصة

إعداد مشهد اللعبة

يضم المشهد الذي سنعمل عليه العقد 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  ونختار قيمتها لتكون بلون رمادي قاتم مع إضافة بعض الشفافية لجعل الزر يبدو باهتًا في وضع الانتظار.

02 cooldown color

نضبط عقدة المؤقت 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 وسنحصل على شريط أفقي من أزرار القدرة كما يلي:

03 cooldown ready

بناء قائمة دائرية منبثقة

تُستخدم القوائم في العديد من اﻷلعاب للوصول إلى ميزات أو وظائف معينة، كأن نحدد من خلالها المهمة المطلوب تنفيذها في اللعبة حاليًا مثل التحدث أو التفتيش أو الهجوم وهكذا. ينبغي أن يكون مظهر وسلوك القائمة متلائمًا مع لعبتنا، لكننا سنركز في هذا المثال على آلية بناء قوائم دائرية Radial Menu ونترك لك حرية تنسيقها. توضح الصورة التالية قائمة العقد المطلوبة لتنفيذ القائمة:

04 ui 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)

ستبدو القائمة كالتالي:

05 ui radial menu ready

الخاتمة

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

ترجمة -وبتصرف- للمقالين: CoolDown Button و Radial Popup Menu

اقرأ أيضًا

image.png


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

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

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



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

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

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

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


×
×
  • أضف...