سنكتشف في هذا المقال متى يدخل أو يخرج كائن من الشاشة، وسنتعرّف على كيفية إنشاء وتحريك شخصية في ألعاب المنصات ثنائية الأبعاد.
إنشاء شخصية وتحريكها في ألعاب المنصات ثنائية الأبعاد
سننشئ الأن شخصيةً في ألعاب المنصات ثنائية الأبعاد. يُفاجَأ المطورون المبتدئون في أغلب الأحيان بمدى تعقيد برمجة شخصيات ألعاب المنصات، لذا يوفر محرّك ألعاب جودو Godot بعض الأدوات المُضمَّنة للمساعدة، ولكن يمكن القول بأن هناك عددًا كبيرًا من الحلول التي تضاهي عدد الألعاب الموجودة. لن نتعمق في شرح الميزات مثل ميزات القفزات المزدوجة أو الانحناء أو القفز على الحائط أو الرسوم المتحركة، بل سنناقش أساسيات الحركة في ألعاب المنصات.
ملاحظة: يمكن استخدام العقدة RigidBody2D
لإنشاء شخصية لألعاب المنصات، ولكننا سنركز على استخدام العقدة CharacterBody2D
، إذ تُعَد الأجسام الحركية Kinematic مناسبةً لألعاب المنصات، حيث لن تكون مهتمًا كثيرًا بالفيزياء الواقعية بقدر الاهتمام بالشعور المتجاوب في اللعبة.
سنبدأ بعقدة CharacterBody2D
، ونضيف إليها عقدتي Sprite2D
و CollisionShape2D
، ثم نرفق السكربت التالي بالعقدة الجذر للشخصية:
extends CharacterBody2D @export var speed = 1200 @export var jump_speed = -1800 @export var gravity = 4000 func _physics_process(delta): # إضافة الجاذبية في كل إطار velocity.y += gravity * delta # يؤثر الدخل على المحور x فقط velocity.x = Input.get_axis("walk_left", "walk_right") * speed move_and_slide() # السماح بالقفز عند وجود الشخصية على الأرض فقط if Input.is_action_just_pressed("jump") and is_on_floor(): velocity.y = jump_speed
يمكننا ملاحظة أننا نستخدم إجراءات الإدخال التي عرّفناها في خريطة الإدخال InputMap وهي: "walk_right"
و "walk_left"
و "jump"
، لذا يمكن الاطلاع على إجراءات الإدخال InputActions لمزيد من التفاصيل.
تعتمد القيم المُستخدَمة للسرعة speed
والجاذبية gravity
وسرعة القفز jump_speed
اعتمادًا كبيرًا على حجم الشخصية الرسومية Sprite الخاصة باللاعب، وتكون خامة Texture اللاعب 108x208 بكسل في هذا المثال، وإذا كانت الصورة أصغر حجمًا، فستستخدم قيمًا أصغر.
نريد أيضًا قيمًا عالية للشعور بالسرعة والاستجابة، إذ تؤدي الجاذبية المنخفضة إلى لعبة نشعر فيها بعدم التوازن؛ بينما تمثل القيمة العالية العودة إلى الأرض بسرعة والجاهزية للقفز مرةً أخرى.
سنتحقق من التابع is_on_floor()
بعد استخدام الدالة move_and_slide()
التي تضبط قيمة هذا التابع، لذا يجب عدم التحقق منه قبل ذلك، وإلّا سنحصل على القيمة من الإطار السابق.
الاحتكاك Friction والتسارع Acceleration
تُعَد الشيفرة البرمجية السابقة بداية جيدة، إذ يمكننا استخدامها كأساس لمجموعة متنوعة من متحكمات المنصات، ولكنها تواجه مشكلة الحركة اللحظية Instantaneous Movement، ولكن يمكننا الحصول على شعور طبيعي أكثر من خلال تسارع الشخصية مثلًا حتى تصل إلى سرعتها القصوى، ثم تتوقف عند عدم وجود دخل.
إحدى الطرق لإضافة هذا السلوك هي استخدام الاستيفاء الخطي Linear Interpolation أو "lerp"، بحيث ننتقل باستخدام الاستيفاء الخطي من السرعة الحالية إلى السرعة القصوى عند التحرك، وننتقل باستخدام الاستيفاء الخطي من السرعة الحالية إلى الصفر أثناء التوقف، وبالتالي سيوفر تعديل مقدار الاستيفاء الخطي مجموعة متنوعة من أنماط الحركة.
ملاحظة: يمكن الاطلاع على مقال الاستيفاء للحصول على مزيد من المعلومات عن الاستيفاء الخطي.
extends CharacterBody2D @export var speed = 1200 @export var jump_speed = -1800 @export var gravity = 4000 @export_range(0.0, 1.0) var friction = 0.1 @export_range(0.0 , 1.0) var acceleration = 0.25 func _physics_process(delta): velocity.y += gravity * delta var dir = Input.get_axis("walk_left", "walk_right") if dir != 0: velocity.x = lerp(velocity.x, dir * speed, acceleration) else: velocity.x = lerp(velocity.x, 0.0, friction) move_and_slide() if Input.is_action_just_pressed("jump") and is_on_floor(): velocity.y = jump_speed
سنجرب هنا تغيير قيم الاحتكاك friction
والتسارع acceleration
لمعرفة مدى تأثيرها على شعورنا أثناء اللعب. فقد يحتاج مستوى الجليد إلى قيم منخفضة جدًا مثلًا، مما يجعل الحركة أصعب.
تمنحنا هذه الشيفرة البرمجية نقطة بداية لبناء متحكم منصات خاص بك، لذا يمكن الاطلاع على المقالات اللاحقة لمزيد من ميزات المنصات المتقدمة مثل القفز على الحائط.
تحديد متى يدخل كائن إلى الشاشة أو يغادرها
يوفر محرّك الألعاب العقدة VisibleOnScreenNotifier2D
، حيث إذا أرفقنا هذه العقدة بالكائن، فسنتمكّن من استخدام إشارات screen_entered
و screen_exited
الخاصة بها.
تطبيق عملي 1
ليكن لدينا مثلًا مقذوف يتحرك في خط مستقيم بعد إطلاقه، وإذا استمرينا في الإطلاق، فسنحصل في النهاية على عدد كبير من الكائنات التي يتعقّبها المحرّك، حتى وإن كانت هذه الكائنات خارج الشاشة، مما قد يتسبب في حدوث تأخير.
توضح الشيفرة البرمجية التالية حركة المقذوف:
extends Area2D var velocity = Vector2(500, 0) func _process(delta): position += velocity * delta
يمكن حذف المقذوف تلقائيًا عند تحرّكه خارج الشاشة من خلال إضافة العقدة VisibleOnScreenNotifier2D
والاتصال بإشارة screen_exited
الخاصة بها.
func _on_VisibleOnScreenNotifier2D_screen_exited(): queue_free()
تطبيق عملي 2
ليكن لدينا مثلًا عدو ينفّذ بعض الإجراءات مثل التحرك على طول مسار أو تشغيل رسوم متحركة، ولن يظهر على الشاشة سوى عدد قليل من الأعداء في الوقت نفسه في خريطة كبيرة تحتوي على العديد من الأعداء، إذ يمكننا تعطيل إجراءات العدو أثناء وجوده خارج الشاشة باستخدام العقدة VisibleOnScreenNotifier2D
كما هو الحال في جزء الشيفرة البرمجية التالي:
var active = false func _process(delta): if active: play_animation() move() func _on_VisibleOnScreenNotifier2D_screen_entered(): active = true func _on_VisibleOnScreenNotifier2D_screen_exited(): active = false
الخاتمة
استعرضنا في هذا المقال كيفية استخدام العقدة VisibleOnScreenNotifier2D
لتحديد متى يدخل كائن إلى الشاشة أو يغادرها، مما يساعد في تحسين أداء اللعبة من خلال إدارة الكائنات غير المرئية بكفاءة؛ كما تناولنا أساسيات إنشاء شخصية وتحريكها في ألعاب المنصات ثنائية الأبعاد باستخدام العقدة CharacterBody2D
، مع التركيز على مفاهيم مثل الجاذبية، القفز، والتسارع للحصول على حركة سلسة واستجابة طبيعية.
يمكن تنزيل شيفرة المشروع البرمجية من Github للاطلاع عليه وفهمه أكثر.
ترجمة -وبتصرّف- للقسمين Entering/Exiting the screen و Platform character من توثيقات Kidscancode.
أفضل التعليقات
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.