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

نتعرف في هذا المقال على طريقة بناء شريط صحة Health Bar يضم قلوبًا أو غيرها من اﻷيقونات، كما سنتعرف على طريقة عرض نسبة تضرر الشخصية في لعبة على شكل نص يطفو فوق الشخصية.

ثلاث طرق لبناء شريط صحة يضم قلوبًا

من الطرق الشائعة في إظهار صحة اللاعب عرض سلسلة من اﻷيقونات -غالبًا بشكل قلوب- يختفي بعضها عندما يتعرض اللاعب إلى ضرر. وسنناقش ثلاث طرق لعرض الأيقونات أطلقنا عليها تسميات بسيطة simple وفارغة empty وجزئية partial.

01 heart bar

تعرض الصورة السابقة ثلاث حالات ممكنة لعرض شريط الصحة:

  • الطريقة البسيطة: تعرض القلوب ممتلئة بالكامل
  • الطريقة الفارغة: تعرض قلوب فارغة وأخرى ممتلئة
  • الطريقة الجزئية: تعرض القلوب نصف ممتلئة

إعداد شريط اﻷيقونات

نستخدم في هذا المثال صور قلوب أبعادها 53x45 حصلنا عليها من موقع Kenney.nl: Platformer Art Deluxe. ومن المفترض أن يكون وضع الشريط ضمن شاشة عرض معلومات المستخدم HUD أو واجهة المستخدم UI سهلًا، لذا من المنطقي أن نبني هذا الشريط ضمن مشهد مستقل.

سنبدأ بعقدة من النوع Node2D كي نُبقى اﻷمور على نفس السوية، ونضبط قيمة الخاصية Sepration ضمن Constants في القسم  Theme Overrides من الفاحص على القيمة 5.

نضيف بعد ذلك عقدة ابن من النوع TextureRect ثم نسحب أيقونة القلب إلى الخاصية Texture ونضبط قيمة Strech Mode على Keep. نعيد تسمية العقدة لتكون 1 ثم باستخدام مفتاحي Ctrl+D ننسخ هذه العقدة بعدد القلوب التي نريد عرضها في الشريط 5 مثلًا. ستبدو لوحة العقد في محرك جودو كالتالي:

02 heart bar nodes

إضافة السكريبت

يغطي السكريبت التالي حالات الشريط الثلاث التي ذكرناها، حيث سنحمّل في البداية الخامات وهي هنا اﻷيقونات التي نحتاجها ونعرف اﻷشرطة الثلاث، وتجدر الملاحظة بأن الكود سيغطي جميع حالات الشريط الثلاثة، وقد نحتاج لاستخدام حالة واحدة فقط في اللعبة، عندها نزيل الكود المتعلق بالحالات الأخرى كما يلي:

extends HBoxContainer

enum modes {SIMPLE, EMPTY, PARTIAL}

var heart_full = preload("res://assets/hud_heartFull.png")
var heart_empty = preload("res://assets/hud_heartEmpty.png")
var heart_half = preload("res://assets/hud_heartHalf.png")

@export var mode : modes

func update_health(value):
    match mode:
      MODES.simple:
        update_simple(value)
      MODES.empty:
        update_empty(value)
      MODES.partial:
        update_partial(value)

يؤدي استدعاء الدالة ()update_health العائدة إلى الشريط عرض القيمة الممرة إليه وفقًا للنمط المختار.

ملاحظة: لن نضيف آليات تحقق من حدود القيمة المدخلة كالتأكد مثلًا من أن الصحة بين 0 و 100، فهناك طرق كثيرة لعرض الصحة في اﻷلعاب لذا سنترك الأمر لكم.

نتنقل في الدالة ()update_simple بين أشرطة الأيقونات ونضبط ظهور كل عقدة TextureRect:

func update_simple(value):
    for i in get_child_count():
      get_child(i).visible = value > i

واﻷمر مشابه في الدالة ()update_empty ما عدا أننا نغير اﻷيقونة إلى اﻷيقونة الفارغة بدلًا من إخفائها:

func update_empty(value):
    for i in get_child_count():
      if value > i:
        get_child(i).texture = heart_full
      else:
        get_child(i).texture = heart_empty

أما في الحالة اﻷخيرة، فلدينا أيقونة ثالثة وضعف القيم الممكنة فمن خلال إنقاص القيمة بمقدار 1 مثلًا يعطي نصف قلب وإنقاص 1 مرة أخرى تعطي قلبًا فارغًا:

func update_partial(value):
    for i in get_child_count():
      if value > i * 2 + 1:
        get_child(i).texture = heart_full
      elif value > i * 2:
        get_child(i).texture = heart_half
      else:
        get_child(i).texture = heart_empty

توضح الصورة أدناه مثالًا عن عمل كل شريط:

03 heart bar final

إنشاء نصوص طافية فوق الشخصية

هناك طرق عدة لتحقيق النصوص الطافية floating text، منها استخدام خط كتابة نقطية bitmap font وبناء صورة لكل عدد انطلاقًا من اﻷرقام المكونة له، ومن ثم استخدام العقدة Sprite2D لعرض وتحريك النص الناتج.

04 fct demo

لكن ما سنفعله في مقالنا هو استخدام العقدة Label واسمها FCT وبهذا سنمتلك مرونة في تغيير الخط إضافة إلى سهولة عرض اﻷعداد كنصوص أو عرض نصوص أخرى مثل "أخفق miss".

نضيف المورد الذي نريده في الخاصية Label Settings ونختار خطًا مناسبًا وقياسًا مناسبًا له، وقد استخدمنا في المثال الخط Xolonium.ttf والقياس 28 مع إطار خارجي أسود بعرض 4 بكسل.

نضيف اﻵن السكريبت التالي إلى العقدة Label:

extends Label

func show_value(value, travel, duration, spread, crit=false):

نستدعي عند توليد النصوص الطافية الدالة ()show_value التي تضبط قيم المعاملات التالية:

  • value وهو العدد أو النص الذي نريد توليده
  • travel وهو عقدة شعاع Vector2 التي تمثل اتجاه حركة النص أو العدد
  • duration تحدد كم سيبقى النص على قيد الحياة
  • spread يحدد أن الحركة ستكون عشوائية عبر هذا القوس
  • crit يشير لأن الضرر كبير في حال كانت قيمته true

وهذا ما تفعله الدالة ()show_value:

    text = value
    var movement = travel.rotated(rand_range(-spread/2, spread/2))
    rect_pivot_offset = rect_size / 2

تضبط الدالة قيمة النص أو العدد ومن ثم تجعل حركته عشوائية وفقًا لقيمة الانتشار spread مابين 90+ و90- مثلًا. وقد نغير أبعاد النصوص المتحركة، لهذا ضبطنا قيمة الخاصية rect_pivot_offset لتمثل مركز عنصر التحكم وبالتالي يكون تغيير اﻷبعاد منسوبًا إلى المركز.

    $Tween.interpolate_property(self, "rect_position",
        rect_position, rect_position + movement,
        duration, Tween.TRANS_LINEAR, Tween.EASE_IN_OUT)
    $Tween.interpolate_property(self, "modulate:a",
        1.0, 0.0, duration,
        Tween.TRANS_LINEAR, Tween.EASE_IN_OUT)

نجري بعد ذلك استيفاء interpolation على قيمتي الخاصية بين لحظتين هما rect_position لتحريك العدد الطافي و modulate.a ﻹخفاء هذا النص بشكل تدريجي وسلس:

    if crit:
      modulate = Color(1, 0, 0)
      $Tween.interpolate_property(self, "rect_scale",
        rect_scale*2, rect_scale,
        0.4, Tween.TRANS_BACK, Tween.EASE_IN)

إن كانت اﻹصابة بالغة، سنغير لون النص ونزيد حجمه لإظهار التأثير. وتجدر الملاحظة بأننا حددنا لون النص في هذه الحالة ليكون أحمر ومن اﻷفضل أن تعرف متحولًا لإسناد قيمة اللون الذي نريده:

    $Tween.start()
    yield($Tween, "tween_all_completed")
    queue_free()

نبدأ بعد ذلك عملية بناء اﻹطارات البينية من خلال التعليمة Tween وننتظر حتى تنتهي ثم نزيل العنوان Label.

إدراة النصوص الطافية FCTManager

ننشئ اﻵن عقدة صغيرة تدير توليد النصوص وتحديد مكانها، وستُلحق بكيانات اللعبة التي نريد أن نضيف إليها تأثير النصوص الطافية. نسمي هذه العقدة من النوع Node2D بالاسم FCTManager ونضيف إليها السكريبت التالي:

extends Node2D

var FCT = preload("res://FCT.tscn")

export var travel = Vector2(0, -80)
export var duration = 2
export var spread = PI/2

func show_value(value, crit=false):
    var fct = FCT.instance()
    add_child(fct)
    fct.show_value(str(value), travel, duration, spread, crit)

يمكن تعديل ما نريده من خصائص العقدة من خلال نافذة الفاحص Inspector، لكن الدالة ()show_value أيضًا تولد النص الطافي وتضبط خصائصه. وبإمكاننا إلحاق نسخة من هذه العقدة بأي وحدة من وحدات اللعبة نريدها أن تمتلك تأثير النصوص الطافية، ثم نضيف كود مشابه لما يلي ضمن التابع ()take_damage للوحدة:

$FCTManager.show_value(dmg, crit)

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

الخاتمة

تعرفنا في هذا المقال على طريقة بناء شريط صحة يضم أيقونات تعرض حالة اللاعب مثل تناقص صحته أو نفاذ ذخيرته، كما تحدثنا عن أحد طرق لتوليد نصوص تطفو حول الشخصية وتختفي للدلالة على حالة معينة مثل مقدار اﻹصابة التي تلقتها.

ترجمة -وبتصرف- للمقالين: HeartContainers: 3 ways و Floating Combat Text

اقرأ أيضًا


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

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

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



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

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

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

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


×
×
  • أضف...