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

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

ننصح قبل بدء العمل بإلقاء نظرة على توثيق الواجهة البرمجية RigidBody2D لفهم خصائصها بشكل أعمق.

بناء الحركة

سننشئ مشهد جديد ثنائي الأبعاد في جودو ونستخدم فيه العقد التالية:

RigidBody2D (Ship)
├── Sprite2D
└── CollisionShape2D

وإليكم وظيفة كل عقدة منها:

  • تمثل RigidBody2D(Ship) العقدة الرئيسية للجسم الصلب الذي نريد تحريكه، والذي يتفاعل مع البيئة ويتأثر بقوانين الفيزياء
  • تمثل العقدة Sprite2D الشكل المرئي للجسم الصلب، وهي في حالتنا صورة سفينة الفضاء التي تتحرك تلقائيًا مع الجسم الفيزيائي الصلب
  • تستخدم العقدة CollisionShape2D لتحديد شكل وحجم منطقة التصادم للجسم الصلب، وهي ضرورية كي تتعرف الفيزياء على حدود الجسم وتتفاعل معه بشكل صحيح عند اصطدامه بأجسام أخرى

توجيه الشخصية

عندما نحرك جسم صلب RigidBody2D في جودو، فإن اتجاهه الأمامي الافتراضي أي الاتجاه الذي يتحرك فيه يُعد دومًا هو الاتجاه الموجب للمحور X أي نحو اليمين في المشهد، لذا علينا في البداية توجيه الجسم الصلب بالشكل الصحيح إذا لم يكن كذلك. هذا الأمر مهم لأننا عندما نطبق قوى أو سرعة على الجسم فإن هذه القيم تُحسب بناءً على المحاور المحلية للجسم، لذا إذا لم يكن الجسم موجّهًا أصلاً نحو اليمين فستكون حركته غير صحيحة أو غير متوقعة. وإن كانت الأيقونة أو صورة الشخصية تتجه باتجاه معاكس، علينا أن ندوّر العقدة Sprite2D وليس الجسم الأب نفسه RigidBody2Dحتى نوجهها بالشكل الصحيح.

كما سنستخدم المدخلات التالية في خريطة الإدخال Input Map:

المُدخل المفتاح
thrust w أو ↑
rotate_right d أو →
rotate_left a أو ←

بعدها، سنضيف سكريبت إلى الجسم الصلب ونعرّف فيه بعض المتغيرات:

extends RigidBody2D

@export var engine_power = 800
@export var spin_power = 10000

var thrust = Vector2.ZERO
var rotation_dir = 0

يحدد أول متغيران كيفية التحكم بحركة سفينة الفضاء. إذ يتحكم المتغير engine_power بالتسارع أي سرعة الحركة للأمام، بينما يتحكم المتغير spin_power بسرعة دوران السفينة. ويُضبط كل من thrust و rotation_dir بناءً على مدخلات المستخدم، وهذا ما سنفعله تاليًا:

func get_input():
    thrust = Vector2.ZERO
    if Input.is_action_pressed("thrust"):
      thrust = transform.x * engine_power
    rotation_dir = Input.get_axis("rotate_left", "rotate_right")

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

يمكن للسفينة الفضائية البدء بالطيران عند تطبيق القيم التالية في الدالة (physics_process(delta_.

func _physics_process(_delta):
    get_input()
    constant_force = thrust
    constant_torque = rotation_dir * spin_power

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

لننتقل إلى خصائص العقدة ومنها إلى Linear ثم Damp و Angular ثم Damp ونضبط قيمتي هاتين الخاصيتين على 1 و 2 على الترتيب. تحدّ هاتان القيمتان من سرعة الحركة وسرعة الدوران مما يسبب توقف السفينة الفضائية.

ملاحظة: يمكن أن نجرّب تغيير قيم هذه الخاصيات ونرى كيف تتفاعل مع engine_power و spin_power.

العودة إلى الشاشة

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

للتغلب على هذه المشكلة، نستخدم دالة رد النداء ()integrate_forces_ الخاصة بالعقدة RigidBody2D، والتي تتيح لنا تعديل الخصائص الفيزيائية مثل الموقع والسرعة بشكل مباشر ومتزامن مع دورة محرك الفيزياء دون أن تتعارض معها.

لننتقل الآن إلى screensize في أعلى السكريبت:

@onready var screensize = get_viewport_rect().size

نضيف بعد ذلك دالة جديدة باسم ()integrate_forces_:

func _integrate_forces(state):
    var xform = state.transform
    xform.origin.x = wrapf(xform.origin.x, 0, screensize.x)
    xform.origin.y = wrapf(xform.origin.y, 0, screensize.y)
    state.transform = xform

نلاحظ أن الدالة ()integrate_forces_  تستقبل معاملًا باسم state، وهو كائن من النوع PhysicsDirectBodyState2D يمثل الحالة الفيزيائية الحالية للجسم الصلب. يحتوي هذا الكائن على معلومات مثل موضع الجسم position والقوى المؤثرة forces والسرعة velocity في الاتجاهات المختلفة وغيرها من الخصائص الفيزيائية.
وبالتالي يمكن أن نحصل من خلال state على التحويل الحالي للجسم  current transform أي موقعه واتجاهه، ثم نعدّل إحداثيات الموقع باستخدام الدالة ()wrapf لتطبيق التفاف حول الشاشة مما يعني أن الجسم سيتجاوز حافة الشاشة ويظهر من الجهة المقابلة لإعطاء تأثير الحركة المستمرة. وأخيرًا، نُعيد ضبط التحويل الجديد داخل state لضمان استمرار حركة الجسم بشكل طبيعي وواقعي.

ستبدو الحركة كما في الصورة التالية:

01 asteroids wrap

الانتقال الفوري

لنلقِ نظرة على مثال آخر يوضح استخدام ()integrate_forces_ لتغيير حالة الجسم دون مشكلات. لنضف آلية انتقال آني أو فوري بحيث يتمكن اللاعب من نقل المركبة الفضائية فورًا إلى موقع عشوائي داخل الشاشة عند الضغط على مفتاح مخصص.

نضيف بداية متغيرًا جديدًا:

var teleport_pos = null

نحضّر تاليًا موقعًا عشوائيًا ضمن الدالة ()get_input:

    if Input.is_action_just_pressed("warp"):
      teleport_pos = Vector2(randf_range(0, screensize.x), randf_range(0, screensize.y))

وأخيرًا وضمن الدالة ()integrate_forces_ سنستخدم teleport_position إن كان موجودًا ومن ثم نمسحه:

    if teleport_pos:
      physics_state.transform.origin = teleport_pos
      teleport_pos = null

02 asteroids warp

وبهذا نكون قد أضفنا ميزة الانتقال الفوري للمركبة الفضائية بشكل آمن ومتوافق مع نظام فيزياء جودو.

الخاتمة

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

ترجمة -وبتصرف- للمقال: Asteroids-style Physics (using RigidBody2D)

اقرأ أيضًا


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

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

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



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

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

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

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


×
×
  • أضف...