يعد تحريك الشخصيات عنصرًا أساسيًا في تطوير الألعاب، ويوفر لنا محرك جودو أدوات مدمجة تساعدنا على إنشاء حركات واقعية وجذابة للشخصيات، سنركز في هذا المقال على شرح استخدام محرر التحريك لجعل شخصيات لعبتنا ثلاثية الأبعاد تطفو وتلتف، ونوضح كيفية استخدام التعليمات البرمجية للتحكم في تحريك الشخصيات وتفاعلها.
لنبدأ بمقدمة حول استخدام محرر التحريك Animation Editor المدمج في محرك ألعاب جودو.
استخدام محرر التحريك Animation Editor
يُستخدم محرر التحريك في جودو لإنشاء حركات للشخصية حيث يتضمن المحرك العديد من أدوات التحريك التي تسهّل إنشاء حركات معقدة وديناميكية مثل حركة المشي أو الركض أو الدوران، ويمكننا بعدها ربط هذه الحركات بالتعليمات البرمجية لتشغيلها عندما تتفاعل الشخصيات مع بعضها أو عندما ينفّذ اللاعب إجراء معين كالضغط على زر ما والتحكم فيها في وقت التشغيل.
إنشاء حركة اللاعب
لنبدأ بإنشاء حركة اللاعب، سنفتح مشهد اللاعب ونحدد العقدة Player
ثم نضيف لها العقدة AnimationPlayer
هذه العقدة هي المسؤولة عن تشغيل الحركات، بعد إضافة العقدة ستظهر لوحة خاصة بالتحريك باسم Animation في اللوحة السفلية.
كما تبين الصورة أعلاه، نجد في الأعلى شريط أدوات وقائمة منسدلة للتحريك تمكننا من الاختيار بين أنواع الحركات المختلفة، وفي المنتصف محرر مسار track editor dock وهو فارغ حاليًا وستُعرض فيه لاحقًا المسارات التي تتبعها العناصر المتحركة، وفي الأسفل خيارات للتصفية والالتقاط والتكبير و التصغير.
لنبدأ بإنشاء حركة، ننقر على القائمة المنسدلة تحريك Animation في الأعلى، ثم نختار جديد New.
ينبغي علينا تسمية التحريك الجديد باسم مناسب وليكن float أي طفو أو عوم.
بمجرد إنشاء التحريك، سيظهر لنا مخطط زمني timeline يعرض أرقامًا تمثل الوقت بالثواني ويسمح لنا بمراقبة تغير خصائص الشخصية عبر الزمن أثناء تشغيل التحريك.
نريد أن يبدأ تشغيل الحركة تلقائيًا في بداية اللعب، وأن تتكرر عملية التحريك بدون توقف، ويمكننا تحقيق بذلك عن طريق النقر فوق الزر الذي يحمل أيقونة A+ في شريط أدوات التحريك والذي يعني التشغيل التلقائي Autoplay، ثم تفعيل الأسهم التي تدل على تكرار الحركة كما في الصورة التالية.
يمكننا أيضًا تثبيت محرر التحريك من خلال النقر على أيقونة الدبوس الظاهرة أعلى يمين الصورة، وهذا يمنعها من الطي عند النقر فوق إطار العرض وإلغاء تحديد العقد.
سنضبط مدة الحركة إلى 1.2
ثانية في الجزء العلوي الأيمن من الشريط، وهذا هذا يعني أن الحركة التي أنشأناها ستستغرق مدة ثانية و 200 ميلي ثانية لإتمامها بالكامل من بدايتها إلى نهايتها.
ينبغي أن نرى الآن الشريط الرمادي يتسع قليلاً، ليُظهر لنا بداية ونهاية الحركة التي صنعناها ونلاحظ الخط الأزرق العمودي الذي يمثل مؤشر الوقت.
يمكننا النقر على شريط التمرير وسحبه في الجزء السفلي الأيسر لتكبير وتصغير المخطط الزمني.
إنشاء حركة الطفو Float Animation
سنعمل على إنشاء حركة الطفو التي تجعل الشخصيات تبدو وكأنها عائمة في الهواء بسلاسة دون تأثيرات مثل الجاذبية، حيث يمكننا تنفيذ الحركات من خلال تحريك معظم الخصائص وعلى أي عدد نريده من العقد باستخدام العقدة AnimationPlayer
التي أضفناها سابقًا.
فبعد إضافة هذه العقدة سنلاحظ وجود رمز مفتاح بجوار الخصائص المختلفة في الفاحص Inspector كالموضع ووالحجم، وغيرها. يمكننا النقر فوق أي منها لإنشاء إطار رئيسي Keyframe لهذه الخاصية والذي يحتوي على زوج من القيم الأولى تمثل الوقت والثانية تمثل قيمة الخاصية المقابلة في هذا الوقت، سيُدرج الإطار الرئيسي حيث يوجد مؤشر الوقت في المخطط الزمني، فإذا كان مؤشر الوقت عند اللحظة 1 في المخطط الزمني، سيضاف الإطار الرئيسي في تلك اللحظة.
دعونا ندرج الإطار الأول، سنجري هنا تحريكًا لكل من الموضع Position والتدوير Rotation للعقدة character
، لذا ينبغي علينا تحديد العقدة character
ثم فتح قسم التحويل Transform في الفاحص Inspector، ثم النقر فوق رمز المفتاح بجوار خاصيتي الموضع والتدوير.
عند النقر على رمز المفتاح يطلب منا تحديد نوع المسار الجديد للخاصية.
بالنسبة لسلسلتنا، سنختار إنشاء مسارات RESET وهو الخيار الافتراضي حيث يظهر مساران في المحرر، ويمثل رمز المعين كل إطار رئيسي keyframe.
يمكننا النقر على المعين وسحبه لتحريكه للوقت المناسب، سنحركه في مسار الموضع إلى 0.3
ثانية وفي مسار التدوير إلى 0.1
ثانية.
كما سنحرك مؤشر الوقت إلى 0.5
ثانية عن طريق النقر والسحب على المخطط الزمني الرمادي.
الآن نعود إلى الفاحص Inspector ونعيّن قيمة المحور Y لخاصية الموضع على 0.65
متر ، والمحور X لخاصية التدوير على 8
درجات.
بهذا نكون قد أنشأنا إطار رئيسي لكل من خاصيتي الموضع و التدوير.
لنحرّك الآن الإطار الرئيسي للموضع إلى0.7
ثانية عن طريق سحبه على المخطط الزمني.
ملاحظة: ليس هدفنا في هذه السلسلة شرح مبادئ التحريك بالتفصيل، لكن يكفي أن نضع بعين الاعتبار عدم تحديد وقت ومسافة متساوية لكل شيء بالتساوي حيث يتلاعب مختصو التحريك عادة بالتوقيت والتباعد، وهما المبدآن الأساسيان للتحريك. لكوننا نريد الموازنة والتباين في حركة الشخصية ونجعلها أكثر حيوية.
الآن لنحدد كيف ستنتهي الحركة ونحرك مؤشر الوقت إلى الموضع 1.2
ثانية. ثم نضبط المحور Y لخاصية الموضع على القيمة 0.35
والمحور X لخاصية التدوير على 9-
درجة، والآن ننشئ مرة أخرى مفتاح لكل خاصية.
يمكننا معاينة النتيجة بالنقر فوق زر التشغيل أو الضغط على Shift + D
. و لإيقاف التشغيل ننقر فوق زر الإيقاف أو نضغط على S
.
سنلاحظ أن المحرك يجري عملية تداخل Interpolation بين الإطارات الرئيسية Keyframes التي أنشأناها لإنتاج حركة مستمرة. لكن تبدو الحركة في الوقت الحالي مصطنعة جدًا. وذلك لأن التداخل الافتراضي خطي، مما يسبب انتقالات بتسارع ثابت constant transitions، وهذا يخالف الكيفية التي تتحرك فيها الكائنات الحية في عالمنا الحقيقي.
يمكننا التحكم في طريقة الانتقال بين الإطارات الرئيسية باستخدام ما يسمى بمنحنيات التخفيف easing curves. ننقر ونسحب بين المفتاحين الأوليين في المخطط الزمني لتحديدهما ضمن مربع.
يمكننا بعدها تحرير خصائص كلا المفتاحين في وقت واحد في الفاحص Inspector، حيث يمكنك رؤية خاصية التخفيف Easing.
انقر على المنحني الظاهر في الصورة واسحبه نحو اليسار. سيؤدي ذلك إلى جعل الحركة تتباطئ أو تخف تدريجيًا Ease-out، أي تنتقل سريعًا في البداية وتتباطأ عندما يصل مؤشر الوقت إلى الإطار الرئيسي التالي.
عندما نشغّل الحركة مرة أخرى سنرى الفرق، يجب أن يبدو النصف الأول من الحركة الآن أكثر مرونة.
بعدها نطبّق التخفيف Easing على الإطار الرئيسي الرئيسي الثاني في مسار التدوير.
سنفعل العكس بالنسبة للإطار الرئيسي الثاني لخاصية الموضع ونسحبه إلى اليمين.
الآن سوف يطفو اللاعب عند تشغيل اللعبة وستبدو الحركة كما يلي:
ملاحظة: تؤثر الحركات التي أضفناها على خصائص العقد المتحركة في كل إطار، مما يعيد تعريف القيم الأولية التي حددناها. لذلك، إذا حركنا عقدة اللاعب مباشرة، فهذا سيمنعنا من تحريكها باستخدام التعليمات البرمجية. وهنا يأتي دور العقدة المحورية Pivot
فعلى الرغم من أننا حركنا الشخصية، فلا يزال بإمكاننا تحريك المحور Pivot
وتدويره لإضافة تغييرات إضافية على الحركة في السكربت البرمجي.
يمكننا على سبيل المثال تحريك Pivot
لأعلى في حال كان اللاعب الذي أنشأناه قريبًا جدًا من الأرض.
التحكم بالحركة من خلال الشيفرة البرمجية
يمكننا التحكم في تشغيل الحركة التي أنشأناها برمجيًا بناء على مدخلات اللاعب. لنجرب على سبيل المثال تغيير سرعة الحركة عندما تتحرك الشخصية.
نفتح السكربت البرمجي للعقدة Player
من خلال النقر على أيقونة السكربت المجاورة لها.
نضيف الشيفرة التالية في الدالة _physics_process()
، بعد السطر الذي نتحقق فيه من المتجه direction
كما يلي:
func _physics_process(delta): #... if direction != Vector3.ZERO: #... $AnimationPlayer.speed_scale = 4 else: $AnimationPlayer.speed_scale = 1
تعمل هذه الشيفرة على تسريع الحركة عن طريق ضرب سرعة التشغيل في 4
، ثم إعادة ضبطها إلى الوضع الطبيعي بمجرد توقف اللاعب.
وقد ذكرنا أن pivot
يمكن أن يساعدنا في تطبيق حركة إضافية على الشخصية. ويمكننا استعماله هنا لجعل الشخصية تشكل قوس عندما تقفز بإضافة الكود التالي في نهاية الدالة _physics_process()
.
func _physics_process(delta): #... $Pivot.rotation.x = PI / 6 * velocity.y / jump_impulse
تحريك الأعداء
من النصائح المفيدة التي تساعدنا على تنفيذ التحريك في جودو هي نسخ واستخدام العقد بمشاهد مختلفة طالما أن هذه العقد تتماثل في البنية، فهذا يوفر علينا الوقت والجهد في إعادة إعداد المشاهد.
على سبيل المثال، يحتوي كل من مشهدي اللاعب Player
والعدو Mob
على عقدة محورية pivot
وعقدة الشخصية character
، لذا في هذه الحالة يمكننا إعادة استخدام الحركة بينهما.
لنفتح مشهد اللاعب player.tsc
، ونحدد العقدة AnimationPlayer
ونفتح التحريك float. بعد ذلك، ننقر فوق التحريك Animation ومن ثم نحتار نسخ Copy.
ثم نفتح مشهد العدو mob.tscn، وننشئ عقدة فرعية من نوع AnimationPlayer
ونحددها، ثم نضغط على تحريك Animation ثم إدارة التحريك Manage Animations ونختار إضافة مكتبة Add Library، سنرى رسالة مفادها "Global library will be created" أي ستنشأ مكتبة عامة نترك الحقل النصي فارغًا ونضغط زر موافق.
بعدها نضغط على أيقونة لصق Paste وستظهر الحركة التي نسخناها في النافذة، نضغط زر موافق لإغلاق النافذة. بعد ذلك، علينا التأكد من تفعيل زر التشغيل التلقائي +A وأسهم تكرار الحركة في محرر التحريك في اللوحة السفلية. هذا كل شيء علينا فعله حيث ستتحرك جميع شخصيات الأعداء الآن بحركة العوم float أيضًا.
يمكننا تغيير سرعة التشغيل بناءً على السرعة العشوائية random_speed
للعدو. لذا سنفتح سكربت العقدة Mob
ونضيف في نهاية دالة initialize()
السطر التالي:
func initialize(start_position, player_position): #... $AnimationPlayer.speed_scale = random_speed / min_speed
بإتمام هذه الخطوات، نكون قد أكملنا برمجة أول لعبة ثلاثية الأبعاد بشكل كامل باستخدام محرك جودو، مما يفتح أمامنا الكثير من الإمكانيات لتطوير ألعاب احترافية أكثر إبداعًا.
الكود الكامل للعبة ثلاثية الأبعاد بمحرك جودو
فيما يلي محتوى ملف السكربت Player.gd
الخاص بعقدة اللاعب Player
extends CharacterBody3D signal hit # سرعة حركة اللاعب مقدرة بالمتر في الثانية @export var speed = 14 # التسارع نحو الأسفل في الهواء مقدرة بالمتر في الثانية مربع @export var fall_acceleration = 75 # الدفعة العامودية المطبقة على الشخصية عند القفز مقدرة بالمتر في الثانية @export var jump_impulse = 20 # الدفعة العامودية المطبقة على الشخصية عند القفز على عدو مقدرة بالمتر في الثانية @export var bounce_impulse = 16 var target_velocity = Vector3.ZERO func _physics_process(delta): # أنشأنا متغير محلي لتخزين دخل الاتجاه var direction = Vector3.ZERO # نتحقق من كل دخل حركة ونحدث الاتجاه حسبه if Input.is_action_pressed("move_right"): direction.x = direction.x + 1 if Input.is_action_pressed("move_left"): direction.x = direction.x - 1 if Input.is_action_pressed("move_back"): # لاحظ أننا نعمل المحورين x و z الخاصين بالشعاع # المستوي XZ هو مستوي الأرض في ثلاثي الأبعاد direction.z = direction.z + 1 if Input.is_action_pressed("move_forward"): direction.z = direction.z - 1 # يمنع الحركة القطرية السريعة if direction != Vector3.ZERO: direction = direction.normalized() $Pivot.look_at(position + direction,Vector3.UP) $AnimationPlayer.speed_scale = 4 else: $AnimationPlayer.speed_scale = 1 # السرعة الأرضية target_velocity.x = direction.x * speed target_velocity.z = direction.z * speed # السرعة العمودية if not is_on_floor(): # إذا كان في الهواء يسقط على الأرض أي الجاذبية target_velocity.y = target_velocity.y - (fall_acceleration * delta) # القفز if is_on_floor() and Input.is_action_just_pressed("jump"): target_velocity.y = jump_impulse # كرر خلال كل الاصطدامات التي تحصل في الإطار # يكون ذلك في C كالتالي # (int i = 0; i < collisions.Count; i++) for index in range(get_slide_collision_count()): # نحصل على واحد من التصادمات مع اللاعب var collision = get_slide_collision(index) # إذا كان التصادم مع الأرض if collision.get_collider() == null: continue # إذا كان التصادم مع العدو if collision.get_collider().is_in_group("mob"): var mob = collision.get_collider() # نتحقق أننا نصدمه من الأعلى if Vector3.UP.dot(collision.get_normal()) > 0.1: # إذا كان كذلك نسحقه mob.squash() target_velocity.y = bounce_impulse # يمنع أي استدعاءات مكررة. break # تحريك الشخصية velocity = target_velocity move_and_slide() $Pivot.rotation.x = PI / 6 * velocity.y / jump_impulse # أضف ذلك في الأسفل. func die(): hit.emit() queue_free() func _on_mob_detector_body_entered(body): die()
وفيما يلي ملف السكربت mob.gd
الخاص بعقدة العدو Mob
extends CharacterBody3D # السرعة الدنيا للعدو مقدرة بالمتر في الثانية @export var min_speed = 10 # السرعة القصوى للعدو مقدرة بالمتر في الثانية @export var max_speed = 18 # تنبثق عندما يقفز اللاعب على عدو signal squashed func _physics_process(_delta): move_and_slide() # يتم استدعاء هذه الدالة من المشهد الأساسي func initialize(start_position, player_position): # نحدد مكان العدو عن طريق وضعه في start_position # وندوره نحو player_position لينظر إلى اللاعب look_at_from_position(start_position, player_position, Vector3.UP) #تدوير العدو عشوائيًا ضمن مجال -90 و +90 درجة # لكي لا تتحرك مباشرة نحو اللاعب rotate_y(randf_range(-PI / 4, PI / 4)) # نحسب سرعة عشوائية (عدد صحيح) var random_speed = randi_range(min_speed, max_speed) # نحسب سرعة امامية تمثل السرعة velocity = Vector3.FORWARD * random_speed # ثم ندور شعاع السرعة اعتمادًا على دوران العدو حول Y # للحركة في الاتجاه الذي ينظر إليه العدو velocity = velocity.rotated(Vector3.UP, rotation.y) func _on_visible_on_screen_notifier_3d_screen_exited(): queue_free() func squash(): squashed.emit() queue_free() # دمّر هذه العقدة
الخلاصة
بهذا نكون وصولنا لنهاية السلسلة التي شرحنا فيها تطوير لعبة ثلاثية الأبعاد في جودو، وتعلّمنا في هذا الدرس كيفية تحريك الشخصية بواسطة محرر التحريك المضمن الموجود في جودو، مما سيضفي طابعًا من الحيوية إلى لعبتك. لا تدع هذه اللعبة تكون آخر لعبة تعمل عليها، فهذا طريق البداية فحسب!
ترجمة - وبتصرف - لقسم Character animation من توثيق جودو الرسمي.
اقرأ أيضًا
- المقال السابق: إضافة النقاط واللعب مجددًا وتأثيرات الصوت للعبة 3D ضمن جودو
- قتل اللاعب عند الاصطدام بالعدو ضمن لعبة 3D بجودو
- العقد Nodes والمشاهد Scenes في جودو Godot
- أشهر أنواع الألعاب الإلكترونية
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.