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

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

بناء المشهد

سنستمر في هذا المقال استخدام مجموعة الملحقات assets التي توفرها منصة Kenney على هذا الرابط والتي شرحنا طريقة تنزيلها واستيرادها في مقال سابق. نفتح مشروع اللعبة ثلاثية الأبعاد التي بدأناها، ونحدّد الآن جميع ملفات block*.glb، وننتقل إلى التبويب استيراد Import ونضبط نوع الجذر Root Type الخاص بهذه الملفات على StaticBody3D ، بعدها ننقر على زر إعادة الاستيراد Reimport كما في الصورة أدناه.

001 ضبط نوع الجذر

بعدها، نحدّد الكائن block-grass-large.glb بزر الفأرة الأيمن ونختار إنشاء مشهد موروث جديد، ستظهر عقدة جديدة باسم block-grass-large في المشهد، وعقدة ابن لها تُمثّل المجسم Mesh، نحدد العقدة الابن وننقر فوق خيار مجسم Mesh في القائمة العلوية الظاهرة في محرر جودو، ثم نحدد خيار إنشاء شكل تصادم Create Collision Shape ونعيّن قيمة الحقل Collision Shape Placement لتكون Sibling وقيمة الحقل Collision Shape Type لتكون Trimmesh كما فعلنا بالضبط في المقال السابق.

عند الضغط على زر الإنشاء سيضيف جودو تلقائيًا العقدة CollionShape3D مع شكل تصادم يطابق المجسم Mesh.

002 مجسم وتصادم للكائن ثلاثي الأبعاد

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

نفتح مشهد الأرضية Ground مع الصناديق Crates الذي عملنا عليه في المقال السابق ونحذف كافة الصناديق المضافة ونستبدلها بالمشهد الموروث الذي حفظناه للتو، بهذا سنتمكّن من وضع عدة كتل بجانب بعضها البعض بحيث تكون في صف واحد وتشكل منصة اللعبة أو عالم اللعبة. بعدها نحدّد العقدة BlockLarge وننقر خيار تعديل المحاذاة Configure Snap من القائمة العلوية التحوّل Transform الظاهرة أعلى نافذة العرض كالتالي:

003 تعديل المحاذاة

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

004 مشهد المنصة

إضافة شخصية Character

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

نضيف عقدة MeshInstance3D على شكل كبسولة، وعقدة CollionShape3D مطابقة لها، ولنتذكّر أن بإمكاننا إضافة عقدة StandardMaterial3D إلى المجسم Mesh وضبط خاصية اللون Color في القسم Albedo. شكل الكبسولة جميل ومناسب للشخصية، ولكن سيكون من الصعب معرفة الاتجاه الذي يمثل الوجه، لذا لنضيف مجسم Mesh آخر على شكل مخروطي CylinderMesh3D، ونضبط نصف قطره العلوي Top Radius على القيمة 0.2 ونصف قطره السفلي Bottom Radius على القيمة 0.001 وارتفاعه Height على القيمة 0.5، ثم نضبط دورانه حول المحور x على ‎-90 درجة. أصبح لدينا الآن شكل مخروطي جميل، لذا نرتّبه بحيث يشير لخارج الجسم على طول محور z السالب، إذ يمكن بسهولة معرفة الاتجاه السالب لأن أسهم أداة Gizmo تشير إلى الاتجاه الموجب.

005 character 01

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

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

كود التحكم في الشخصية

قبل إضافة سكربت برمجي للتحكم في الشخصية، سوف نفتح إعدادات المشروع Project Settings، ونضيف المدخلات التالية في تبويب خريطة الإدخال Input Map:

إجراء الإدخال Input Action المفتاح Key
التحرك للأمام move_forward المفتاح W
التحرك للخلف move_back المفتاح S
الانعطاف لليمين strafe_right المفتاح D
الانعطاف لليسار strafe_left المفتاح A
القفز jump المسافة Space

يمكننا الآن كتابة سكربت لتحريك شخصيتنا ثلاثية الأبعاد كما يلي:

extends CharacterBody3D

var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")
var speed = 4.0  # سرعة الحركة
var jump_speed = 6.0  # تحديد ارتفاع القفزة
var mouse_sensitivity = 0.002  # سرعة الدوران


func get_input():
    var input = Input.get_vector("strafe_left", "strafe_right", "move_forward", "move_back")
    velocity.x = input.x * speed
    velocity.z = input.y * speed

func _physics_process(delta):
    velocity.y += -gravity * delta
    get_input()
    move_and_slide()

نلاحظ أن الشيفرة البرمجية في الدالة ‎_physics_process()‎ بسيطة، حيث نضيف الجاذبية للتسارع في الاتجاه الموجب للمحور Y إلى الأسفل، ثم نستدعي الدالة get_input()‎ للتحقق من الإدخال أي معرفة المفتاح الذي ضغط المستخدم عليه، ثم نستخدم التابع move_and_slide()‎ للتحرك في اتجاه متجه السرعة. نشغّل اللعبة لاختبارها، وسنرى النتيجة التالية:

006 3d move 01

هذا يبدو جيدًا، ولكن يجب أن نكون قادرين على تدوير الشخصية باستخدام الفأرة، لذا سنضيف الشيفرة البرمجية التالية إلى سكربت الشخصية:

func _unhandled_input(event):
    if event is InputEventMouseMotion:
        rotate_y(-event.relative.x * mouse_sensitivity)

عندما نحرك الفأرة أفقيًا في اتجاه المحور x سيعمل السكربت بتدوير الشخصية حول المحور الرأسي Y، وبالتالي إذا حركنا الفأرة إلى اليمين، سيؤدي ذلك إلى تدوير الشخصية إلى اليسار. وإذا حركنا الفأرة إلى اليسار ستدور الشخصية إلى اليمين.

007 3d move 02

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

الاستفادة من التحويلات Transforms

التحويل هو مصفوفة رياضية تحتوي على معلومات حول انتقال الكائن ودورانه وتغيير حجمه في آن واحد، ويُخزَّنه جودو في نوع البيانات Transform، حيث تُسمَّى معلومات الموضع بالاسم transform.origin وتكون معلومات الاتجاه في transform.basis.

تشير محاور X و Y و Z الخاصة بأداة Gizmo على طول محاور الكائن نفسه عندما تكون في وضع الحيّز المحلي Local Space Mode، ويشابه ذلك الأساس basis الخاص بالتحويل، حيث يحتوي هذا الأساس على ثلاثة كائنات Vector3 تسمى x و y و z تمثل هذه الاتجاهات، ويمكننا استخدامها لضمان أن الضغط على مفتاح W سيؤدي إلى التحرك في الاتجاه الأمامي للكائن دائمًا.

نعدّل الدالة get_input()‎ كما يلي:

func get_input():
    var input = Input.get_vector("strafe_left", "strafe_right", "move_forward", "move_back")
    var movement_dir = transform.basis * Vector3(input.x, 0, input.y)
    velocity.x = movement_dir.x * speed
    velocity.z = movement_dir.z * speed

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

007_3d_move_02.gif

القفز Jumping

سنضيف الآن حركة أخرى إلى اللاعب وهي القفز، لتحقيق ذلك نضيف الأسطر التالية إلى نهاية الدالة ‎_unhandled_input()‎:

if event.is_action_pressed("jump") and is_on_floor():
    velocity.y = jump_speed

تحسين الكاميرا

إذا وقفت الشخصية بالقرب من عائق ما، فيمكن للكاميرا الالتصاق بالكائن بطريقة غير لطيفة. وبالرغم من أن برمجة كاميرا ثلاثية الأبعاد قد تكون أمرًا معقدًا بحد ذاته، ولكن يمكننا استخدام عقد جودو المضمنة للحصول على حل جيد لذلك. نحذف العقدة Camera3D من مشهد الشخصية ونضيف العقدة SpringArm3D التي تعمل كذراع متحركة تحمل الكاميرا أثناء كشف التصادمات، وستقرّب الكاميرا عند وجود عقبة، ثم نضبط خاصية طول الذراع Spring Length على القيمة 5، ونضبط خاصية الموضع Position على القيم ‎(0, 1, 0)‎. نلاحظ ظهور خط بلون أصفر يشير إلى طول النابض Spring Length، حيث ستتحرك الكاميرا على طول هذا الخط، ولكنها ستقترب عند وجود عقبة حتى نهايته كلما أمكن ذلك.

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

الخلاصة

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

ترجمة -وبتصرّف- للقسم Creating a 3D Character من توثيقات Kidscancode.

اقرأ أيضًا


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

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

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



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

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

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

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


×
×
  • أضف...