نشرح في هذا المقال كيفية استخدام جدول SpriteSheet لتنظيم حركة الشخصية في الألعاب الثنائية الأبعاد ضمن محرك الألعاب جودو، كما نوضح دور المتحكم AnimationTreeState Machine في تنظيم حركة الشخصية والتحكم في عمليات التنقل بين حالات الحركة المتختلفة.
الحركات داخل جدول الشخصيات SpriteSheet
تُعد جداول الشخصيات Spritesheets من الطرق الشائعة لتوزيع الحركات للرسوم المتحركة ثنائية البعد، إذ توضع جميع إطارات الشخصية ضمن صورة واحدة. سنستخدم في مقالنا شخصية المغامر، ويمكن الحصول عليها وعلى غيرها من الشخصيات من متجر Elthen's Pixel Art Shop الذي يوفر ملحقات مفيدة يمكن استخدامها في تطوير الألعاب.
تحذير: علينا التأكد من أن الصور في جدول الشخصيات مرتبة ضمن شبكة ذات حجم ثابت، مما يسمح لمحرك جودو باقتطاعها تلقائيًا، بينما إن وضعنا الشخصيات ضمن الجدول بشكل غير منتظم، فلن نتمكن من استخدام التقنيات التي نشرحها تاليًا.
إعداد عقد التحريك
تستخدم تقنية التحريك العقدة Sprite2D
لعرض الخامة، بعدها نحرّك الإطارات المتغيرة باستخدام العقدة AnimationPlayer
. يمكن لهذا الترتيب أن يعمل على أي عقدة ثنائية البعد، لكننا سنستخدم في مثالنا العقدة CharacterBody2D
.
لنضف العقد التالية إلى المشهد:
CharacterBody2D: Player Sprite2D CollisionShape2D AnimationPlayer
نسند جدول الشخصيات إلى الخاصية Texture
للعقدة Sprite2D
وسنلاحظ أن الجدول بكامله قد ظهر في نافذة العرض. ولكي نقطعه إلى إطارات فردية، نوسّع قسم التحريك Animation في نافذة الفاحص وناضبط قيم الخاصيتين Hframes
و Vframes
على 13
و 8
على الترتيب، وهما يمثلان عدد الإطارات الأفقية والعمودية في جدول الشخصيات:
لنجرّب تغيير الخاصية Frame
لمراقبة تغيّر الصورة، فهي الخاصية التي سنعمل على تحريكها.
تحريك الشخصية
سنختار العقدة AnimationPlayer
ثم ننقر الزر تحريك Animation يتبعه الزر جديد New ونسمي الحركة الجديدة idle. نضبط بعد ذلك مدة الحركة على 2
ثانية وننقر الزر Loop كي تتكرر الحركة باستمرار.
نجعل شريط التقدم Scrubber عند الزمن 0
ثم نختار العقدة Sprite2D
. نضبط الخاصية Animation>Frame
على 0
ثم ننقر على أيقونة المفتاح إلى جوار القيمة.
إن حاولنا الآن تشغيل الحركة فلن نرى شيئًا، لأن الإطار الأخير رقم 12 يبدو مشابهًا للإطار الأول رقم 0. مع ذلك لم نتمكن من رؤية الإطارات بينهما. لإصلاح الأمر نغير الخاصية Update Mode
للمسار من القيمة الافتراضية Discrete
إلى Continuous
وسنجد هذا الزر في نهاية المسار من الجانب الأيمن.
نلاحظ أن هذا الحل سيعمل فقط مع جداول الشخصيات، حيث تكون الشخصيات مرتبة مسبقًا، فإن لم يكن الأمر كذلك، علينا ترتيب كل إطار على حدى ضمن المسار.
يمكن تجربة وضع حركات أخرى مثل حركة القفز التي نجد صورها في الإطارات من 65 إلى 70.
استخدام المتحكم AnimationTreeStateMachine
لنتخيل أن لدينا كم كبير من الحركات، وأصبح من الصعب علينا التحكم عملية التنقل فيما بينها، وامتلأ السكريبت بعبارات if
وكلما أردنا تصحيح شيء أخفق ما تبقى. لحل الأمر نستخدم العقدة AnimationTree
لإنشاء مُتحكّم يسمح لنا بترتيب الحركات المختلفة للشخصية وإدارة عملية التنقل فيما بينها.
سنستخدم في مثالنا نفس شخصية المغامر التي استخدمناها في المثال السابق، ونفترض أننا هيأنا مسبقًا حركات الشخصية باستخدام العقدة AnimationPlayer
. وعندما نستخدم جدول الشخصيات السابق سنجد صورًا توافق الحركات التالية:
- سكون idle
- ركض run
- هجوم attack1
- هجوم attack2
- إصابة hurt
- موت die
استخدام شجرة الرسوميات AnimationTree
نضيف العقدة AnimationTree
إلى المشهد ثم نختار New AnimationNodeStateMachine من الخاصية TreeRoot
.
تتحكم العقدة AnimationTree
بالرسوميات التي تنشأ ضمن العقدة AnimationPlayer
، ولكي نسمح لها بالوصول إلى الرسوميات الموجودة، ننقر على الخاصية Assign
ضمن الخاصية Anim Player
ثم نختار عقدة الحركة.
يمكننا الآن إعداد متحكم التنقل ضمن نافذة AnimationTree
:
ننتبه إلى التحذير الظاهر، ونضبط الخاصية Active
في نافذة الفاحص على القيمة On
ثم ننقر بعد ذلك بالزر اليميني للفأرة ونختار Add Animation. نختار بعد ذلك الحركة idle وسنرى صندوقًا صغيرًا يمثل هذه الحركة. نكرر نفس العملية لإضافة مثل هذه الصناديق إلى بقية الحركات.
سنتمكن الآن من إضافة الاتصالات، لهذا ننقر على زر Connect nodes ثم نتنقل بالسحب بن العقد لوصلها مع بعضها. وكمثال على الاتصال سنستخدم الرسوم المتحركة لحالتي الهجوم:
عندما تختار حركة، ستتبع الشجرة المسار الذي يصل العقدة الحالية إلى الوجهة. لكن في طريقة إعداد المثال السابق، لن نرى الهجوم الأول attack1 إن شغلنا الهجوم الثاني attack2. يعود السبب في ذلك إلى أن نمط التبديل switch mode للاتصال نوعه مباشر immediate. لهذا، ننقر على زر Move/select ثم ننقر على الاتصال بين attack1 و attack2 ثم نغير من نافذة الفاحص الخاصية Switch Mode
إلى At End
ونكرر ذلك على الاتصال بين attack2 و idle.
ما يحدث الآن أنه عند تشغيل في AnimationTree، أنه عند الانتقال من idle إلى attack2، يجري تشغيل الحركتين attack1 و attack2 على التتابع، ولكن بعد ذلك تتوقف الرسوم المتحركة عند attack2 بدلاً من العودة تلقائيًا إلى idle. لحل هذه المشكلة، نضبط الخاصية Advance>Mode
على Auto
مما يسمح للشجرة بالعودة إلى الحركة idle بشكل تلقائي بعد تنفيذ حركتي الهجوم attack، ونلاحظ أن أيقونة الاتصال تتحول إلى اللون الأخضر لإظهار ذلك.
وهكذا ستُتنفذ الحركات على التتابع بمجرد تفعيلها.
استدعاء الحالات في الشيفرة
فيما يلي شجرة الحركات بأكملها:
لنهيئ الآن الشخصية كي تستخدم هذه الحركات:
extends CharacterBody2D var state_machine var run_speed = 80.0 var attacks = ["attack1", "attack2"] @onready var state_machine = $AnimationTree["parameters/playback"]
تضم الخاصية state_machine
مرجعًا إلى المتحكم بالحالة وهو AnimationNodeStateMachinePlayback
، ولاستدعاء حركة محددة، نستخدم التابع travel
الذي سيتّبع الاتصالات إلى الرسم المتحرك المحدد:
func hurt(): state_machine.travel("hurt") func die(): state_machine.travel("die") set_physics_process(false)
لدينا هنا مثال عن الدوال التي قد نستدعيها، إن أصيب اللاعب أو قتل. وبالنسبة إلى بقية الحالات كالركض والهجوم وغيرها فلا بد من جمعها مع شيفرة الحركة وشيفرة معالجة المدخلات. وستحدد الخاصية velocity
إن كنا سنرى حالة حركة الركض run أو حركة السكون idle:
func get_input(): var current = state_machine.get_current_node() velocity = Input.get_vector("move_left", "move_right", "move_up", "move_down") * run_speed if Input.is_action_just_pressed("attack"): state_machine.travel(attacks.pick_random()) return # اقلب الشخصية من اليمين إلى اليسار if velocity.x != 0: $Sprite2D.scale.x = sign(velocity.x) # اختر رسمًا متحركًا if velocity.length() > 0: state_machine.travel("run") else: state_machine.travel("idle") move_and_slide()
نلاحظ استخدام return
بعد الانتقال إلى حركة الهجوم كي نتمكن من الانتقال إلى حالات الحركة أو السكون لاحقًا في الدالة.
الخاتمة
تعرفنا في هذا المقال على طريقة استخدام SpriteSheet في جودو لتوليد حركات مختلفة للشخصية، كما تعرفنا على استخدام AnimationTree Animation Tree State Machine في إدارة التنقل بين الرسوميات المختلفة للشخصية. وبإمكانك من الاطلاع على المشروع بصيغته المكتملة لتنفيذه وفهمه بصورة أفضل.
ترجمة -وبتصرف- للمقالين: SpriteSheet ANimation و Using AnimationTreeStateMachine
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.