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

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

الكائنات القابلة لإعادة الاستخدام

سيطلق اللاعب كمًا كبيرًا من القذائف مع تقدم اللعبة وجميعها أشياء متطابقة تمامًا، وبالتالي سنتحتاج إلى تنفيذ ما يلي:

  • إظهار القذيفة أمام اللاعب
  • تحريك القذيفة نحو الأمام حتى تغادر الشاشة
  • التقاط حدث تصادم القذيفة مع الأعداء

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

مشهد القذيفة

سننشئ هنا مشهدًا جديدًا من خلال النقر على خيار المشهد Scene ثم على خيار مشهد جديد New Scene، أو بالنقر على الزر + في أعلى نافذة العرض.

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

وستكون العقد على النحو الآتي:

- Area2D: Bullet
  - Sprite2D
  - CollisionShape2D
  - VisibleOnScreenNotifier2D

سننقر على العقدة Sprite2D ثم نسحب الصورة Player_charged_beam (16 x 16).png من مدير الملفات إلى الخاصية Texture في نافذة الفاحص Inspector. وكما نلاحظ، توجد أكثر من نسخة للقذيفة، ولهذا سنضبط قيمة الخاصية Animation> Hframe على القيمة 2 كي نرى نسخةً واحدةً فقط في كل مرة.

سنضبط الآن شكل التصادم CollisionShape2D كما فعلنا في المقال السابق مع شكل التصادم الخاص بعقدة اللاعب.

سكريبت القذيفة

سنلحق الآن سكريبتًا جديدًا بالعقدة Bullet ونبدأ بتحريك القذيفة كالآتي:

extends Area2D

@export var speed = -250

func start(pos):
    position = pos

func _process(delta):
    position.y += speed * delta

من المفترض أن يكون الأمر مألوفًا، فهو مشابه لسكريبت اللاعب، وقد غيرنا فقط position.y لأن الحركة ستكون شاقوليةً نحو الأعلى.

يسمح لنا التابع ()start الذي عرّفناه بضبط موقع البداية position للقذيفة، لأن اللاعب سيتحرك باستمرار وينشر القذائف باتجاهات مختلفة.

ربط الإشارات Signals

نختار العقدة Bullet وننقر على النافذة الفرعية عقدة Node إلى جوار الفاحص:

01 signals

تعرض لقطة الشاشة السابقة جميع الإشارات Signals التي يمكن للعقدة المختارة أن تبثها. تخبرنا الإشارات في جودو أن شيئٍا ما قد حدث، وفي حالتنا سنستخدم الإشارة area_entered لتخبرنا متى تلامس فيها هذه القذيفة عقدةً أخرى من النوع Area2D. ولفعل ذلك، سنختار الإشارة area_entered، ثم ننقر على الزر توصيل Connect أسفل النافذة، بعدها ننقر على الزر وصل Connect في النافذة التي تظهر؛ إذ لسنا بحاجة إلى تغيير أي شيء فيها.

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

لنضف الآن هذه الشيفرة:

func _on_area_entered(area):
    if area.is_in_group("enemies"):
      area.explode()
      queue_free()

سنتحقق في هذه الشيفرة مما إذا كانت القذيفة قد أصابت عدوًا، بحيث إن فعلت ذلك، نطلب من العدو أن ينفجر، ومن ثم نمسح القذيفة مباشرةً.

نكرر نفس الخطوات السابقة لوصل الإشارة screen_exited العائدة إلى العقدة VisibleOnScreenNotifier2D ونضيف الشيفرة التالية في الدالة التي توّلدها الإشارة:

func _on_visible_on_screen_notifier_2d_screen_exited():
    queue_free()

إطلاق النار

يقدم المشهد Bullet كائنًا قابلًا لإعادة الاستخدام يمكن نسخه في كل مرة يطلق فيها اللاعب النار. ولضبط عملية إطلاق النار، سنتبع ما يلي.

إضافات إلى مشهد اللاعب

لنعد إلى سكريبت اللاعب Player ونضيف بعض المتغيرات الجديدة:

@export var cooldown = 0.25
@export var bullet_scene : PackedScene
var can_shoot = true

يساعدنا التوجيه export@ هنا في ضبط قيم المتغيرات ضمن نافذة الفاحص، وبالتالي سنكون قادرين على ضبط وقت الانتظار قبل إعادة إطلاق النار Cooldown Time.

نعود الآن إلى مشهد اللاعب، ثم نغيّر قيمة المتغير bullet_scene بالنقر على الخاصية الموافقة في الفاحص، ثم نختار الملف bullet.tscn، أو بسحب المشهد من نافذة نظام الملفات وسحبه إلى الخاصية.

يطلق المبرمجون على المتغير can_shoot تسمية الراية Flag، وهو متغير بولياني منطقي يتحكم بشرط معين. وفي حالتنا سيتحكم بإمكانية إطلاق اللاعب للنار أو لا، وستكون قيمة هذا المتغير في فترة الانتظار false.

سنضيف تاليًا دالة ()start مشابهة للتي أضفناها إلى مشهد القذيفة، والتي تساعدنا على ضبط بعض القيم الابتدائية للاعب وإعادة ضبطها عندما تبدأ اللعبة من جديد.

func _ready():
    start()

func start():
    position = Vector2(screensize.x / 2, screensize.y - 64)
    $GunCooldown.wait_time = cooldown

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

تُستدعى الدالة ()shoot في أي وقت نضغط فيه على مفتاح الإطلاق الذي يتفعّل عن ضغط الزر Space:

func shoot():
    if not can_shoot:
      return
    can_shoot = false
    $GunCooldown.start()
    var b = bullet_scene.instantiate()
    get_tree().root.add_child(b)
    b.start(position + Vector2(0, -8))

تتحقق الدالة بدايةً مما إذا ما كان إطلاق النار مسموحًا، فإن لم يكن كذلك، فسيُنهى تنفيذ الدالة مباشرةً باستخدام الأمر return. وإن كان مسموحًا للاعب بإطلاق النار، فسنضبط قيمة الراية على false ونبدأ العد لفترة الانتظار.

سننشئ بعذ ذلك نسخةً عن مشهد القذيفة ونضيفه إلى اللعبة، ونستدعي الدالة ()start الخاصة بالقذيفة للتأكد من وضعها في المكان المناسب فوق سفينة اللاعب.

يمكن استدعاء هذه الدالة عندما يضغط اللاعب على مفتاح الإطلاق، لهذا، نضيف ما يلي داخل الدالة ()process_ بعد السطر ()position.clamp:

if Input.is_action_pressed("shoot"):
    shoot()

نصل أيضًا الإشارة timeout العائدة للعقدة GunCooldown:

func _on_gun_cooldown_timeout():
    can_shoot = true

عندما تنتهي فترة الانتظار، يُسمح للاعب بإطلاق النار مجددًا؛ وهنا سنشغّل المشهد ونجرب التحرك وإطلاق النار.

02 shooting

وكما هو ظاهر في الصورة أعلاه، فقد أضفنا قذائف جديدة كأبناء لشجرة المشاهد get_tree().root وليس إلى مشهد اللاعب، لأننا لو فعلنا ذلك فستكون القذائف أبناءً للسفينة وتتحرك مع حركتها.

الخاتمة

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

ترجمة-وبتصرف- للمقالين: Bullet Scene و Shooting.

اقرأ أيضًا


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

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

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



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

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

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

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


×
×
  • أضف...