تعرّفنا في المقال السابق على طريقة استيراد كائنات ثلاثية الأبعاد لمحرك الألعاب جودو وكيفية ترتيبها في مشهد اللعبة، وسنضيف في هذا المقال مزيدًا من الكائنات إلى مشهد اللعبة، وسنشرح طريقة إنشاء شخصية ثلاثية الأبعاد يتحكم فيها المستخدم.
بناء المشهد
سنستمر في هذا المقال استخدام مجموعة الملحقات assets التي توفرها منصة Kenney على هذا الرابط والتي شرحنا طريقة تنزيلها واستيرادها في مقال سابق. نفتح مشروع اللعبة ثلاثية الأبعاد التي بدأناها، ونحدّد الآن جميع ملفات block*.glb
، وننتقل إلى التبويب استيراد Import ونضبط نوع الجذر Root Type الخاص بهذه الملفات على StaticBody3D
، بعدها ننقر على زر إعادة الاستيراد Reimport كما في الصورة أدناه.
بعدها، نحدّد الكائن block-grass-large.glb
بزر الفأرة الأيمن ونختار إنشاء مشهد موروث جديد، ستظهر عقدة جديدة باسم block-grass-large في المشهد، وعقدة ابن لها تُمثّل المجسم Mesh، نحدد العقدة الابن وننقر فوق خيار مجسم Mesh في القائمة العلوية الظاهرة في محرر جودو، ثم نحدد خيار إنشاء شكل تصادم Create Collision Shape ونعيّن قيمة الحقل Collision Shape Placement لتكون Sibling وقيمة الحقل Collision Shape Type لتكون Trimmesh كما فعلنا بالضبط في المقال السابق.
عند الضغط على زر الإنشاء سيضيف جودو تلقائيًا العقدة CollionShape3D
مع شكل تصادم يطابق المجسم Mesh.
يمكننا الآن حفظ المشهد باسم BlockLarge
، ويفضل إنشاء مجلد منفصل وليكن platform_objects
لحفظ المشهد وكافة المشاهد الأخرى التي تمثل أجزاء منصة اللعبة بأشكالها المختلفة.
نفتح مشهد الأرضية Ground مع الصناديق Crates الذي عملنا عليه في المقال السابق ونحذف كافة الصناديق المضافة ونستبدلها بالمشهد الموروث الذي حفظناه للتو، بهذا سنتمكّن من وضع عدة كتل بجانب بعضها البعض بحيث تكون في صف واحد وتشكل منصة اللعبة أو عالم اللعبة. بعدها نحدّد العقدة BlockLarge وننقر خيار تعديل المحاذاة Configure Snap من القائمة العلوية التحوّل Transform الظاهرة أعلى نافذة العرض كالتالي:
نضبط خيار ترجمة المحاذاة Translate Snap على القيمة 0.5، ثم ننقر على زر استخدام المحاذاة Snap Mode أو نضغط على مفتاح Y
، ثم نكرّر الكتلة عدة مرات ونسحبها لترتيبها ضمن المشهد بالشكل المناسب. يمكن أيضًا إضافة مشاهد لبعض كتل المنصة الأخرى وترتيبها في أيّ شكل نريده.
إضافة شخصية 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 تشير إلى الاتجاه الموجب.
ملاحظة: أضفنا في هذه الشخصية أيضًا مجسمين كرويين بنفس الطريقة لتمثيل عيني الشخصية، ويمكن إضافة أية تفاصيل نريدها.
لنضف الآن عقدة كاميرا 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()
للتحرك في اتجاه متجه السرعة. نشغّل اللعبة لاختبارها، وسنرى النتيجة التالية:
هذا يبدو جيدًا، ولكن يجب أن نكون قادرين على تدوير الشخصية باستخدام الفأرة، لذا سنضيف الشيفرة البرمجية التالية إلى سكربت الشخصية:
func _unhandled_input(event): if event is InputEventMouseMotion: rotate_y(-event.relative.x * mouse_sensitivity)
عندما نحرك الفأرة أفقيًا في اتجاه المحور x سيعمل السكربت بتدوير الشخصية حول المحور الرأسي Y، وبالتالي إذا حركنا الفأرة إلى اليمين، سيؤدي ذلك إلى تدوير الشخصية إلى اليسار. وإذا حركنا الفأرة إلى اليسار ستدور الشخصية إلى اليمين.
لدينا هنا مشكلة، إذ يحرّكنا الضغط على مفتاح 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 الخاص بالكائن.
القفز 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.
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.