البحث في الموقع
المحتوى عن 'godot'.
-
سنتحدث في هذا الدرس عن الإشارات Signals في محرك الألعاب جودو، التي هي عبارة عن رسائل تصدرها العقد عندما يحصل داخلها شيء أو حدث معين، مثل حدث الضغط على أحد الأزرار عندها يطلق هذا الزر إشارة، ويمكن أن تتصل عقد أخرى مع هذه الإشارة وتستدعي دالة كي تنفذ عند حصول الحدث. تعد الإشارات طريقة للتفويض delegation مضمّنة في محرك الألعاب جودو فهي تسمح لكائن ما بالتفاعل لتغيير كائن آخر دون الحاجة إلى أن تكون الكائنات متصلة ببعضها البعض مباشرة، مما يقلل من الترابط بين الكائنات ويبقى الكود مرنًا. مثلًا قد يكون لديك شريط حياة health bar يمثل صحة اللاعب، تريد أن يتغير شريط الحياة عندما يتضرر اللاعب أو يستخدم عقار لزيادة الصحة، يجب استخدام الإشارات للقيام بذلك. ملاحظة: الإشارات في جودو هي تطبيق لنمط المراقب Observer Pattern في البرمجة وهو نمط تصميم يُستخدم لتمكين كائن (المراقب) من الاستجابة لتغييرات تحدث في كائن آخر دون أن يكون هناك ارتباط مباشر بينهما لتقليل الترابط بين الأجزاء المختلفة من البرنامج وتسهيل إدارة التغييرات والتحديثات بشكل أكثر مرونة.، يمكنك التعلم المزيد عنه هنا وفي الفقرات التالية سنستخدم الإشارات لجعل أيقونة جودو التي حركناها في الدرس السابق تتحرك وتتوقف عن طريق ضغط الأزرار. إعداد المشهد لإضافة زر للعبة يجب إنشاء مشهد أساسي جديد يضم كلًّا من الزر والمشهد sprite_2d.tscn الذي أنشأناه في درس كتابة أول كود برمجي خاص بك في جودو. أنشئ مشهد جديد في جودو عن طريق الذهاب إلى قائمة "مشهد" Scene ومن ثم اختر "مشهد جديد" New Scene في قائمة "المشهد" Scene انقر زر 2D الذي سيضيف عقدة Node2D كجذر للمشهد. انقر واسحب ملف sprite_2d.tscn المحفوظ سابقًا في Node2D في قائمة FileSystem لاستنساخه. انقر بالزر الأيمن للفأرة على Node2D واختر إضافة عقدة لإضافة عقدة أخرى Sprite2D كشقيق أي في نفس المستوى العقدة المحددة. ابحث عن عقدة الزر Button وأضفها كما يلي: تكون هذه العقدة صغيرة افتراضيًا، انقر واسحب المقبض من الزاوية اليمينية السفلى للزر في مجال العرض لتغيير حجمها إذا لم تظهر لك المقابض تأكد من أن أداة الاختيار مفعلة في شريط الأدوات. انقر على الزر واسحبه لتقريبه أكثر من الأيقونة. يمكنك أيضًا كتابة عنوان للزر عن طريق تعديل خاصية النص الخاصة به في الفاحص Inspector، وادخل على Toggle motion. يجب أن تكون شجرة المشهد وإطار الرؤية على النحو التالي: احفظ المشهد باسم node_2d.tscn إن لم تقم بذلك حتى الآن، يمكنك تشغيله عن طريق F6 (أو Cmd+R على macOS)، إن الزر سيكون واضح، ولكن لن يحصل شيء إذا ضغطته. ربط الإشارة بالمحرر نريد ربط إشارة ضغط الزر مع Sprite2D الخاص بنا، ونريد استدعاء دالة جديدة تفعّل أو تبطل حركته، إذ نحن بحاجة شيفرة برمجية مرتبط مع عقدة Sprite2D الذي قمنا به في الدرس السابق. يمكن ربط الإشارات في قائمة العقدة عن طريق اختيار زر العقدة على يمين المحرر والنقر على التبويبة المسماة "عقدة Node" بجانب "الفاحص Inspector". تظهر القائمة عدد من الإشارات المتوافرة للعقدة المختارة انقر نقرة مزدوجة على إشارة "مضغوط pressed" لفتح نافذة ارتباط العقدة يمكنك هنا ربط الإشارة مع عقدة Sprite2D التي تحتاج إلى تابع مستقبل أي إلى دالة تستدعيها جودو عندما يصدر الزر الإشارة، وسينشئ المحرر هذه الدالة لك تلقائيًا، تسمى هذه العقد اصطلاحًا "onnodenamesignalname" وقد أسميناها هنا "onbuttonpressed". ملاحظة: يمكننا استخدام نمطين عند ربط الإشارات باستخدام قائمة محرر العقدة، يسمح لك النمط الأول بربط العقد التي لديها أكواد برمجية مرتبطة معها ويُنشئ تلقائيًا دالة رد نداء عليهم كي تُنفذ عندما يتم إرسال الإشارة وبالتالي يمكنك تحديد ما يجب فعله عند استلام الإشارة من خلال كتابة الأكواد داخل دالة رد النداء هذه. ويتيح لك نمط المشاهدة المتقدمة الذي يوفره جودو إعدادات أكثر تفصيلًا عند ربط الإشارات، بما يتجاوز الخيارات الأساسية المتاحة في الوضع الافتراضي، ويمكِّنك من إجراء الربط مع أي عقدة ودالة مضمّنة وإضافة الوسائط للاستدعاء وضبط كافة الخيارات، يمكنك تفعيل هذا النمط في أسفل ويمين النافذة عن طريق النقر على مفتاح التبديل Advanced كما في الصورة السابقة لتحديد الدالة المخصصة في الكود يدويًا والتي تريد أن يتم تنفيذها عند استلام الإشارة. انقر على زر الربط لإنهاء ارتباط الإشارة والانتقال إلى مكان عمل البرنامج النصي، يجب أن ترى تابعًا جديدًا مع أيقونة الإشارة على الهامش الأيسر. إذا نقرت على الأيقونة مُظهرة معلومات عن الارتباطستظهر نافذة منبثقة تعرض تفاصيل حول كيفية ارتباط الإشارة بالعقدة والدالة المستدعاة والخيارات الإضافية، وهذه الميزة موجودة فقط عند ربط العقد في المحرر. لنستبدل السطر الذي يتضمن الكلمة المفتاحية pass في الشيفرة بشيفرة فعلية تنفذ حركة العقدة. ستتحرك Sprite2D بفضل الشيفرة في دالة _process()، إذ تقدم جودو تابعًا مضمنًا لتفعيل أو تعطيل المعالجة ويمكنك تفعيل أو تعطيل المعالجة المستمرة للدالة _process() على عقدة معينة فإذا مررت true للتابع Node.set_process() سينفذ _process() في كل إطار مما يسمح لك بتحديث الموقع أو الحالة بمرور الوقت؛ وإذا مررت false فلن تنفذ وستتوقف التحديثات المستمرة للعقدة. يفيد تابع آخر لصنف العقدة is_processing() في التحقق مما إذا كانت المعالجة المستمرة المنفذة في الدالة _process() مفعلة أم لا حيث تعيد القيمة true إذا كانت المعالجة الخاصة بالعقدة مفعلة والدالة _process() تنفذ في كل إطار، وتعيد false إذا كانت معالجة العقدة غير نشطة، ويمكننا استخدام الكلمة المفتاحية not لعكس القيمة. بلغة GDScript: func _on_button_pressed(): set_process(not is_processing()) بلغة C#: private void OnButtonPressed() { SetProcess(!IsProcessing()); } تفعل هذه السيفرة دالة المعالجة والتي بدورها تفعل أو تعطل حركة الأيقونة عند ضغط الزر. نحتاج قبل تجربة اللعبة لتبسيط دالة _Process() لنقل العقدة تلقائيًا والانتظار لمدخلات المستخدم، استبدلها بالشيفرة التالية التي شاهدناها منذ درسين سابقين. بلغة GDScript: func _process(delta): rotation += angular_speed * delta var velocity = Vector2.UP.rotated(rotation) * speed position += velocity * delta بلغة C#: public override void _Process(double delta) { Rotation += _angularSpeed * (float)delta; var velocity = Vector2.Up.Rotated(Rotation) * _speed; Position += velocity * (float)delta; } يجب أن تكون شيفرة sprite_2d.gd كالتالي: بلغة GDScript: extends Sprite2D var speed = 400 var angular_speed = PI func _process(delta): rotation += angular_speed * delta var velocity = Vector2.UP.rotated(rotation) * speed position += velocity * delta func _on_button_pressed(): set_process(not is_processing()) بلغة C#: using Godot; public partial class MySprite2D : Sprite2D { private float _speed = 400; private float _angularSpeed = Mathf.Pi; public override void _Process(double delta) { Rotation += _angularSpeed * (float)delta; var velocity = Vector2.Up.Rotated(Rotation) * _speed; Position += velocity * (float)delta; } private void OnButtonPressed() { SetProcess(!IsProcessing()); } } شغل المشهد الآن وانقر على الزر لترى كيفية تحرك وتوقف الأيقونة مع النقر. ربط الإشارة باستخدام الشيفرة يمكنك ربط الإشارات باستخدام الشيفرة بدلًا من استخدام المحرر، هذا ضروري عند إنشاء العقد أو استنساخ المشاهد دخل البرنامج النصي. لنستخدم عقدة مختلفة، إن لجودو عقدة مؤقت زمني Timer تفيد في تحديد أوقات المهارات أو أوقات إعادة تلقيم الأسلحة النارية في الألعاب. عد إلى مساحة العمل عن طريق النقر على "2D" أعلى الشاشة أو الضغط على Ctrl+F1 (أو Ctrl+Cmd+1على macOS) انقر بالزر الأيمن على عقدة Sprite2D في قائمة "المشهد" Scene وأضف عقدة ابن جديدة، وابحث عن المؤقت Timer وأضف العقدة المرافقة، يجب أن يكون المشهد كالتالي اذهب إلى "الفاحص" Inspector بعد اختيار عقدة المؤقت لتفعيل خاصية البدء التلقائي. انقر على أيقونة السكريبت بجانب Sprite2D للانتقال إلى مكان عمل البرنامج النصي سنحتاج إلى إجراء عمليتين لربط العقد عن طريق الشيفرة البرمجية: الحصول على مرجع من المؤقت إلى Sprite2D استدعاء التابع connect() على إشارة المؤقت "timeout" ملاحظة: تحتاج استدعاء تابع connect() للإشارة التي تريد أن تستمع إليها لربط الإشارة باستخدام الشيفرة، وفي حالتنا نريد الاستماع لإشارة "timeout" الخاصة بالمؤقت. نريد ربط الإشارة عند استنساخ المشهد، ويمكنن القيام بذلك باستخدام الدالة المضمّنة Node._ready() التي يتم استدعائها تلقائيًا عن طريق المحرك عندما يتم استنساخ المشهد بالكامل. نستخدم الدالة Node.get_node() للحصول على مرجع بالنسبة للعقدة الحالية، ويمكننا تخزين المرجع في متغير. بلغة GDScript: func _ready(): var timer = get_node("Timer") بلغة C#: public override void _Ready() { var timer = GetNode<Timer>("Timer"); } تنظر الدالة get_node() إلى أبناء Sprite2D وتحصل على العقد بأسمائها، مثلًا إذا أعدت تسمية عقدة المؤقت إلى "BlinkingTimer" في المحرر، فيجب عليك تغيير الاستدعاء إلى get_node("BlinkingTimer") يمكننا الآن ربط المؤقت إلى Sprite2D في دالة _ready() بلغة GDScript: func _ready(): var timer = get_node("Timer") timer.timeout.connect(_on_timer_timeout) بلغة C#: public override void _Ready() { var timer = GetNode<Timer>("Timer"); timer.Timeout += OnTimerTimeout; } يُقرأ السطر كالتالي: ربطنا إشارة المؤقت "timeout" للعقدة التي يرتبط بها السكربت، وعندما يصدر المؤقت timeout نريد استدعاء الدالة _on_timer_timeout() التي نحتاج لتعريفها، لنضف ذلك إلى أسفل الشيفرة البرمجية ونستخدمه لتفعيل شفافية الأيقونة. ملاحظة: تسمى دالة رد النداء callback اصطلاحًا باسم GDScript "OnNodeNameSignalName" وستكون هنا باسم "_on_timer_timeout" في كود GDScript وباسم "()OnTimerTimeout" في كود #C. بلغة GDScript: func _on_timer_timeout(): visible = not visible بلغة C#: private void OnTimerTimeout() { Visible = !Visible; } إن الخاصية visible بوليانية وتتحكم بشفافية العقدة، يتفعل السطر visible = not visible وإذا كانت القيمة visible هي true تصبح false والعكس صحيح سترى الأيقونة الآن تظهر وتختفي كل ثانية إذا شغلت المشهد البرنامج النصي الكامل لقد انتهينا من أيقونة جودو المتحركة التي تومض والملف التالي هو ملف sprite_2d.gd كاملًا كمرجع. بلغة GDScript: extends Sprite2D var speed = 400 var angular_speed = PI func _ready(): var timer = get_node("Timer") timer.timeout.connect(_on_timer_timeout) func _process(delta): rotation += angular_speed * delta var velocity = Vector2.UP.rotated(rotation) * speed position += velocity * delta func _on_button_pressed(): set_process(not is_processing()) func _on_timer_timeout(): visible = not visible بلغة C#: using Godot; public partial class MySprite2D : Sprite2D { private float _speed = 400; private float _angularSpeed = Mathf.Pi; public override void _Ready() { var timer = GetNode<Timer>("Timer"); timer.Timeout += OnTimerTimeout; } public override void _Process(double delta) { Rotation += _angularSpeed * (float)delta; var velocity = Vector2.Up.Rotated(Rotation) * _speed; Position += velocity * (float)delta; } private void OnButtonPressed() { SetProcess(!IsProcessing()); } private void OnTimerTimeout() { Visible = !Visible; } } الإشارات المخصصة هذا القسم هو مرجع لكيفية تعريف واستخدام الإشارات الخاصة بك، ولا يبني على المشروع المُنشئ في الدروس السابقة يمكنك تعريف إشارات مخصصة في برنامج نصي. مثلًا تريد أن تظهر شاشة "انتهت اللعبة" عندما تصل حياة اللاعب للصفر، يمكنك تعريف إشارة اسمها "died" أو "health_depleted" للقيام بذلك عندما تصل حياتهم للصفر. إليك الشيفرة بلغة GDScript: extends Node2D signal health_depleted var health = 10 وبلغة C#: using Godot; public partial class MyNode2D : Node2D { [Signal] public delegate void HealthDepletedEventHandler(); private int _health = 10; } ملاحظة: تمثل الإشارات أحداثًا حصلت للتو، ويمكننا استخدام أفعال بالزمن الماضي في تسميتها. تعمل إشاراتك الخاصة مثل تلك المضمّنة في محرك جودو أي تظهر في تبويبة العقد ويمكنك ربطها بنفس الطريقة لِبَثّ إشارة في برنامج النصي تحتاج لاستدعاءemit() على الإشارة بلغة GDScript: func take_damage(amount): health -= amount if health <= 0: health_depleted.emit() بلغة C#: public void TakeDamage(int amount) { _health -= amount; if (_health <= 0) { EmitSignal(SignalName.HealthDepleted); } } ويمكن أن تصرح الإشارة على وسيط واحد أو أكثر من خلال تحديد أسماء الوسطاء ما بين قوسين. بلغة GDScript: extends Node signal health_changed(old_value, new_value) var health = 10 بلغة C#: using Godot; public partial class MyNode : Node { [Signal] public delegate void HealthChangedEventHandler(int oldValue, int newValue); private int _health = 10; } ملاحظة: تظهر وسائط الإشارة في قائمة محرر العقد، وتستخدمها جودو لإنشاء رد نداء callback خاص بك، ولكن يمكنك إرسال أي عدد من الوسائط عند بث الإشارة، لذا يجب عليك بث القيم الصحيحة. لبث القيم باستخدام الإشارة أضف بعض الوسائط الإضافية إلى الدالة emit() بلغة GDScript: func take_damage(amount): var old_health = health health -= amount health_changed.emit(old_health, health) بلغة C#: public void TakeDamage(int amount) { int oldHealth = _health; _health -= amount; EmitSignal(SignalName.HealthChanged, oldHealth, _health); } الخلاصة تبث أي عقدة في جودو إشارة عندما يحصل فيها شيء معين مثل الضغط على زر، ويمكن للعقد الأخرى أن ترتبط مع إشارات وتتفاعل مع أحداث مختارة. هناك العديد من الاستخدامات للإشارات حيث يمكنك التفاعل مع العقد الداخلة إلى أو الخارجة من عالم اللعبة أو مع الاصطدامات أو دخول أو خروج شخص من منطقة أو مع تغير حجم عنصر من الواجهة والعديد من ذلك على سبيل المثال إذا كان هناك عنصر في اللعبة (مثل قطعة نقدية) يمكن للاعب التقاطها أو جمعها ممثلة بعقدة Area2D فإنها ستصدر أو تَبُثّ إشارة body_entered عندما يدخل جسم اللاعب شكل الاصطدام الخاص بها مما يسمح لك بمعرفة إذا ما تم التقطها من قبل اللاعب أم لا. سنتعلم في القسم التالي كيفية إنشاء لعبة ثنائية الأبعاد كاملة في محرك ألعاب جودو، ونطبق كل ما تعلمناه من مفاهيم على أرض الواقع. ترجمة - وبتصرف - لقسم Using signals من توثيق جودو الرسمي. اقرأ أيضًا المقال السابق: الاستماع لمدخلات اللاعب في جودو Godot ألعاب الفيديو: تطورها وأهميتها وخطوات برمجتها لغات البرمجة المتاحة في جودو Godot تعرف على أشهر محركات الألعاب Game Engines
-
نشرح في مقال اليوم كيفية إضافة ميزة مهمة لأي لعبة بناء على المقال السابق الذي أنشأنا فيه سكربت للعبة بسيطة لتحريك أيقونة جودو Godot في مسارات دائرية، ألا وهي إعطاء التحكم بالحركة للاعب، ولذلك نحتاج لتعديل الكود البرمجي الذي كتبناة في ملف sprite_2d.gd. لدينا أداتان رئيسيتان لمعالجة مدخلات اللاعب في جودو هما: دوال رد نداء الدخل المضمّنة في جودو built-in input callbacks، وبالأخص الدالة _unhandled_input()_ التي تستدعى لمعالجة الأحداث التي لم يتم التعامل معها بواسطة العقد الأخرى والدالة _process() وهي دالة افتراضية مضمنّة تستدعيها جودو كل مرة يضغط فيها اللاعب على مفتاح وتُستخدم للتفاعل مع الأحداث التي لا تحصل في كل إطار بشكل مستمر مثل الضغط على Space للقفز، راجع استخدام InputEvent للمزيد عن استرجاع استدعاءات المدخلات الكائن المتفرّد Input، إن الكائنات المتفردة Singelton هي عبارة عن كائنات يمكن الوصول إليها بشكل عام، وتقدم جودو الوصول للعديد منها في السكربتات، إنها الأداة الأفضل لتفقد المدخلات في كل إطار. سنستخدم الكائنInput لأننا نريد أن نعرف إذا ما كان يريد اللاعب التحرك أو الدوران في كل إطار. يجب استخدام المتغير الجديد direction من أجل الدوران، استبدل السطر rotation += angular_speed * delta في دالة _process() بالشيفرة التالية: بلغة GDScript: var direction = 0 if Input.is_action_pressed("ui_left"): direction = -1 if Input.is_action_pressed("ui_right"): direction = 1 rotation += angular_speed * direction * delta بلغة C#: var direction = 0; if (Input.IsActionPressed("ui_left")) { direction = -1; } if (Input.IsActionPressed("ui_right")) { direction = 1; } Rotation += _angularSpeed * direction * (float)delta; إن المتغير المحلي direction هو مُضَاعف multiplier يمثل الاتجاه الذي يريد اللاعب الدوران نحوه وزيادته تضخّم مقدار الدوران، وتمثل القيمة 0 أن اللاعب لم يضغط على مفتاح السهم اليميني أو اليساري، والقيمة 1 تعني أن اللاعب يريد الدوران نحو اليمين، و-1 تعني أنه يريد الدوران نحو اليسار. يجب التصريح عن مجموعة شروط واستخدام Input لإنتاج هذه القيم، نبدأ الشرط بالكلمة المفتاحية if في GDScript وننهيها بنقطتين، ويكون الشرط هو التعبير بين الكلمة المفتاحية ونهاية السطر. نستدعي Input.is_action_pressed() للتحقق فيما إذا كان المفتاح مضغوطًا ضمن هذا الإطار، إذ يأخذ التابع سلسلة نصية تمثل المدخلات ويعيد true إذا كان المفتاح قد ضُغط وإلا يعيد false. إن الفعلين المستخدمين سابقًا "uileft" و "uiright" مُعرفين مسبقًا في جودو، إذ يُفعّلان عندما يضغط اللاعب السهمين اليميني واليساري على لوحة المفاتيح أو الزرين اليمين واليسار على قبضة التحكم. ملاحظة: يمكن مشاهدة وتعديل المدخلات في المشروع الخاص بك بالذهاب إلى "إعدادات المشروع Project settings" والنقر على تبويبة "خريطة الإدخال Input Map". أخيرًا نستخدم direction كمُضاعف عند تحديث زاوية الدوران rotation الخاص بالعقدة: rotation += angular_speed * direction * delta يجب أن تتحرك الأيقونة عند الضغط على Left و Right عند تشغيل المشهد بهذه الشيفرة. التحرك عند الضغط على Up نحتاج لتعديل الشيفرة التي تحسب السرعة velocity من أجل التحرك عند الضغط فقط، بدّل السطر الذي يبدأ بـ var velocity بالشيفرة التالية: بلغة GDScript: var velocity = Vector2.ZERO if Input.is_action_pressed("ui_up"): velocity = Vector2.UP.rotated(rotation) * speed بلغة C#: var velocity = Vector2.Zero; if (Input.IsActionPressed("ui_up")) { velocity = Vector2.Up.Rotated(Rotation) * _speed; } هيئنا متجه السرعة velocity بالقيمة Vector2.ZERO وهو ثابت مضمّن في نوع Vector يمثل متجه ثنائي الأبعاد بطول 0. إذا ضغط اللاعب "ui_up" نحدث قيمة السرعة وتتحرك الشخصية إلى الأمام. البرنامج الكامل التالي هو الملف الكامل sprite_2d.gd كمرجع. بلغة GDScript: extends Sprite2D var speed = 400 var angular_speed = PI func _process(delta): var direction = 0 if Input.is_action_pressed("ui_left"): direction = -1 if Input.is_action_pressed("ui_right"): direction = 1 rotation += angular_speed * direction * delta var velocity = Vector2.ZERO if Input.is_action_pressed("ui_up"): velocity = Vector2.UP.rotated(rotation) * speed position += velocity * delta بلغة C#: using Godot; public partial class MySprite2D : Sprite2D { private float _speed = 400; private float _angularSpeed = Mathf.Pi; public override void _Process(double delta) { var direction = 0; if (Input.IsActionPressed("ui_left")) { direction = -1; } if (Input.IsActionPressed("ui_right")) { direction = 1; } Rotation += _angularSpeed * direction * (float)delta; var velocity = Vector2.Zero; if (Input.IsActionPressed("ui_up")) { velocity = Vector2.Up.Rotated(Rotation) * _speed; } Position += velocity * (float)delta; } } تستطيع الآن الدوران باستخدام الأسهم يمين ويسار والتحرك للأمام عن طريق ضغط Up إذا شغّلت المشهد. الخلاصة يمثل كل برنامج نصي في جودو صنفًا ويتوسع في الأصناف المضمّنة، إن أنواع العقد التي ترث منها الأصناف الخاص بك تعطيك وصولًا إلى خاصيًات مثل rotation و position كما في حالتنا، ويمكن أيضًا وراثة العديد من الدوال التي لم تُذكر في هذا المثال. إن المتغيرات التي تضعها في أول الملف بلغة GDScript هي خاصيات الصنف، التي تدعى متغيرات الأعضاء، ويمكن تعريف الدوال التي ستكون في أغلب الأحوال دوال الأصناف الخاصة بك. تقدم جودو العديد من التوابع الافتراضية التي يمكن تعريفها لتتصل مع الأصناف الخاصة بك مع المحرك، وتضم الدالة_process() لتطبيق التغييرات للعقدة في كل إطار، والدالة unhandled_input()_ لاستقبال المدخلات مثل ضغط المفاتيح أو الأزرار من المستخدم، وهناك المزيد أيضًا. كما يسمح الصنف المتفرد Input بالتفاعل مع إدخالات اللاعب في أي مكان من الشيفرة الخاصة بك، وستستخدمها بالتحديد في حلقة process()_ سنتعلم في الدرس التالي مفهوم الإشارات signals في محرك جودو، وتتمكن من بناء علاقات بين البرامج النصية والعقد عن طريق تشغيل العقد للشيفرات في السكريبتات بشكل يجعل الكود أكثر تنظيمًا وأسهل في الصيانة. ترجمة - وبتصرف - لقسم Listening to player input من توثيق جودو الرسمي. اقرأ أيضًا المقال السابق: كتابة برنامجك الأول باستخدام جودو Godot مدخل إلى محرك الألعاب جودو Godot العقد Nodes والمشاهد Scenes في جودو Godot إعداد محرك الألعاب جودو Godot للعمل مع قاعدة البيانات SQLite
-
في هذا المقال ستبرمج لعبتك الأولى لتحريك أيقونة جودو Godot في مسارات دائرية باستخدام لغة البرمجة GDScript. وسنفترض أنك على معرفة بأساسيات البرمجة، وتعرف ما هي لغات البرمجة المتاحة في محرك الألعاب جودو لتتمكن من تنفيذ برنامجك الأول الذي سيحرك الصورة في مسار دائري كما في الصورة التالية: إعداد المشروع من الأفضل إنشاء مشروع جديد للبدء من الصفر، ويجب أن يحتوي مشروعك على صورة واحدة فقط هي أيقونة محرك الألعاب جودو التي تستخدم غالبًا في مجتمع المطورين لبناء النماذج الأولية. ستحتاج إلى إنشاء عقدة Sprite2D لعرضها في اللعبة، لذا عليك أن تنتقل للتبويب "مشهد Scene"، ثم تنقر فوق الزر"عقدة أخرى Other Node". اكتب Sprite2D في شريط البحث لتصفية النتائج، ثم انقر نقرًا مزدوجًا على Sprite2D لإنشاء العقدة. يُفترض أن تحوي علامة تبويب المشهد الآن على عقدة Sprite2D فقط. تحتاج عقدة Sprite2D إلى خلفية للعرض. وستلاحظ من قائمة "الفاحص" Inspector على اليمين أن خاصية Texture تشير إلى أنها فارغة، لعرض أيقونة جودو، انقر واسحب ملف الأيقونة icon.svg من قائمة نظام الملفات FileSystem إلى الخانة Texture ملاحظة: يمكنك إنشاء عقد Sprite2D تلقائيًا عن طريق سحب وإفلات الصور في نافذة العرض، ثم انقر واسحب الأيقونة في نافذة العرض لتثبيتها في وسط نافذة اللعبة. إنشاء نص برمجي جديد لإنشاء سكربت أو نص برمجي جديد وربطه بالعقدة، انقر بزر الفأرة الأيمن على Sprite2D في قائمة "المشهد Scene" وحدد خيار "إلحاق نص برمجي Attach Script" كما في الصورة التالية: ستظهر نافذة "إلحاق نص برمجي Attach Node Script"، حيث تتيح لك تحديد لغة النص البرمجي ومسار الملف من بين خيارات أخرى. غيّر حقل "القالب Template" من "Node: Default" (أي عقدة افتراضية) إلى "Object: Empty" (أي كائن فارغ) وذلك لبدء العمل بملف فارغ، ثم اترك الخيارات الأخرى على قيمها الافتراضية وانقر فوق الزر "أنشئ Create" لإنشاء النص البرمجي. ملاحظة: يجب أن تتطابق أسماء ملفات سكربت C# مع اسم الصنف الذي تتبع له. في هذه الحالة، يجب تسمية الملف MySprite2D.cs من المفترض أن تظهر مساحة عمل لكتابة الكود البرمجي مع فتح ملف sprite_2d.gd الجديد وسطر الشيفرة البرمجية التالية: بلغة GDScript: extends Sprite2D بلغة C#: using Godot; public partial class MySprite2D : Sprite2D { } كل ملف GDScript هو عبارة عن صنف class بشكل ضمني. وتُحدد الكلمة المفتاحية extends الصنف الذي يرثه أو يوسعه هذا الكود البرمجي. في هذه الحالة الصنف هو Sprite2D، مما يعني أن الكود البرمجي سيصل إلى جميع خصائص ودوال عقدة Sprite2D، بما في ذلك الأصناف التي ترث منها هذه العقدة مثل Node2D و CanvasItem و Node ففي حالتنا هذه يرث الصنف Sprite2D يرث من Node2D، والذي بدوره يرث من CanvasItem. وبالتالي سيصل صنفك الجديد إلى خصائص ودوال كل من Sprite2D و Node2D و CanvasItem. ملاحظة: إذا أغفلت السطر الذي يحتوي على الكلمة المفتاحية extends في لغة GDScript سيمدّد الصنف تلقائيًا إلى عقدة RefCounted، أي سيصبح بشكل افتراضي صنفًا فرعيًا من RefCounted الذي يستخدمه محرك ألعاب جودو لإدارة ذاكرة تطبيقك. يمكنك رؤية كافة الخصائص الموروثة في قائمة "الفاحص Inspector"، مثل خاصية texture الخاصة بالعقدة مما يسمح لك بتحريرها بسهولة داخل المحرر أو برمجيًا داخل النص البرمجي. ملاحظة: تعرض قائمة "الفاحص Inspector" خصائص العقدة في "حالة العنوان Title Case" افتراضيًا، مع كلمات بأحرف كبيرة ومنفصلة باستخدام مسافة. أما في شيفرة GDScript، فتكون هذه الخصائص مكتوبة بحالة الثعبان snake_case (أي بحالة الأحرف الصغيرة مع كلمات مفصولة عن بعضها باستخدام شرطة سفلية_). يمكنك التمرير فوق اسم أي خاصية في قائمة "الفاحص" Inspector لرؤية وصفها ومعرّفها في الشيفرة. تعليمة طباعة Hello World لا ينفذ النص البرمجي حاليًا أي شيء. سنجعله يطبع النص Hello world في الخرج. أضف الشيفرة التالية إلى نصك البرمجي: بلغة GDScript: func _init() : print(“Hello, world!”) بلغة C#: public MySprite2D() { GD.Print("Hello, world!"); } دعونا نشرح ما سبق. تُحدد الكلمة المفتاحية func دالة جديدة تسمى _init وهو اسم خاص لمنشئ أو باني الصنف لدينا. إذا عرّفت هذه الدالة، فسوف يستدعي جودو دالة _init() لكل كائن أو عقدة عند إنشائه في الذاكرة. ملاحظة: لغة البرمجة GDScript هي لغة تعتمد على المسافة البادئة، فالفراغ في بداية السطر الذي يحوي تعليمة الطباعة ()print ضروري لعمل الشيفرة البرمجية، فإذا أغفلتها أو لم تضع مسافة بادئة في بداية السطر بشكل صحيح سينبهك المحرر عليها باللون الأحمر ويعرض رسالة الخطأ التالية: "مسافة بادئة مطلوبة". احفظ المشهد باسم sprite_2d.tscn إذا لم تكن قد فعلت ذلك بالفعل، ثم اضغط على F6 (Cmd + R على macOS) لتشغيله. انظر إلى اللوحة السفلية للخرج لترى النص Hello world احذف الدالة _init()، بحيث يتبقى لديك فقط السطر extends Sprite2D. تدوير العقدة حان الوقت لتحريك العقدة وتدويرها. لفعل بذلك، سنضيف متغيرين جديدين إلى النص البرمجي هما سرعة الحركة المقاسة بوحدة البكسل في الثانية، والسرعة الزاوية المقاسة بالراديان في الثانية. أضف التالي بعد السطر extends Sprite2D. بلغة GDScript: var speed = 400 var angular_speed = PI بلغة C#: private int _speed = 400; private float _angularSpeed = Mathf.Pi; نكتب المتغيرات الجديدة بالقرب من بداية النص البرمجي، بعد الأسطر التي تحوي extends، وقبل الدوال. وستحتوي كل نسخة عقدة مرتبطة بهذا النص البرمجي على نسخة خاصة بها من خصائص speed و angular_speed. ملاحظة: تقاس الزوايا في محرك جودو بالراديان افتراضيًا، ولكن هناك دوال وخصائص مدمجة متاحة إذا كنت تفضل حساب الزوايا بالدرجات بدلاً من ذلك. لتحريك أيقونة جودو، نحتاج إلى تحديث موقعها وتدويرها في كل إطار ضمن حلقة اللعبة. يمكننا استخدام الدالة الافتراضية _process() الخاصة بالصنف Node. فإذا عرفتها في أي صنف يوسع الصنف Node مثل Sprite2D فسوف يستدعي جودو هذه الدالة في كل إطار ويمرر له قيمة باسم دلتا delta تمثل المدة الزمنية التي انقضت منذ الإطار الأخير. ملاحظة: تعمل الألعاب عن طريق عرض العديد من الصور في الثانية يطلق على كل منها إطار، ويتم ذلك بشكل حلقة متكررة. نقيس المعدل الذي تنتج فيه اللعبة الصور بمعدل الإطارات في الثانية (FPS). تهدف معظم الألعاب إلى 60 إطارًا في الثانية، على الرغم من أنك قد تجد أرقامًا مثل 30 إطارًا في الثانية على أجهزة الجوال الأبطأ أو أرقام بين 90 إلى 240 في ألعاب الواقع الافتراضي. يبذل المطورون قصارى جهدهم لتحديث عالم الألعاب وعرض الصور بفواصل زمنية ثابتة، لكن هناك دائمًا اختلافات بسيطة في أوقات عرض الإطارات. لهذا السبب يعطي جودو قيمة زمن دلتا delta، كي يجعل الحركة مستقلة عن معدل الإطارات. في نهاية الكود البرمجي، عرّف الدالة _process() كما يلي: بلغة GDScript: func _process(delta) : rotation += angular_speed * delta تحدد الكلمة المفتاحية func في الكود السابق دالة جديدة. بعد ذلك علينا كتابة اسم الدالة والقيم التي تأخذها بين قوسين. وتنهي النقطتان : التعريف وتمثل الكتل التي تتبعها محتوى الدالة أو تعليماتها. بلغة C#: public override void _Process(double delta) { Rotation += _angularSpeed * (float)delta; } ملاحظة: لاحظ كيف أن الدالة _process() مثل الدالة _init() تبدأ بشرطة سفلية. ووفق العُرف المتبع، تبدأ جميع دوال جودو الافتراضية بشرطة سفلية وهي ذاتها الدوال المضمنة التي يمكنك تعريفها للتواصل مع جودو. يستخدم السطر rotation += angular_speed * delta داخل الدالة لتحديث دوران العقدة أو الصورة في كل إطار استنادًا إلى سرعة الدوران والمدة الزمنية المنقضية، حيث أن الخاصية rotation هي خاصية موروثة عن الصنف Node2D التي تمتد منها العقدة Sprite2D وهي تتحكم في دوران العقدة. شغّل المشهد لمشاهدة أيقونة جودو تدور في مكانها. متابعة العمل على تحريك العقدة دعنا الآن نجعل العقدة تتحرك في مسار دائري. أضف السطرين التاليين داخل دالة _process() مع التأكد من إضافة مسافة بادئة للسطرين الجديدين بنفس طريقة المسافة البادئة للسطر rotation += angular_speed * delta قبلهما. بلغة GDScript: var velocity = Vector2.UP.rotated(rotation) * speed position += velocity * delta بلغة C#: var velocity = Vector2.Up.Rotated(Rotation) * _speed; Position += velocity * (float)delta; كما رأينا سابقًا، تحدد الكلمة المفتاحية var متغيرًا جديدًا. فإذا وضعته في بداية النص البرمجي، فإنه يُحدد خاصية الصنف. بينما إذا وضعته داخل الدالة، فإنه يُحدد متغيرًا محليًا يوجد ضمن نطاق الدالة نفسها فقط. نحدد متغيرًا محليًا يسمى velocity وهو متجه ثنائي الأبعاد يمثل الاتجاه والسرعة معًا. لتحريك العقدة للأمام، نبدأ من ثابت الصنف Vector2 وهو Vector2.UP يمثل متجه يشير لأعلى، ونديره عن طريق استدعاء طريقة Vector2 وهي rotated(). التعبير التالي Vector2.UP.rotated(rotation) هو شعاع يشير إلى الأمام بالنسبة إلى صورة الأيقونة مضروبًا بخاصية speed، حيث يعطينا سرعة يمكننا استخدامها لتحريك العقدة للأمام. نضيف velocity * delta إلى psotion العقدة لتحريكها. والموقع نفسه من نوع Vector2 وهو نوع مدمج في جودو يمثل متجه ثنائي الأبعاد. شغّل المشهد الآن لمشاهدة أيقونة جودو تتحرك في مسار دائري. بهذا نكون انتهينا من كتابة كود برمجي لتحريك العقدة بمفردها في محرك ألعاب جودو. وستتعلّم في الدرس التالي كيفية الحصول على مدخلات من اللاعب واستخدامها للتحكم بالعقدة. النص البرمجي الكامل لتحريك العقدة فيما يلي ملف sprite_2d.gd الكامل لاستخدامه كمرجع. بلغة GDScript: extends Sprite2D var speed = 400 var angular_speed = PI func _process(delta) : rotation += angular_speed * delta var velocity = Vector2.UP.rotated(rotation) * speed position += velocity * delta بلغة C#: using Godot; public partial class MySprite2D : Sprite2D { private int _speed = 400; private float _angularSpeed = Mathf.Pi; public override void _Process(double delta) { Rotation += _angularSpeed * (float)delta; var velocity = Vector2.Up.Rotated(Rotation) * _speed; Position += velocity * (float)delta; } } الخلاصة تعلمنا في مقال اليوم طريقة كتابة برنامج بسيط لتحريك العقد في جودو وضبط سرعتها واتجاهها، والجدير بالذكر أنّ تحريك العقدة الذي تعلمناه في هذا المقال لا يأخذ بعين الاعتبار الاصطدام بالجدران أو الأرض. وستتعلّم المزيد عند إنشاء أول لعبة ثنائية الأبعاد في جودو ونستخدم نهجًا آخر لتحريك الكائنات مع اكتشاف التصادمات. ترجمة بتصرف لقسم Creating your first script من توثيق جودو الرسمي. اقرأ أيضًا المقال السابق: تعرف على لغات البرمجة المتاحة في محرك جودو دليلك الشامل إلى برمجة الألعاب كيف تصبح مبرمج ألعاب فيديو ناجح العقد Nodes والمشاهد Scenes في جودو Godot
-
سنلقي الضوء في مقال اليوم على لغات البرمجة المتوفرة في محرك الألعاب جودو، ومتى نختار كل لغة منها ونتعلم محاسن ومساوئ كل خيار، وسنكتب في الجزء التالي أول سكريبت برمجي ضمن محرك جودو باستخدام لغة البرمجة الافتراضية GDScript. ترتبط الأكواد البرمجية بالعقد، فعندما تربط سكريبت برمجي بعقدة معينة، فإنك توسع سلوك تلك العقدة بإضافة وظائف جديدة لها، هذا يعني أن السكريبتات ترث كل وظائف وخصائص العقد المرتبطة بها. على سبيل المثال لنفترض أن تعمل على برمجة لعبة فضائية تتحرك فيها سفينة فضائية كما تبين الصورة التالية، وتريد أن تهتز الكاميرا عندما تتضرر السفينة.عليك أن تضيف عقدة كاميرا Camera2D تابعة لعقدة السفينة في التسلسل الهرمي.كي تتبع الكاميرا حركة السفينة لأن العقدة تتبع العقدة الأب لها في التسلسل الهرمي للعقد بشكل افتراضي، ولجعل الكاميرا تهتز عندما تتضرر السفينة، يجب عليك في هذه الحالة إنشاء سكريبت برمجي وربطه بعقدة Camera2D وتكتب فيه شيفرة الاهتزاز كون هذه الميزة غير متوافرة بشكل ضمني في جودو. لغات البرامج النصية المتوافرة في جودو يوفر محرك الألعاب جودو أربع لغات برمجية هي: GDScript وC# بالإضافة إلى كل من C وC++ من خلال تقنية GDExtention، وهناك لغات أخرى مدعومة من مجتمع جودو ولكن اللغات المذكورة هنا هي الرسمية. يمكنك استخدام عدة لغات برمجة في مشروع واحد، مثلًا يمكنك كتابة شيفرة منطق اللعب باستخدام GDScript كونها سريعة في الكتابة، وتستخدم C# أو C++ لتطبيق خوارزميات معقدة لتحسين أداء الشيفرة، أو يمكنك كتابة كل شيء باستخدام GDScript أو C#، الأمر عائد لك. الهدف من هذه المرونة هو تلبية احتياجات المطورين ومشاريع الألعاب المختلفة. أي لغة برمجة يجب أن أستخدم مع جودو؟ يُنصح باستخدام لغة GDScipt إذا كنت مبتدئًا، إذ أنشئت هذه اللغة لجودو بشكل خاص ولحاجات مطوري الألعاب، وهي تتميز بصياغتها السهلة وقلة حجمها، كما أنها تقدم أفضل تكامل مع محرك الألعاب جودو. ستحتاج بالنسبة للغة C# إلى محرر أكواد خارجي مثل VSCode أو Visual Studio، وعلى الرغم من كون دعم لغة C# أفضل، ولكنك لن تجد العديد من الموارد مقارنة للغة GDScript، لذا يُنصح باستخدام لغة C# لمستخدمي اللغة الخبراء. لنتحدث عن مميزات كل لغة، و محاسنها ومساوئها. لغة GDScript إن لغة GDScript هي لغة أمرية Imperative وكائنية التوجه Object-oriented مبنية خصوصًا لمحرك الألعاب جودو ومصممة لمطوري الألعاب لتوفير الوقت في كتابة شيفرة للألعاب، وتضم المميزات التالية: صياغة بسيطة تعطي ملفات صغيرة الحجم. وقت تحميل قليل وتصيير rendering سريع جدًا. تكامل قوي مع محرر جودو، مع إكمال للشيفرة للعقد والإشارات ومعلومات أكثر مع المشهد المرتبط به. وجود أنواع معطيات لتمثيل المتجهات vectors والتحويلات الهندسية transforms مما يسهل استخدام الجبر الخطي الضروري للألعاب. تدعم الخيوط المتعددة multiple threads وتملك نفس كفاءة اللغات المكتوبة بشكل ساكن (وهي اللغات التي يحدد فيها نوع البيانات بشكل ثابت وقت الترجمة). لا يوجد فيها كنس للمهملات garbage collection، لأن هذه الميزة تعيق إنشاء الألعاب، إذ يحسب المحرك المراجع ويدير لك الذاكرة في معظم الحالات بشكل افتراضي، ولكن يمكنك التحكم بالذاكرة إن احتجت لذلك. توفر ميزة تحديد النوع التدريجي Gradual Typing، فالمتغيرات لها أنواع ديناميكية افتراضيًا، ولكن يمكنك استخدام تلميحات النوع type hinting للتحقق من الأنواع. تشبه لغة GDScipt لغة بايثون Python في الهيكلية عن طريق استخدام كتل الشيفرة والمسافات البادئة، ولكنها لا تعمل بنفس المضمون، فقد طورت بالاعتماد على لغات برمجية متعددة مثل Squirrel وLua وبايثون. ملاحظة: قد تتساءل لماذا لا نستخدم لغة بايثون أو لغة Lua مباشرة في محرك جودو؟ في الواقع كانت لغة جودو تستخدم بايثون و Lua وقد تطلب تكامل اللغتين الكثير من العمل وكان له العديد من المحدوديات، مثلًا كان دعم الخيوط threads تحديًا كبيرًا في بايثون. كما أن تطوير لغة جديدة لا يتطلب جهدًا أكبر ويمكننا تصميمه ليلبي احتياجات مطوري الألعاب، ويتم العمل الآن على تحسينات وميزات كبيرة على اللغة ستكون صعبة التنفيذ مع لغات برمجية من طرف ثالث. لغة .NET/C# دُعمَت لغة C# الخاصة بمايكروسوفت رسميًا في محرك الألعاب جودو لأنها المفضلة لدى مطوري الألعاب، كما أن لغة C# متطورة ومرنة ولديها الكثير من المكتبات المكتوبة لأجلها، وكان إضافة الدعم لها ممكنًا بفضل تبرع سخي من مايكروسوفت لمطوّري جودو. تقدم لغة C# حلًا وسطيًّا بين الأداء وسهولة الاستخدام، ولكن يجب الانتباه من استخدامها لجامع المهملات garbage collector ملاحظة1: يجب استخدام نسخة .NET من محرر جودو لكتابة البرامج النصية باستخدام C#، ويمكن تنزيله من موقع جودو الرسمي. بما أن جودو تستخدم .NET6 فيمكنك نظريًا استخدام أي مكتبة .NET من طرف ثالث أو أي هيكلية في جودو، بالإضافة لأي لغة برمجة متوافقة مع البنية التحتية مثل F# وBoo وClojureCLR، ولكن لغة C# هي خيار .NET الوحيد المدعوم. ملاحظة2: لا تُنفذ شيفرة GDScript بسرعة لغات C# أو C++ المصرّفة، ولكن محرك جودو نفسه يحتوي على الكثير من الخوارزميات الأساسية السريعة التي تستدعي الدوال والوظائف الأساسية المكتوبة بلغة C++ داخل بنية المحرك، ففي معظم الأحوال لن تؤثر كتابة منطق اللعب على الأداء بشكل ملحوظ سواء استخدمت لغة GDScript أو C# أو C++ . استخدام لغة C++ عبر GDExtention تسمح لك GDExtention بكتابة شيفرة اللعبة باستخدام لغة C++ دون الحاجة لإعادة تصريف جودو. يمكنك استخدام أي إصدار من اللغة أو تنويع المصرف والإصدارات للمكتبات المشتركة بفضل استخدام واجهة برمجة التطبيقات المعروفة باسم C API Bridge وهي واجهة برمجية تسمح باستخدام أكواد مكتوبة بلغة C مع لغات برمجة أخرى وتمكن التطبيقات المكتوبة بلغات مختلفة من التفاعل مع بعضها البعض بسهولة. إن GDExtention هي أفضل خيار لتعزيز أداء الألعاب التي تطورها باستخدام جودو، إذ لست بحاجة لاستخدامها في كل اللعبة ويمكنك استخدامها فقط في الأجزاء التي تحتاج إلى تحسين الأداء بشكل كبير وكتابة أكواد بلغات مختلفة باستخدام GDScript أو C#، وعند العمل باستخدام GDExtention ستتشابه الأنواع والدوال والخاصيات لواجهة برمجة تطبيقات C++ مع تلك التي تستخدمها عند البرمجة داخل محرك Godot نفسه. الخلاصة تعرفنا في مقال اليوم على أهمية كتابة الأكواد البرمجية (السكريبتات) في جودو فهي ملفات تحتوي شيفرة برمجية يمكن ربطها مع العقد لتوسيع وظائفها، ورأينا أن محرك ألعاب جودو يدعم أربع لغات برمجة نصية رسمية ليمنح لمبرمجي الألعاب مرونة في اختيار اللغة التي يفضلونها، كما يمكن مزج اللغات لتطبيق خوارزميات ثقيلة باستخدام لغة C أو C++ وكتابة معظم منطق اللعبة باستخدام GDScript أو C#. ترجمة - وبتصرف - لقسم Scripting Languages من توثيق جودو الرسمي. اقرأ أيضًا المقال السابق: إنشاء نسخ من المَشاهِد والعقد في جودو Godot تعرف على أشهر لغات برمجة الألعاب تعرف على أشهر محركات الألعاب Game Engines أهمية صناعة الألعاب الإلكترونية دليلك الشامل إلى برمجة الألعاب
-
رأينا في الدرس السابق أنّ المشهد عبارة عن مجموعة من العقد المنظمة على شكل شجرة مع عقدة جذر واحدة، كما تعرفنا على إمكانية تقسيم مشروعك إلى أي عدد تريده من المَشاهد، حيث تساعدك هذه الميزة في تقسيم وتنظيم مكونات لعبتك المختلفة. يمكنك في محرك جودو إنشاء العديد من المَشاهد بالطريقة التي ترغب بها وحفظها كملفات بامتداد .tscn والذي تعني مشهدًا نصيًا وكان ملف label.tscn من الدرس السابق مثالًا على ذلك. ونسمّي تلك الملفات مَشاهد مُخزنّة لأنها تخزن معلومات حول محتوى المشهد. إليك مثالًا على مشهد كرة مكوّن من عقدة جذر RigidBody2D باسم كرة Ball، والتي تسمح للكرة بالسقوط والارتداد على الجدران، وعقدة Sprite2D، وعقدة CollisionShape2D. بمجرد حفظ أي مشهد سيعمل كنموذج أو قالب Template، أي يمكنك استنساخه في مَشاهد أخرى بقدر ما ترغب ونسمي عملية تكرار كائن من قالب كهذا باسم ** استنساخ Instancing ** وكما ذكرنا فيالدرس السابق، تتصرف المشاهد المُستنسخة كعقدة يخفي المحرر محتواها افتراضيًا. فسترى عند استنساخ الكرة عقدتها فقط، لاحظ أيضًا كيف أن لكل نسخة اسمًا فريدًا خاصًّا بها. تبدأ كل نسخة من مشهد الكرة بنفس الهيكل والخصائص كملف ball.tscn ومع ذلك، يمكنك تعديل كل منها بشكل مستقل، مثل تغيير كيفية ارتدادها، وزنها، أو أي خاصية يعرضها المشهد الأصلي. التطبيق العملي سنستخدم استنساخ المشاهد في تطبيق عملي لنرى كيف يعمل في جودو Godot. يمكنك تحميل مشروع الكرة الذي أعددناه لك من هنا instancing_starter.zip استخرج الملف على حاسوبك. ولاستيراده، تحتاج إلى الوصول إلى مدير المشروع Project Manager. يمكن الوصول إليه عن طريق فتح جودو، أو إذا كان مفتوحًا بالفعل، انقر على "المشروع Project" ثم " العودة لقائمة المشاريع Quit to Project List" (Ctrl + Shift + Q, Ctrl + Option + Cmd + Q على macOS) في مدير المشروع، انقر فوق زر "استيراد" Import لاستيراد المشروع في النافذة المنبثقة التي تظهر، انقر فوق زر "تصفح" browse وانتقل إلى المجلد الذي استخرجته. انقر نقرًا مزدوجًا على ملف project.godot لفتحه. وأخيرًا، انقر فوق زر "استيراد وتعديل" Import & Edit. يحتوي المشروع على مشهدين محفوظين هما المشهد الرئيسي main.tscn الذي يحتوي على الجدران التي تصطدم بها الكرة، ومشهد الكرة ball.tscn. يجب أن يفتح المشهد الرئيسي تلقائيًا. وفي حال رأيت مشهدًا فارغًا ثلاثي الأبعاد بدلاً من المشهد الرئيسي فانقر على زر 2D في أعلى الشاشة. لنضف كرة بصفة ابن للعقدة الرئيسية Main. من قائمة "المشهد" Scene، اختر العقدة Main، ثم انقر على أيقونة الرابط في أعلى القائمة. يسمح هذا الزر بإضافة نسخة من مشهد كابن للعقدة المختارة. انقر نقرًا مزدوجًا على مشهد الكرة لإنشاء نسخة منه. تظهر الكرة في الزاوية اليسرى أعلى نافذة العرض كما يلي: انقر عليها واسحبها نحو المنتصف. شغِّل اللعبة بالضغط على F5 (Cmd + B على macOS) ويجب أن ترى الكرة وهي تسقط للأسفل. نريد الآن إنشاء مزيد من النُسخ من عقدة الكرة. بالمحافظة على الكرة محددة، اضغط على Ctrl + D (Cmd + D على macOS) لاستدعاء أمر النسخ. ثم انقر واسحب لنقل الكرة الجديدة إلى موقع مختلف. يمكنك تكرار هذه العملية حتى تحصل على عدة كرات في المشهد. شغِّل اللعبة مرة أخرى. يجب أن ترى الآن كل كرة تسقط بشكل مستقل الأخرى. هذا ما تفعله النُسخ، حيث أن كل نسخة هي إعادة تشكيل مستقل لمشهد حسب قالب معين. تحرير المَشاهد والنُسخ هناك المزيد من المميزات حول النُسخ. إذ يمكنك أيضًا: تغيير خصائص إحدى الكرات دون التأثير على الأخرى باستخدام أمر "الفاحص" Inspector تغيير الخصائص الافتراضية لكل كرة بفتح مشهد ball.tscn وإجراء تغيير على عقدة الكرة هناك وعند الحفظ، ستُحدّث قيم جميع نُسخ الكرة في المشروع. لنجرب التالي: افتح ball.tscn وحدد عقدة الكرة. ومن قائمة "الفاحص" Inspector على اليمين، انقر على خاصية PhysicsMaterial لاستعراضها. اضبط خاصية Bounce إلى 0.5 بالنقر على مجال الأرقام أو كتابة 0.5 والضغط على Enter شغِّل اللعبة بالضغط على F5 (Cmd + B على macOS) ولاحظ كيف تترتّب الكرات الآن بشكل أكبر. حيث أن مشهد الكرة هو قالب لجميع النُسخ، ويُسبب إجراء التعديل والحفظ تحديث جميع النسخ وفقًا لذلك. الآن، دعنا نعدِّل نسخة فردية من هذه النسخ. انتقل مرة أخرى إلى المشهد الرئيسي بالنقر على التبويب المقابل فوق نافذة العرض. حدد إحدى عقد الكرة المُستنسخة واضبط قيمة مقياس الجاذبية إلى 10 من قائمة "الفاحص" Inspector. ستظهر أيقونة رمادية "revert" بجوار الخاصية المعدَّلة. تُشير هذه الأيقونة إلى أنك تجاوزت قيمة من مشهد المصدر المحزَّم، وسيتم الاحتفاظ بالتجاوز في النسخة حتى إذا عدّلت الخاصية في المشهد الأصلي. ستُستعاد الخاصية إلى قيمتها في المشهد المحفوظ، بالنقر على أيقونة revert. أعِد تشغيل اللعبة ولاحظ كيف تسقط هذه الكرة الآن بسرعة أكبر من الكرات الأخرى. ملاحظة: قد تلاحظ أنه لا يمكنك تغيير قيم PhysicsMaterial للكرة. هذا يعود إلى أن PhysicsMaterial هو مورد resource، والموارد هي مكونات أساسية أخرى في ألعاب محرك جودو، سنتحدث عنها في درس لاحق. ويجب عليك جعله فريدًا قبل أن تتمكن من تحريره في مشهد يرتبط بالمشهد الأصلي. ولجعل مورد ما فريدًا لحالة واحدة، انقر بالزر الأيمن عليه من قائمة "الفاحص" Inspector وانقر على "Make Unique" في القائمة التي تظهر لك. حالات المشهد كلغة تصميم تقدم نُسخ ومشاهد جودو لغة تصميم ممتازة، تجعله يبرز عن غيره، فمحرك جودو صّمّم منذ البداية ليناسب هذه الفكرة. لست ملزمًا باتّباع أنماط الشيفرة المعمارية عند صنع ألعاب باستخدام جودو، مثل نموذج-عرض-تحكم (MVC) أو مخططات كيان-علاقة (Entity-Relationship) ويمكنك بدلاً من ذلك، البدء بتخيّل العناصر التي سيراها اللاعبون في لعبتك وبناء شيفرتك منها. على سبيل المثال، يمكنك تقسيم لعبة إطلاق نار على النحو التالي: يمكنك إنشاء مخطط مثل هذا لأي نوع من الألعاب تقريبًا حيث يُمثِّل كل مستطيل في الصورة السابقة كيانًا يظهر في اللعبة من وجهة نظر اللاعب، وتُخبرك الأسهم أي مشهد يمتلك مشهدًا آخر. بمجرد أن تضع مخطط لعبتك فيمكنك حينها إنشاء مشهد لكل عنصر مُدرج في هذا المخطط لتطوير لعبتك. ويمكنك الاستفادة من ميزة الاستنساخ لبناء شجرة مَشاهد، سواء بكتابة كود برمجي لإتشاء المشهد أو يمكنك إنشاؤه مباشرة في المحرر الرسومي لجودو. غالبًا ما يقضي المبرمجون وقتًا طويلًا في تصميم هياكل معمارية مجردة ومحاولة تنسيق المكونات ضمنها، إلا أنّ التصميم بناءً على مشاهد اللعبة يجعل التطويرَ أسرع وأسهل، مما يسمح لك بالتركيز على منطق اللعبة نفسه. ونظرًا لأن معظم مكونات اللعبة تتطابق مباشرة مع مشهد ما، فإن استخدام التصميم المعتمد على المشهد يعني أن بإمكانك العمل على كل مشهد بشكل مستقل والتركيز على العلاقات بين المشاهد ودمج المشاهد الفرعية في المشاهد الرئيسية، وهذا يقلل من حجم الشيفرة البرمجية. إليك مثالًا على مخطط المشهد للعبة عالم مفتوح بأصول كثيرة وعناصر متداخلة: تخيل أننا بدأنا بإنشاء الغرفة، يُمكننا إنشاء عدة مَشاهد غرف مختلفة بترتيبات فريدة من الأثاث في كل منها، ويُمكننا في وقت لاحق إنشاء مشهد منزل يستخدم عدة نسخ غرف داخلية. وسنُنشئ قلعة من العديد من المنازل المُثبتة وأرضية كبيرة نضع عليها القلعة. وكل هذه ستكون حالات استنساخ لمشهد واحد أو أكثر. وفي وقت لاحق، يُمكن إنشاء مَشاهد جديدة تُمثِّل حُراسًا وإضافتهم إلى القلعة. ستتم إضافتهم بشكل غير مباشر إلى عالم اللعب كاملًا، وبفضل هذه الميزة في محرك ألعاب جودو، سيكون من السهل عليك تحسين وتطوير لعبتك بطريقة سهلة ومنظمة إذ أن كل ما عليك فعله هو إنشاء واستنساخ المزيد من المَشاهد. لقد صُمّم المحرر في جودو ليكون سهل الوصول من قبل المبرمجين والمصممين والفنانين على حد سواء، ويُمكن أن يشارك في عملية تطوير اللعبة فريق مؤلف من مصممي نمذجة ثنائية أو ثلاثية الأبعاد، ومصممي مستويات، ومصممي ألعاب، ومصممي رسوم متحركة، ويمكنهم أن يتعاونوا بكل مرونة ويعملوا جميعًا ضمن محرر جودو بطريقة سلسلة ومنظمة. الخلاصة تعرفنا في مقال اليوم على ميزة الاستنساخ التي يقصد بها عملية إنتاج نسخ من كائن ما في محرك ألعاب جودو ورأينا أن لهذه الميزة استخدامات عديدة. كما تعرفنا أكثر على المَشاهد التي تمنحك القدرة على تقسيم لعبتك إلى مكوِّنات قابلة لإعادة الاستخدام وتوفر لك أداة لبناء الأنظمة المعقدة كما توفر لك لغة رسومية تسهل عليك التفكير في هيكل مشروع اللعبة وتحسينها وتطويرها. ترجمة - وبتصرف - لقسم Creating instances من توثيق جودو الرسمي. اقرأ أيضًا المقال السابق: العقد Nodes والمشاهد Scenes في جودو Godot الرؤية التصميمية لمحرك اﻷلعاب جودو Godot مدخل إلى محرك الألعاب جودو Godot تعرف على أهمية صناعة الألعاب الإلكترونية
-
تعرفنا في مقال سابق على المفاهيم الرئيسية لمحرك الألعاب جودو Godot، ورأينا أنّ اللعبة عبارة عن مجموعة من المَشاهد على شكل شجرة وأنّ كل مشهد هو شجرة من العقد بحد ذاته. في هذا الدرس، سنشرح المزيد حول العقد والمشاهد ونعلمك كيف يمكن أن تنُشئ أول مشهد في جودو بنفسك. العقد العقد هي الوحدات الأساسية لبناء لعبتك، فهي مثل المكونات في نموذج، وهناك عشرات الأنواع من العقد والتي يمكن أن تعرض صورة أو صوتًا أو كاميرا وغيرها الكثير من الأنواع. تمتلك جميع العقد في جودو الخصائص التالية: اسم. خصائص قابلة للتحرير. تتلقى استدعاءات لتحديثها في كل إطار. يمكنك تطويرها بإضافة خصائص ودوال جديدة. يمكنك إضافتها إلى عقدة أخرى بصفة ابن. الخاصية الأخيرة مهمة حيث تشكّل العقد مع بعضها شجرة وهي ميزة قوية لتنظيم المشاريع. ونظرًا لأن العقد المختلفة لها دوال مختلفة، فإن دمجها معًا ينتج عنه سلوك أكثر تعقيدًا حيث يمكنك بناء شخصية لعبة ثنائية الأبعاد تتبعها الكاميرا باستخدام عقدة CharacterBody2D، وعقدة Sprite2D، وعقدة Camera2D، وعقدة CollisionShape2D. المَشاهد Scenes عندما ترتّب العقد ضمن شجرة، نسمي هذا التركيب مشهدًا تمامًا مثل الشخصية الموضحة في الصورة التالية، وبمجرد حفظها، ستصبح المَشاهد كنوع جديد من العقد في المحرر، حيث يمكنك إضافتها بصفة ابن لعقد موجودة مسبقًا. وفي هذه الحالة، تظهر نسخة من المشهد كعقدة واحدة مع تفاصيلها الداخلية المخفية. تسمح لك المَشاهد ببناء الشيفرة البرمجية للعبتك بالطريقة التي تريدها، كما يمكنك تكوين عقد لإنشاء أنواع مخصصة ومعقدة منها، مثل شخصية لعبة تركض وتقفز، مع شريط حياة Health bar، ومجموعة من الإعدادات التي تمكنك من التفاعل معها، وغيره الكثير. يعدّ محرر الألعاب جودو في الأساس محرر مشاهد فهو يحتوي على العديد من الأدوات لتحرير المَشاهد ثنائية وثلاثية الأبعاد، بالإضافة إلى واجهات المستخدم. كما أن المشروع الذي يُنفذ باستخدام محرك جودو يمكن أن يحتوي على العديد من هذه المَشاهد حسب الحاجة. يتطلّب المحرك وجود مشهد واحد فقط كمشهد رئيسي لتطبيقك، وهو ذاته المشهد الذي يعرضه المحرك في البداية عند تشغيل اللعبة. تتمتع المَشاهد - بالإضافة إلى عملها كعقد - بالخصائص التالية: لديها دائمًا عقدة جذر واحدة، مثل الشخصية في مثالنا. يمكنك حفظها على وسيط التخزين الخاص بك وتحميلها لاحقًا. يمكنك إنشاء عدد لا منتهي من النسخ للمشهد، ويمكنك أن تملك خمس أو عشر شخصيات في لعبتك، تم إنشاؤها من مشهد الشخصية الأساسي الخاص بك. إنشاء أول مشهد لك سنُنشئ أول مشهد بعقدة واحدة، ولتنفيذ ذلك، ستحتاج أولاً إلى إنشاء مشروع جديد. بعد فتح نافذة المشروع، سيظهر لك محرر فارغ. تظهر عدة خيارات في مشهد فارغ، ومن قائمة "مشهد Scene" على اليسار لإضافة عقد جذر بسرعة. تضيف 2D Scene عقد Node2D وتضيف 3D Scene عقد Node3D، وتضيف User Interface عقد Control. هذه التعيينات اختيارية حسب الحاجة وليس بالضرورة اختيارها جميعها. يتيح لك الخيار Other Node تحديد أي عقدة لتكون هي العقدة الجذر. ويعادل خيار Other Node في مشهد فارغ خيار Add Child Node في أعلى الجزء الأيسر من قائمة "مشهد Scene"، والذي عادة ما يضيف عقدًا جديدة كأبناء للعقد التي تم اختيارها مسبقًا. الآن سنضيف عقدة Label واحدة إلى المشهد. تتمثل وظيفتها برسم نص على الشاشة. انقر فوق زر Add Child Node أو Other Node لإنشاء عقدة جذر. ستفتح نافذة إنشاء العقد وتظهر قائمة طويلة من العقد المتوفرة. حدد عقدة Label. يمكنك كتابة اسمها لتصفية القائمة. انقر فوق عقدة Label لتحديدها ثم اضغط على زر "أنشئ Create" في أسفل النافذة. يحدث الكثير في محرك الألعاب عند إضافة أول عقدة إلى المشهد، حيث يتغير المشهد إلى مساحة عمل ثنائية الأبعاد لأن Label هو عقدة من نوع 2D. يظهر Label مُحدَّدًا في الزاوية اليسرى العلوية من نافذة العرض، وتظهر العقدة في القائمة "مشهد Scene" على اليسار، كما تظهر عناصر العقد في قائمة "الفاحص Inspector" على اليمين. تغيير عناصر العقد الخطوة التالية هي تغيير عنصر "Text" في Label. دعونا نغيره إلى Hello World انتقل إلى قائمة الفاحص Inspector على يمين نافذة العرض، ثم انقر داخل الحقل أسفل عنصر Text واكتب Hello World سترى أن النص يرسم ضمن نافذة العرض أثناء كتابتك. يمكنك نقل عقدة Label في نافذة العرض عن طريق تحديد أداة النقل في شريط الأدوات. عند تحديد عقدة Label، انقر واسحب في أي مكان ضمن نافذة العرض لنقلها إلى وسط العرض المحدد بالمستطيل. تشغيل المشهد أصبح كل شيء جاهزًا لتشغيل المشهد. اضغط على زر تشغيل المشهد في أعلى اليمين من الشاشة أو اضغط على F6) Cmd+R على نظام macOS). ستظهر نافذة منبثقة تطلب منك حفظ المشهد، وهذا خيار ضروري لتشغيل المشهد. انقر على زر الحفظ في مستعرض الملفات لحفظه باسم label.tscn يجب أن يفتح التطبيق في نافذة جديدة ويعرض فيها النص Hello World على النحو التالي: أغلق النافذة أو اضغط على F8 (cmd + . على نظام macOS) لإيقاف تشغيل المشهد. تعيين المشهد الرئيسي استخدمنا زر التشغيل لتشغيل المشهد، ويوجد زر آخر بجانبه يسمح لك بتحديد وتشغيل المشهد الرئيسي للمشروع. يمكنك الضغط على F5 (Cmd + B على نظام macOS) لفعل ذلك. ستظهر نافذة منبثقة تطلب منك تحديد المشهد الرئيسي. انقر على زر التحديد ثم انقر مرتين على label.tscn في المربع الذي يظهر لك يجب أن تشغل العرض التوضيحي مرة أخرى وعند تشغيلك للمشروع في المرات القادمة، سيستخدم جودو هذا المشهد كنقطة بداية. الخلاصة تناولنا في هذا المقال مفاهيم العقد Nodes والمشاهد Scenes في محرك الألعاب Godot ورأينا أن العقد هي الوحدات الأساسية التي تشكل مكونات اللعبة وقد تكون عبارة عن صورة أو صوت أو عنصر واجهة مستخدم، وأنها تتكون من خصائص قابلة للتعديل وتستجيب لاستدعاءات التحديث في كل إطار من إطارات اللعبة، كما استعرضنا كيفية تركيب العقد داخل شجرة لتشكيل مشهد يمثل كيانًا مستقلاً في اللعبة مثل شخصية أو مستوى ما. وفي المقال التالي، سنناقش مفهومًا رئيسيًا آخر في برمجة الألعاب باستخدام جودو وهو إنشاء نسخ من المشهد. ترجمة - وبتصرف - لقسم Nodes and Scenes من توثيق جودو الرسمي. اقرأ أيضًا مطور الألعاب: من هو وما هي مهامه تعرف على أشهر لغات برمجة الألعاب دليلك الشامل إلى بناء كاميرا خاصة بشاشات اللمس في محرّك اﻷلعاب جودو إعداد محرك الألعاب جودو Godot للعمل مع قاعدة البيانات SQLite
-
آخر ما يلزمنا ﻹكمال اللعبة ثنائية الأبعاد "تفادي الزواحف" التي بدأنا العمل عليها في مقال سابق هو بناء واجهة المستخدم user interface المخصصة عرض أشياء مهمة مثل النتيجة أو عبارة "انتهت اللعبة" وزر ﻹعادة اللعب. لهذا سننشىء في مقال اليوم مشهدًا جديدًا ثم نضيف عقدة من النوع CanvasLayer (واجهة مستخدم) باسم HUD وهي اختصار للكلمات "Heads-up display" بمعنى "شاشة المقدمة" والتي تعرض معلومات عن اللعبة وتظهر على شكل طبقة فوق نافذة عرض اللعبة. إنشاء واجهة اللعبة تسمح لك العقدة CanvasLayer في محرك الألعاب جودو GODOT برسم عناصر واجهة المستخدم على طبقة فوق بقية مشاهد اللعبة، وبالتالي لن تغطيها أية عناصر أخرى كاللاعب أو اﻷعداء. في لعبتنا الحالة، نحتاج لأن تعرض الواجهة HUD المعلومات التالية: النتيجة وتغيّرها ScoreTimer. رسالة مثل "انتهت اللعبة" أو "استعد!". زر البداية "ابدأ" لكي تبدأ اللعب. إن الصنف اﻷساسي لهذه العقدة هو Control، وسنحتاج في واجهتنا لنوعين من عناصر التحكم Control هما عنوان Label وزر Button، لهذا عليك إنشاء ثلاث عقد أبناء للعقدة HUD كالتالي: عنوان Label باسم ScoreLabel. عنوان Label باسم Message. زر Button باسم StartButton. مؤقت Timer باسم MessageTimer. انقر على العنوان ScoreLabel ثم اكتب رقمًا في الحقل Text ضمن الفاحص، وكما تلاحظ، فإن الخط المستخدم افتراضيًا للعقدة Control صغير وغير مناسب، لذلك سنستخدم خطًا موجودًا ضمن المجلد "font" يُدعى "Xolonium-Regular.ttf" كالتالي: في نافذة الفاحص وتحت اللوحة انقر على Theme Overridesثم Fonts ثم Font ثم اختر "تحميل" واختر الملف "Xolonium-Regular.ttf": زد حجم الخط إلى 64 في الحقل "Font Size" ثم كرر نفس العملية على العنوان اﻵخر وعلى الزر في المشهد. ملاحظة: للعقدة Control موقع وأبعاد لكنها تمتلك أيضًا ما يُعرف بالمربط Anchor الذي يحدد نقطة اﻷصل، وهي النقطة المرجعية لحواف العقدة. رتب العقد كما في لقطة الشاشة التالية، وبإمكانك سحب العقد إلى المكان الذي تريده يدويًا أو استخدم المربط لضبط الموقع بدقة أكبر: العنوان ScoreLabel أضف النص 0 اضبط المحاذاة اﻷفقية Horizontal Alignment والعمودية Vertical Alignment على Center. اختر القيمة Center Top لضبط المربط (الخاصية Anchor Preset). الرسالة Message أضف النص "تفادي الزواحف". اضبط المحاذاة اﻷفقية Horizontal Alignment والعمودية Vertical Alignment على Center. اضبط قيمة "نمط الالتفاف التلقائي Autowrap Mode" على Word وإلا سيبقى النص على نفس السطر. اضبط قيمة الخاصية Size X الموجودة على المسار Control>Layout>Transform على القيمة 480 اختر القيمة Center لضبط المربط (الخاصية Anchor Preset). الزر StartButton أضف النص "ابدأ". اضبط قيمة الخاصية Size X الموجودة على المسار Control>Layout>Transform على القيمة 200 والخاصية Size Y على القيمة 100. اختر القيمة Center Bottom لضبط المربط (الخاصية Anchor Preset). ا ضبط قيمة الخاصية Position Y الموجودة على المسار Control>Layout>Transform على القيمة 580. وأخيرًا، اضبط الخاصية Wait Time في المؤقت MessageTimer على 2، والخاصية One Shot على "فعّال ON" ثم أضف السكربت التالي إلى HUD: extends CanvasLayer # Notifies `Main` node that the button has been pressed signal start_game نريد اﻵن عرض رسالة مؤقتة مثل " استعد!" لهذا سنضيف الشيفرة التالية: func show_message(text): $Message.text = text $Message.show() $MessageTimer.start() علينا أيضًا معالجة الحالة التي يخسر فيها اللاعب، لهذا ستعرض الشيفرة التالية رسالة "انتهت اللعبة" لمدة ثانيتين ثم تعود إلى الشاشة الرئيسية وتعرض بعد توقف صغير الزر "ابدأ": func show_game_over(): show_message("Game Over") # Wait until the MessageTimer has counted down. await $MessageTimer.timeout $Message.text = "Dodge the\nCreeps!" $Message.show() # Make a one-shot timer and wait for it to finish. await get_tree().create_timer(1.0).timeout $StartButton.show() تُستدعى الدالة اﻷخيرة عندما يخسر اللاعب. ملاحظة: إن أردت أن توقف اللعبة لفترة وجيزة، بإمكانك استخدام الدالة ()create_timer العائدة لشجرة المشاهد بدلًا من استخدام عقدة مؤقت. وهذه الدالة مفيدة جدًا في إضافة تأخير زمني في الحالات المشابهة لحالتنا التي نرغب فيها الانتظار قليلًا قبل عرض زر "إبدأ". أضف الشيفرة التالية اﻵن لتحديث النتيجة: func update_score(score): $ScoreLabel.text = str(score) صل اﻹشارة ()timeout العائدة للمؤقت MessageTimer واﻹشارة ()pressed العائدة للزر StartButton ثم عدل الشيفرة لتصبح كالتالي: func _on_start_button_pressed(): $StartButton.hide() start_game.emit() func _on_message_timer_timeout(): $Message.hide() ربط المشهد HUD بالمشهد الرئيسي Main بعد أن انتهينا من إنشاء المشهد HUD سنعود إلى المشهد الرئيسي Main. انشئ نسخة من المشهد HUDضمن المشهد الرئيسي كما نسخنا مشهد اللاعب سابقًا. ويجب أن تبدو شجرة المشاهد كالتالي حتى يكون كل شيء في مكانه الصحيح: علينا اﻵن وصل وظائف المشهد HUD إلى السكربت الرئيسي، ويتطلب ذلك بعد اﻹضافات إلى المشهد الرئيسي. صل اﻹشارة start_game للمشهد HUD إلى الدالة ()new_game في المشهد الرئيسي بالنقر على نسخة HUD في المشهد الرئيسي ثم الانتقال إلى "عقدة" في الشريط الجانبي واختيار اﻹشارة start_game ثم النقر عليها نقرًا مزدوجًا لتظهر نافذة "قم بوصل اﻹشارة إلى الدالة" ثم النقر على زر "Pick" في أسفلها واختيار الدالة ()new_game. تأكد من وجود اﻷيقونة الخضراء إلى جوار ()func new_game في السكربت الرئيسي، ولا تنس إزالة الاستدعاء ()new_game من الدالة ()ready_ ﻷننا لا نريد أن تبدأ اللعبة تلقائيًا. عدّل الشيفرة الموجودة ضمن الدالة ()new_game لتعرض الرسالة "استعد!": $HUD.update_score(score) $HUD.show_message("Get Ready") كما عليك استدعاء دالة HUD الموافقة للدالة ()game_over في السكربت الرئيسي: $HUD.show_game_over() أضف أخيرًا الشيفرة التالية إلى الدالة ()on_score_timer_timeout_ كي يبقى عداد النتيجة متزامنًا مع التغيرات: $HUD.update_score(score) أصبحت اﻵن جاهزًا للعب! انقر على زر تشغيل المشهد، وإذا طُلب منك اختيار المشهد اﻷساسي اختر main.tscn. إزالة الزواحف القديمة عندما تنتهي اللعبة وتحاول اللعب مجددًا ستبقى الزواحف من اللعبة القديمة موجودة على الشاشة ومن اﻷفضل أن تختفي جميعها قبل البدء بلعبة جديدة. إذًا لابد من طريقة ﻹخبار الزواحف بتدمير نفسها، وذلك باستخدام الميزة "المجموعات group". اختر المشهد ثم اختر العقدة الرئيسية وانقر على نافذة "عقدة" إلى جوار الفاحص وهو نفس المكان الذي تجد فيه اﻹشارات. انقر على العنوان "مجموعات" ثم اكتب اسمًا للمجموعة الجديدة وليكن "mobs" ثم انقر "أضف" وستظهر المجموعة الجديدة تحت الصندوق النصي: أضف السطر التالي إلى الدالة ()new_game في السكربت الرئيسي: get_tree().call_group("mobs", "queue_free") تستدعي الدالة ()call_group تابعًا محددًا (المعامل الثاني لها) باسمه وتطبقه على كل عناصر المجموعة المختارة (المعامل الأول)، وفي حالتنا يحذف كل زاحف نفسه. وضع اللمسات اﻷخيرة على اللعبة اكتملت اللعبة حاليًا من ناحية الوظيفة، وما سنفعله تاليًا هو بعض اﻹضافات لتحسين المظهر العام لها. الخلفية قد يجد البعض أن الخلفية الرمادية غير جذابة لذا دعونا نغير اللون. ومن الطرق المتبعة نجد العقدة ColorRect التي يجب وضعها تحت العقدة Main مباشرة كي تُرسم خلف جميع العقد. ولهذا العقدة خاصية واحدة هي Color. اختر اللون الذي تريده ثم انقر على Layout>Anchor Preset واختر "على كامل المستطيل"، كما يمكنك تنفيذ العملية من خلال شريط اﻷدوات أعلى نافذة العرض. بإمكانك أيضًا استخدام صورة في الخلفية إن أردت لكنك ستحتاج إلى عقدة من نوع TextureRect. إضافة تأثيرات صوتية للعبة مقاطع الصوت والموسيقى من أكثر العوامل التي تزيد من جاذبية تصميم الألعاب الإلكترونية، لهذا وضعنا في المجلد "art" الخاص بلعبتنا ملفين صوتيين الأول هو "House In a Forest Loop.ogg" لموسيقى الخلفية واﻵخر "gameover.wav" لخسارة اللاعب. وعلينا اﻵن إضافة عقدتين من النوع AudioStreamPlayer كأبناء للعقدة Main وتسميتهما باسم Music و DeathSound. انقر بعد ذلك على الخاصية Stream لكل منهما ثم "تحميل" واختر المقطع الصوتي الموافق. تُدرج المقاطع الصوتية تلقائيًا وتكون الخاصية Loop غير مفعلة لها بصورة افتراضية. فإن أردت أن تشغل الموسيقى دون توقف، فانقر على السهم المجاور لاسم الملف الصوتي في الخاصية stream ثم اختر "اجعله فريدًا" وانقر بعدها على المربع الذي يضم اسم العقدة الصوتية أعلى حاوية الفاحص واختر "stream" ثم فعّل قيمة الخاصية Loop. ولتشغيل الموسيقى أضف التابع ()Music.play$ إلى الدالة ()new_game والتابع ()Music.stop$ إلى الدالة ()game_over ثم أضف أخيرًا التابع ()DeathSound.play$ إلى الدالة ()game_over. اختصارات لوحة المفاتيح طالما أن التحكم باللعبة سيكون عن طريق لوحة المفاتيح، فمن الملائم جدًا أن نبدأ اللعبة بالضغط على أحد مفاتيحها. يمكن إنجاز اﻷمر بالاستفادة من الخاصية "Shortcut" للزر Button. لهذا سننشئ إجراء دخل يربط مفتاحًا بالزر "Start". انقر على مشروع>إعدادات الشروع ثم انقر على النافذة الفرعية "خريطة اﻹدخال" في النافذة التي تظهر. انشئ بعد ذلك وبنفس الطريقة التي أنشأت بها سابقًا إجراءات الحركة اﻷربعة إجراءًا جديدًا باسم start_game واربطه بالمفتاح Enter. وإن أردت أيضًا ربط أزرار أي أداة تحكم أخرى باﻹجراءات السابقة، تأكد من توصيل هذه اﻷداة ثم انقر اﻷيقونة "+" الموجودة إلى يمين كل إجراء ثم اضغط زر اﻷداة الذي تريد ربطه بهذا اﻹجراء. عد إلى الواجه HUD وانقر على الزر StartButton ثم ابحث عن الخاصية "Shortcut" ضمن "الفاحص" ثم انشئ اختصارًا جديدًا بالنقر على مربع النص المجاور واختيار "جديدة Shortcut" ثم النقر على الاختصار الجديد وفتح "Events" وإضافة حدث جديد بالنقر على "Array[InputEvent]". أنشئ حدث جديدة InputEventAction باسم start_game: وهكذا عندما يظهر زر "ابدأ" يمكن النقر عليه أو الضغط على المفتاح "Enter" لتبدأ اللعب! الخلاصة لقد انتهيت من تصميم أولى ألعابك ثنائية البعد وقد تعلمت إنشاء الشخصيات التي تتحكم بها والزواحف التي تتكاثر لتملأ الشاشة واستطعت حساب النتيجة وأنجزت طريقة لتبدأ اللعبة وتنهيها وتستخدم واجهة لها وأضفت مقاطع صوتية. تهانينا! لا زال هناك الكثير لتتعلمه بالطبع لتكون مطور ألعاب إلكترونية محترف، لكن خذ الآن قسطًا من الراحة واستمتع بإنجازك! ترجمة -وبتصرف- للمقالين: Heads_up display و Finishing up اقرأ أيضًا المقال السابق: بناء لعبة ثنائية البعد عبر محرك الألعاب Godot - الجزء الثالث: إنشاء المشهد الرئيسي في اللعبة تعرف على أشهر لغات برمجة الألعاب كيف تصبح مبرمج ألعاب فيديو ناجح دليلك الشامل إلى بناء كاميرا خاصة بشاشات اللمس في محرّك اﻷلعاب جودو
-
بدأنا في مقال سابق بإنشاء لعبة إلكترونية باستخدام محرك الألعاب جودو، وحان الوقت اﻵن لضم كل شيء معًا وإنشاء مشهد كامل للعبتنا، لهذا سنبدأ في مقال اليوم باستكمال العمل على اللعبة وإنشاء مشهدها الأساسي وإعداده بالطريقة المناسبة. إنشاء عقدة المشهد الرئيسي للعبة لإنشاء مشهد جديد في محرك ألعاب جودو علينا إنشاء عقدة اسمها Main من النوع Node ولا حاجة أن تكون من النوع Node2D لأنها مجرد حاوية لمنطق اللعبة ولن تحتاج فعلًا إلى وظائف هذا النوع من العقد. انقر على زر إنشاء نسخة (أيقونة السلسلة في نافذة المشهد) ثم اختر المشهد "Player.tscn". أضف بعد ذلك العقد التالية كأبناء للعقدة Main وفق اﻷسماء التالية: مؤقت Timer باسم MobTimer للتحكم بمعدل تكاثر الزواحف، واضبط الخاصية Wait Time له على 0.5. مؤقت Timer باسم ScoreTimer لزيادة النتيجة كل ثانية، واضبط الخاصية Wait Time له على 1. مؤقت Timer باسم StartTimer لإضافة تأخير بسيط قبل بدء اللعبة، واضبط الخاصية Wait Time له على 2. عقدة من النوع Marker2D اسمها StartPosition لتحديد موقع البداية بالنسبة للاعب. إضافة إلى ذلك، اضبط الخاصية One Shot للمؤقت StartTimer على "فعّال On" و الخاصية Position للعقدة StartPosition على القيمة التالية(450, 240). تكاثر الأعداء نستخدم المشهد الرئيسي Main في تكاثر الأعداء ونريدهم أن يظهروا في أماكن عشوائية على حواف الشاشة، لهذا أضف عقدة من النوع Path2D باسم MobPath كابن للعقدة الرئيسية. سترى عدة أزرار إضافية في أعلى المحرر عند اختيار اﻷيقونة Path2D: اختر الزر الموجود في المنتصف "إضافة عقدة (في مساحة خالية)" ثم ارسم مسارًا بالنقر ضمن المحرر في النقاط التي يعرضها الشكل التالي. ولكي يكون تحديد النقاط دقيقًا فعّل الخيارين "استخدام المحاذاة للشبكة Use Grid Snap" و "استخدم المحاذاة الذكية Use Smart Snap"، وستجدهما إلى يمين أيقونة "القفل" على شكل مغناطيس بجوار عدة نقاط وآخر بجوار شبكة كما توضح الصورة التالية: ملاحظة مهمة: ارسم المنحني باتجاه عقارب الساعة وإلا ستتكاثر الزواحف إلى الخارج بدلًا من الداخل. بعد تحديد أربع نقاط، انقر على أيقونة "إغلاق المنحني" وسيكتمل هذا المنحني. أضف بعد الانتهاء من رسم المنحني العقدة PathFollow2D كابن للعقدة MobPath وسمها MobSpawnLocation. ستدور هذه العقدة وتتبع المسار عندما تتحرك وبالتالي يمكن استخدامها لاختيار مواقع واتجاهات عشوائية على طول المسار. سيبدو المشهد الرئيسي اﻵن كما يلي: السكربت الرئيسي أضف سكربت (نص برمجي) إلى المشهد Main ثم أضف العبارة export var mob_scene: PackedScene@ التي تسمح لنا باختيار مشهد اﻷعداء الذي نريد صنع نسخة عنه: extends Node @export var mob_scene: PackedScene var score انقر على العقدة Main وستجد الخاصية Mob Scene ضمن الفاحص في لوحة "متغيرات السكربت"، وﻹسناد قيمة إلى هذه الخاصية يمكنك اتباع إحدى الطريقتين التاليتين: اسحب المشهد من حاوية نظام الملفات وأفلته ضمن الخاصية Mob Scene. انقر على زر السهم المجاور للخاصية Mob Scene وانقر "تحميل" ثم اختر mob.tscn. اختر نسخة العقدة Player الموجودة ضمن العقدة Main ثم انقر على حاوية "عقدة" إلى جوار "الفاحص" في الشريط الجانبي لترى قائمة تضم جميع اﻹشارات للعقدة Player. جد اﻹشارة hit وانقر عليها نقرة مزدوجة (أو انقر عليها بالزر الأيمن واختر "توصيل"). ستفتح هذه العملية نافذة جديدة نريد فيها إنشاء دالة جديدة ندعوها game_over تعالج ما نريد فعله عند انتهاء اللعبة. اكتب "game_over" في المربع النصي "الدالة المتلقية Receiver Method" أسفل النافذة ثم انقر زر "وصل Connect". وما سيحدث اﻵن أن اﻹشارة hit التي بثها اللاعب Player سيعالجها السكربت اﻷساسي "Main". أضف اﻵن الشيفرة التالية إلى الدالة الجديدة، وأضف أيضًَا الدالة new_game التي تحضّر كل شيء عند بداية اللعبة: func game_over(): $ScoreTimer.stop() $MobTimer.stop() func new_game(): score = 0 $Player.start($StartPosition.position) $StartTimer.start() صل اﻵن اﻹشارة ()timeout العائدة إلى كل عقدة مؤقت (StartTimer و ScoreTimer و MobTimer) إلى السكربت الرئيسي. وسيشغل المؤقت StartTimer المؤقتين الآخرين ويزيد المؤقت ScoreTimer النتيجة بمقدار 1. func _on_score_timer_timeout(): score += 1 func _on_start_timer_timeout(): $MobTimer.start() $ScoreTimer.start() سننشئ ضمن الدالة نسخة عن العدو (الزواحف)، لهذا، سنختار مكانًا عشوائيًا للبدء على المسار Path2D ونضبط حركة العدو. تدور العقدة تلقائيًا عندما تلحق بالمسار، لهذا سنستفيد منها في اختيار جهة حركة الزاحف وموقعه. وعندما تتكاثر الزواحف سنختار قيمة عشوائية لسرعة حركتها بين 150 و 250. وانتبه إلى أن إضافة نسخة جديدة إلى المشهد تكون من خلال التعليمة add_child: func _on_mob_timer_timeout(): # mob scene أنشئ نسخة من مشهد الزاحف var mob = mob_scene.instantiate() # Path2D اختر مكانًا عشوائيًا على المسار. var mob_spawn_location = get_node("MobPath/MobSpawnLocation") mob_spawn_location.progress_ratio = randf() # اجعل اتجاه الزاحف عمودًا على اتجاه المسار var direction = mob_spawn_location.rotation + PI / 2 # اختر موقعًا عشوائيًًًًًًًًًا للزاحف mob.position = mob_spawn_location.position # أضف بعض العشوائية إلى المسار direction += randf_range(-PI / 4, PI / 4) mob.rotation = direction # اختر سرعة الزاحف var velocity = Vector2(randf_range(150.0, 250.0), 0.0) mob.linear_velocity = velocity.rotated(direction) # إجعل الزواحف تتكاثر بإضافتها إلى الشاشةالرئيسية add_child(mob) ملاحظة هامة: قد تتساءل لماذا نستخدم العدد PI في الدوال التي تتعامل مع الزوايا؟ لأن جودو يستخدم الراديان لقياس الزوايا. تمثّل PI نصف دوره كما يمكن استعمال TAU التي تمثّل دورة كاملة. لكن إن كنت تفضّل العمل مع الدرجات ستحتاج إلى الدالتين ()rad_to_deg و ()deg_to_rad للتحويل بين الدرجات والراديان. اختبار المشهد نختبر اﻵن إذا كانت كل شيء على ما يرام حتى اللحظة، لهذا استدعي الدالة new_game ضمن الدالة ()ready_: func _ready(): new_game() لنجعل أيضًا المشهد Main المشهد الرئيسي في اللعبة، وهو المشهد الذي يعمل تلقائيًا. انقر على زر التشغيل واختر main.tscn عندما يُطلب ذلك. من المفترض أن تكون قادرًا على تحريك اللاعب في جميع الاتجاهات وترى الزواحف تتحرك وتتكاثر، وسترى كيف يختفي اللاعب عندما يصطدم بالعدو. عندما تتأكد أن كل شيء يعمل جيدًا أزل الدالة new_game من الدالة ()ready_. الخلاصة تعلمنا في مقال اليوم كيف ننشئ المشهد الأساسي للعتبنا الإلكترونية وسنكتفي في هذا المقال بهذه المرحلة، ومع ذلك لا تزال اللعبة غير مكتملة وتنقصها بعض اللمسات النهائية وإنشاء واجهة مناسبة للعبة، لذا سنشرح خطوات تعزيز اللعبة بواجهة تتضمن تأثيرات صوتية واختصارات لوحة المفاتيح وغيرها من الخيارات وهذا ما سنراه في المقال التالي من هذه السلسلة. ترجمة -وبتصرف- للمقال: The main game scene اقرأ أيضًا المقال السابق: بناء لعبة ثنائية البعد عبر محرك الألعاب Godot - الجزء الثاني: إنشاء مشاهد اللعبة وبرمجتها إعداد محرك الألعاب جودو Godot للعمل مع قاعدة البيانات SQLite مدخل إلى محرك الألعاب جودو Godot مطور الألعاب: من هو وما هي مهامه
-
بعد أن أنشأنا في المقال السابق ملفات مشروع لعبة "تفادي الزواحف" ونظمناه، سنبدأ في هذا المقال بالعمل على شخصيات اللعبة (لاعب أساسي وأعداء). إذ سنبني المشهد اﻷول Player (وهو كائن أو عقدة) للاعب وآخر Mob للأعداء، ومن ميزات إنشاء مشهد مستقل لكل منهما هو إمكانية اختبارها بشكل مستقل وقبل أن ننشئ بقية أجزاء اللعبة. هيكلية العقدة نحتاج بداية إلى عقدة جذرية للاعب، وكقاعدة عامة، لا بد أن تعكس العقدة الجذرية للمشهد الوظيفة المرغوبة للكائن وما هو هذا الكائن. لهذا، انقر على زر "عقدة أخرى" وأضف عقدة من نوع Area2D إلى المشهد: سيعرض جودو أيقونة تنبيه إلى جوار العقدة في شجرة المشاهد، تجاهلها اﻵن وسنعود إليها لاحقًا. نتمكن باستخدام Area2D من استشعار الكائنات التي تتداخل أو تعمل ضمن اللاعب، وسنغير اسم العقدة الجديدة إلى Player بالنقر المضاعف عليها. وبعد إنشاء العقدة الجذرية، سنضيف عقدًا إضافية لمنحها مقدرات وظيفية أكبر. لكن قبل ذلك، علينا أن نتأكد من عدم تحريك أو تغيير العقد اﻷبناء عند النقر عليهم. لهذا انقر على اﻷيقونة الواقعة على يسار أيقونة القفل في شريط أدوات المشهد (سيعرض لك وصف اﻷداة عند تمرير مؤشر الفأرة فوقها العبارة "اجعل فروع العقدة المختارة غير قابلة للاختيار"): احفظ المشهد بالنقر على مشهد>حفظ أو اضغط Ctrl + S في ويندوز ولينكس أو Cmd + S في ماك أو إس. سوف نستخدم في مشروعنا أسلوب التسمية المتبع في محرك الألعاب جودو وهو كالتالي حسب لغة البرمجة المستخدمة: في لغة GDScript: نتبع أسلوب باسكال في اﻷصناف (الحرف اﻷول من كل كلمة كبير)، وفي الدوال والمتغيرات أسلوب اﻷفعى (تفصل بين كل كلمتين شرطة سفلية _)، أما الثوابت فتكتب كل حروفها بالشكل الكبير. في لغة #C: تسمى الأصناف والمتغيرات والتوابع بأسلوب باسكال، ونستخدم في تعريف الحقول الخاصة private والمتغيرات المحلية والمعاملات أسلوب سنام الجمل (الحرف اﻷول من كل كلمة كبير ما عدا الكلمة اﻷولى). وتأكد من كتابة أسماء التوابع بشكل دقيق عند ربط اﻹشارات. الرسم المتحرك للشخصية (Sprite) انقر على أيقونة العقدة Player وأضف عقدة ابن من نوع AnimatedSprite2D (استخدم Ctrl + A في ويندوز ولينكس) والتي تتولى أمور مظهر وتحريك اللاعب، ولاحظ وجود إشارة تحذير إلى جانب العقدة. تحتاج العقدة موردًا يُدعى "إطارات السبرايت SpriteFrames"، وﻹنشائه، ابحث عن الخاصية SpriteFrames ضمن النافذة الفرعية Animation في حاوية "الفاحص" ثم انقر على مربع النص empty واختر "جديدة SpriteFrame". انقر مجددًا لفتح لوحة "إطارات-اﻷرسومة". ستجد إلى اليمين قائمة بالرسومات، انقر على الافتراضية وسمها "walk"، ثم انقر على أيقونة إضافة إطار في الزاوية العليا اليمينية وأضف إطارًا آخر سمِّه "up". ابحث بعد ذلك عن الصور المناسبة في المجلد "art" في نظام الملفات وانقل الصور playerGrey_walk[1/2] إلى اﻹطار "walk" بالسحب واﻹفلات، أو بفتح الصورة من خلال أيقونة المجلد وكرر العملية بنقل الصورتين playerGrey_up[1/2] إلى اﻹطار "up". إن أبعاد الصور أكبر من أبعاد نافذة اللعبة، ولا بد من تصغير هذه الصور بالنقر على العقدة AnimatedSprite2D ومن ثم ضبط الخاصية Scale على القيمة Scale. ستجد هذه الخاصية في حاوية الفاحص تحت العنوان Node2D والقائمة "Transform تحويل": أضف أخيرًا عقدة من النوع CollisionShape2D لتكون ابنًا للعقدة Player، وتحدد هذه العقدة "صندوق التصادم" المحيط باللاعب أو حدود منطقة التصادم المحيطة به. وتلائمنا في هذا الصدد كائن من النوع CapsuleShape2D، لهذا انقر في "الفاحص" على المربع إلى جوار العنوان واختر "جديدة CapsuleShape2D". استخدم بعد ذلك مقبضي التحكم بالأبعاد (النقطتين الحمراوين) في نافذة المشهد لتغطية الأرسومة بالغلاف: عندما تنتهي من ذلك سيكون شكل مشهد اللاعب Player كالتالي: تأكد من حفظ المشهد مجددًا بعد هذه التغييرات. سنضيف تاليًا سكربت إلى عقدة اللاعب لتحريكه ثم نُعدّ آلية لترصد التصادم لنعرف إذا ما اصطدم اللاعب بشيء ما. كتابة الشيفرة اللازمة لتحريك اللاعب سنعمل في هذا القسم على كتابة شيفرة لتحريك اللاعب، وإعداده ليترصد التصدامات، لهذا، علينا إضافة بعض الخواص الوظيفية التي لا تقدمها العقد المتوفرة عن طريق إضافة سكربت أو كود برمجي إلى العقدة. انقر على العقدة Player ثم انقر على "إلحاق نص برمجي": لا داعي لتغيير أي شيئ في نافذة إلحاق نص برمجي، اترك كل شيء كما هو وانقر على زر "أنشئ". ملاحظة: إن كنت تريد إنشاء سكربت #C، اختر هذه اللغة من القائمة المنسدلة قبل النقر على "أنشئ". extends Area2D @export var speed = 400 # How fast the player will move (pixels/sec). var screen_size # Size of the game window. تُسمح لنا التعليمة export قبل المتغير speed بضبط قيمته في نافذة الفاحص. ولهذا اﻷمر فائدته إن أردت تعديل قيمة المتغير بالطريقة نفسها التي تعدّل فيها خاصيات أي عقدة موجودة أصلًا في جودو. انقر اﻵن على العقدة Player وسترى الخاصية موجودة ضمن قسم "متغيرات السكربت" في حاوية الفاحص (تحت نفس الاسم الذي يحمله ملف السكربت). وتذكر أن تغيير القيمة في هذا المكان سيلغي القيمة التي يحملها المتغير في السكربت. يتضمن السكربت player.gd تلقائيًا الدالتين ()ready_ و ()process_. فإن لم تختر القالب الافتراضي للسكربت أنشئ هاتين الدالتين. وتُستدعى الدالة ()ready_ عندما تدخل عقدة شجرة المشاهد وهو وقت مناسب لمعرفة أبعاد نافذة اللعبة func _ready(): screen_size = get_viewport_rect().size بإمكاننا اﻵن استخدام الدالة ()process_ لتحديد ما يفعله اللاعب، وتُستدعى هذه الدالة من أجل كل إطار ونستخدمها لتحديث العناصر في لعبتنا والتي نتوقع أن تتغير أحيانًا. فمن أجل لاعبنا لا بد من: التحقق من وجود دخل. تحريكه في الاتجاه المطلوب. تشغيل الرسوم المتحركة المناسبة. كما ذكرنا علينا بداية التحقق من الدخل، أي هل يضغط اللاعب على زر معين مثلًا؟ ففي لعبتنا هناك عناصر إدخال لأربعة اتجاهات علينا أن نتحقق منها. عُرّفت إجراءات الدخل في إعدادات المشروع تحت عنوان "خريطة الإدخال". وفيها نستطيع تعريف أحداث مخصصة وتعيين أزرار مختلفة، وأحداث تتعلق بالفأرة وغيرها من المدخلات. انقر على المشروع، ثم إعدادات المشروع لتفتح نافذة اﻹعدادات، ثم انقر على النافذة الفرعية "خريطة الإدخال" في الأعلى. اكتب بعد ذلك "move_right" (تحرك يمينًا) في الشريط العلوي وانقر الزر "أضف" ﻹضافة الإجراء move_right. علينا اﻵن أن نربط اﻹجراء بزر معين، لهذا انقر على أيقونة "+" إلى اليسار كي نفتح نافذة "تهيئة الحدث event configuration". كل ما عليك الآن هو النقر على الزر الذي تريد ربطه بالحدث سواء زر لوحة مفاتيح أو زر الفأرة. انقر الآن مفتاح السهم اليميني على لوحة المفاتيح وسيظهر الخيار تلقائيًا في مربع "يتم رصد المدخلات.." انقر بعد ذلك على "حسنًا" لتعيين المفتاح. كرر نفس الخطوات لربط الحركات الثلاث الباقية كالتالي: اربط move_left بالسهم اليساري. اربط move_up بالسهم للأعلى. اربط move_down بالسهم للأسفل. يجب أن تظهر خارطة المدخلات كالتالي: انقر اﻵن على "إغلاق" ﻹغلاق إعدادات المشروع. ملاحظة: ربطنا مفتاح واحد بكل إجراء دخل، لكنك تستطيع أن تربط أكثر من مفتاح أو زر عصا تحكم أو زر فأرة بإجراء الدخل نفسه. تستطيع أن تقرر إذا ما ضُغط زر باستخدام العبارة ()Input.is_action_pressed التي تعيد القيمة true إذا ضُغط الزر وfalse إن لم يُضغط. func _process(delta): var velocity = Vector2.ZERO # The player's movement vector. if Input.is_action_pressed("move_right"): velocity.x += 1 if Input.is_action_pressed("move_left"): velocity.x -= 1 if Input.is_action_pressed("move_down"): velocity.y += 1 if Input.is_action_pressed("move_up"): velocity.y -= 1 if velocity.length() > 0: velocity = velocity.normalized() * speed $AnimatedSprite2D.play() else: $AnimatedSprite2D.stop() بدأنا بضبط قيمة velocity على (0, 0) فلن يتحرك اللاعب افتراضيًا، وبعد ذلك تحققنا من كل المدخلات وأضفنا إلى المتغير velocity أو طرحنا منه للحصول على الاتجاه. فلو ضغطنا على السهم اليميني واليساري في نفس الوقت ستكون نتيجة المتجه velocity هي (1, 1)، وفي هذه الحالة نكون قد أضفنا حركة أفقية وعمودية في نفس الوقت، وسيتحرك اللاعب بشكل أسرع بالاتجاه القطري موازنة بالحالة التي يتحرك فيها أفقيًا فقط. لكن بإمكاننا منع حدوث هذا اﻷمر بتسوية قيمة السرعة بأن نضبط قيمتها على 1 ثم نضربه بالقيمة المطلوبة ولن تكون السرعة في الاتجاه القطري عندها أكبر. كما علينا أن تحقق فيما لو تحرّك اللاعب كي نستدعي الدالتين ()play و ()stop في AnimatedSprite2D يُعيد $ عقدة معينة إن كانت موجودة في نفس المسار النسبي ويعيد null إن لم تكن موجودة في هذا المسار. وطالما أن AnimatedSprite2D هي عقدة ابن للعقدة الحالية، بإمكاننا استخدام AnimatedSprite2D$. وطالما حددنا الآن اتجاه الحركة، بإمكاننا تحديث موقع اللاعب. كما نستطيع باستخدام الدالة ()clamp منع اللاعب من مغادرة الشاشة وتقييده ضمن مجال محدد. أضف الشيفرة التالية إلى أسفل الدالة ()process_ (انتبه إلى أن الشيفرة غير منزاحة تحت else? position += velocity * delta position = position.clamp(Vector2.ZERO, screen_size) انقر على الزر "شغل المشهد الحالي" (F6 أو Cmd+R في ماك أو إس) وتأكد من قدرتك على تحريك اللاعب ضمن المشهد في جميع الاتجاهات. اختيار الرسوم المتحركة بإمكاننا تحريك اللاعب الآن، لكننا نحتاج إلى تغيير الرسم المتحرك الذي يمثّل الكائن وفقًا لاتجاهه. ليدنا الرسم "تحرّك" والذي يعرض اللاعب وهو يتحرك يمينًا، ولا بد من قلبه أفقيًا حتى يعبّر عن التحرك نحو اليسار باستخدام الخاصية flip_h. وكذلك لدينا الرسم "up" الذي يجب أن يُعكس عموديًا لتمثيل الحركة نحو اﻷسفل باستخدام الخاصية flip_v. لهذا عليك اضافة الشيفرة التالية إلى أسفل الدالة ()process_: if velocity.x != 0: $AnimatedSprite2D.animation = "walk" $AnimatedSprite2D.flip_v = false # اطلع على املاحظة التالية بخصوص اﻹسناد المنطقي $AnimatedSprite2D.flip_h = velocity.x < 0 elif velocity.y != 0: $AnimatedSprite2D.animation = "up" $AnimatedSprite2D.flip_v = velocity.y > 0 ملاحظة: يُعد استخدام طريقة اﻹسناد المنطقي في هذه الشيفرة اختصارًا شائعًا. فما نفعله هو اختبار موازنة (منطقي) وإسناد قيمة منطقية، لهذا يمكننا تنفيذ اﻷمرين معًا. وما يفعله هذا الاختصار مطابق لعمل الشيفرة التالية: if velocity.x < 0: $AnimatedSprite2D.flip_h = true else: $AnimatedSprite2D.flip_h = false شغّل المشهد وتأكد من تغيّر الرسم مع تغير اتجاه الحركة. عندما تتأكد أن كل شيء يعمل كما يجب، أضف السطر التالي إلى الدالة ()ready_ كي يختفي اللاعب في بداية اللعبة. hide() إعداد التصادمات نريد من اللاعب Player أن يعرف متى يستطدم بالعدو، لكننا لم نصنع اﻷعداء بعد! لا بأس بذلك لأننا سنستخدم حاليًا إشارات جودو لننجز اﻷمر. أضف اﻷسطر التالية إلى أعلى السكربت. فإن كنت تستخدم GDScript، أضفها بعد العبارة extends Area2D، وإن كنت تستخدم لغة #C ضعها بعد العبارة public partial class Player: Area2D. signal hit تُعرّف التعليمة السابقة إشارة خاصة باسم "hit" يبثها اللاعب (يرسلها) عندما يتصادم مع عدو. وسنستخدم الكائن Area2D لالتقاط هذه اﻹشارة. اختر العقدة Player وانقر على النافذة الفرعية "عقدة" ضمن لوحة "الفاحص" كي تعرض قائمة اﻹشارات التي يمكن للاعب بثها: لاحظ وجود إشارتنا المخصصة "hit" أيضًا ضمن تلك القائمة. وطالما أن العدو سيكون عقدة من النوع RigidBody2D، سنحتاج إلى الإشارة body_entered(body: Node2D). أوجد تلك اﻹشارة في القائمة ثم انقر عليها بالزر اليميني واختر "يتصل" لتظهر نافذة "قم بوصل اﻹشارة إلى دالة". لا حاجة لتغيير أي شيء، بل انقر فقط على "وصل" وسيوّلد جودو تلقائيًا الدالة المناسبة في الشيفرة: لاحظ اﻷيقونة الخضراء إلى يسار الشيفرة المخصصة للإشارة وتدل على أن إشارة متصلة مع هذه الدالة. أضف اﻵن الشيفرة التالية إلى الدالة: func _on_body_entered(body): hide() # يختفي اللاعب بعد أن يصطدم. hit.emit() # Must be deferred as we can't change physics properties on a physics callback. $CollisionShape2D.set_deferred("disabled", true) في كل مرة يصدم بها العدو اللاعب ستُرسل اﻹشارة، ولا بد من تعطيل التصادم الخاص باللاعب كي لا نفعّل اﻹشارة hit أكثر من مرة. ملاحظة: قد ينتج عن تعطيل غلاف التصادم الخاص بالمنطقة خطأ إن حدث اﻷمرأثناء معالجة المحرّك للتصادمات. لهذا استخدم الدالة ()set_deferred ﻹخبار المحرّك ألا يعطّل غلاف التصادم حتى يرى أن اﻷمر آمن. أضف أخيرًا دالة نستدعيها ﻹعادة ضبط اللاعب عندما تبدأ لعبة جديدة func start(pos): position = pos show() $CollisionShape2D.disabled = false إنشاء شخصية العدو حان الوقت اﻵن ﻹنشاء اﻷعداء الذي يجب على اللاعب تفاديهم. ولن يكون سلوكهم معقدًا جدًا بل سيتحركون عشوائيًا على أطراف الشاشة، يأخذون اتجاهًا عشوائيًا ويتحركون وفق خط مستقيم. نبدأ عملنا بإنشاء مشهد باسم Mob يشكل الأساس الذي نشتق منه أي عدد نحتاجه من هذه الكائنات في لعبتنا. إعداد العقدة انقر على مشهد>مشهد جديد ثم أضف العقد التالية وفق الترتيب المبين: RigidBody2D: AnimatedSprite2D CollisonShape2D VisibleOnScreenNotifier2D ولا تنسَ ضبط العقدة اﻷم كي لا يمكن اختيار اﻷبناء كما فعلنا سابقًا عند بناء شخصية اللاعب. اختر بعد ذلك العقدة Mob ثم اضبط قيمة الخاصية Gravity Scale على 0، وذلك في قسم RigidBody2D ضمن الفاحص. يمنع هذا اﻷمر الأعداء من السقوط للأسفل. افتح المجموعة "Collision" الموجودة في اللوحة "CollisionObject2D" تحت "RigidBody2D" ضمن الفاحص. الغ بعد ذلك تفعيل الخيار 1 ضمن الخاصية Mask بالنقر عليه كي لا تتصادم اﻷعداء فيما بينها. اضبط العقدة كما فعلنا في مشهد اللاعب، وهنا نستخدم ثلاث رسومات هي fly و swim و walk، وهنالك صورتان لكل مشهد في المجلد "art". تُضبط الخاصية Animation Speed (سرعة التحريك) لكل رسم متحرك على حدى، لهذا اضبط كلًا منها على 3: بإمكانك اﻵن النقر على الزر "تشغيل الرسم المتحرك" إلى يسار "سرعة التحريك" لعرض الرسوم المتحركة. سنختار إحدى هذه الرسوم عشوائيًا حتى يكون للأعداء أشكال مختلفة، وكما هو الحال مع رسومات اللاعب لا بد من تصغير هذه الرسومات، وذلك بضبط الخاصية Scale على (0.75, 0.75) (راجع فقرة إنشاء اللاعب لتتذكر كيفية العمل). علينا اﻵن أن نضيف غلاف CapsuleShape2D من أجل التصادمات كما فعلنا مع اللاعب. ولكي يتماشى الغلاف مع الرسم المتحرك لا بد من تدويره بضبط الخاصية Rotation Degrees على 90 (تحت لوحة "Node2D" والقائمة "Transform" ضمن الفاحص). كتابة شيفرة تحريك العدو أضف سكربت إلى العقدة Mob كما فعلنا سابقًا: extends RigidBody2D نشغّل باستخدام الدالة ()ready_ الرسومات ونختار عشوائيًا أحد اﻷنواع الثلاث لهذه الرسوميات كالتالي: func _ready(): var mob_types = $AnimatedSprite2D.sprite_frames.get_animation_names() $AnimatedSprite2D.play(mob_types[randi() % mob_types.size()]) ما تفعله هذه الشيفرة هو الحصول على أسماء الرسومات من الخاصية frames للعقدة AnimatedSprite2D، وستكون النتيجة مصفوفة تضم اﻷنواع الثلاث: ["walk", "swim", "fly"]. ثم نختار عشوائيًا رقمًا بين 0 و 2 لاختيار أحد اﻹطارات الثلاث من المصفوفة السابقة (يبدأ العدد في المصفوفات من 0) بتطبيق التعليمة randi() % n، والتي تختار عددًا صحيحًا عشوائيًا بين 0 و n-1. وأخيرًا نحتاج إلى شيفرة كي يحذف العدو نفسه عندما يغادر شاشة اللعبة. ولتنفيذ ذلك صل الإشارة ()Screen_exited العائدة للعقدة ()VisibleOnScreenNotifier إلى العقدة Mob (راجع فقرة وصل إشارة اللاعب التي نفّذناها سابقًا) ثم أضف الأمر ()queue_free إلى الدالة التي تظهر في السكربت كالتالي: func _on_visible_on_screen_notifier_2d_screen_exited(): queue_free() وهكذا سيكتمل مشهد العدو. الخلاصة بهذا نكون قد وصلنا لنهاية مقالنا الحالي الذي أنشأنها فيه مشهدين من مشاهد اللعبة ثنائية الأبعاد في محرك جودو، تابع معنا المقال التالي الذي سنقوم فيه بضم المشهدين معًا والسماح للأعداء بالتكاثر على الشاشة والحركة لتحويل المشاهد إلى لعبة تعمل كما خططنا لها. ترجمة -وبتصرف- للمقالات: Creating the Player scene و Coding the player و Creating the enemy اقرأ أيضًا المقال السابق: بناء لعبة ثنائية البعد عبر محرك الألعاب Godot - الجزء الأول: تجهيز الملفات وضبط الإعدادات كيف تحصل على أفكار ألعاب فيديو ناجحة تعرف على أشهر لغات برمجة الألعاب مدخل إلى محرك الألعاب جودو Godot
-
ننقلك في سلسلة المقالات التالية خطوة بخطوة لإنشاء لعبة كاملة ثنائية البعد باستخدام محرّك اﻷلعاب جودو Godot. وفي نهاية السلسلة ستكون قد أنجزت لعبة بسيطة كتلك الموضحة في الصورة التالية: وسنتعلم من خلال هذه السلسلة كيفية عمل محرّر محرك الألعاب جودو Godot، وكيفية هيكلة المشروع، ومن ثم ستتعلم خطوات بناء اللعبة بشكل عملي. ملاحظة: هذا المقال هو مدخل إلى محرك اﻷلعاب جودو، ويفترض أنك تتمتع ببعض الخبرات البرمجية لاستخدام لغات برمجة الألعاب وبرمجة لعبتك الإلكترونية من خلالها. فإن كنت جديدًا في عالم البرمجة. ننصحك بالعودة إلى أكاديمية حسوب التي تضم عددًا كبيرًا من المقالات والمواضيع التي تناسب القادمين الجدد وتقدم أفضل الدورات التعليمية للبرمجة من الصفر وحتى الاحتراف. سوف نطلق على لعبتا اسم "تفادي الزواحف Dodge the creeps"، ومن المفترض أن تبتعد شخصية اللعبة عن اﻷعداء قدر اﻹمكان. ستتعلم من خلال هذه السلسلة كيف تقوم بما يلي: تنشئ لعبة مكتملة ثنائية البعد باستخدام محرك الألعاب جودو. تهيكل مشروع لعبة بسيطة. تحرك شخصية اللعبة وتغيير شكلها. تنشر أعداء عشوائيًا. تعيد نتيجة اللعبة. لماذا نطور لعبة ثنائية البعد 2D؟ إن كنت جديدًا في تطوير اﻷلعاب أو لا تألف بيئة جودو، ننصحك ان تبدأ بتعلم تصميم اﻷلعاب ثنائية البعد، فهي تسمح لك في تعلم بيئة العمل وترتاح فيها قبل أن تبدأ اﻷلعاب ثلاثية الأبعاد التي تميل أكثر إلى التعقيد. خُصِّصَ هذا المقال والمقالات اللاحقة للمبتدئين الذين لديهم أساسيات في التعامل مع محرك جودو، فإن كنت جديدًا في البرمجة ويصعب عليك كتابة الأكواد من الصفر، بإمكانك الاطلاع على الشيفرة المصدرية للعبة عبر جيت-هب وفهمها. كما حضرنا مسبقًا بعض الملحقات التي تحتاجها، لهذا سنقفز مباشرة إلى الشيفرة التي يمكنك تحميلها من المستودع المخصص على جيت-هب. إعداد المشروع سنُعّد في هذا المقال مشروعنا وننظمه، لهذا شغّل محرك ألعاب جودو وأنشئ مشروعًا جديدًا. ليس عليك سوى تحديد مسار مناسب لتخزين المشروع وبإمكانك ترك بقية القيم كما هي. بعدها عليك تحميل الأرشيف الذي يتضمّن كافة ملفات الصور والمقاطع الصوتية التي سنستخدمها في صنع اللعبة ثنائية الأبعاد الخاصة بنا، ثم استخرج محتوياته وانقل المجلدين /art و /fonts إلى مجلد اللعبة. يجب أن يبدو مجلد اللعبة مشابهًا للقطة الشاشة التالية: صُممت هذه اللعبة لنمط العرض الشاقولي portrait، لهذا لا بد من تعديل قياس نافذة اللعبة. انقر على المشروع ثم اختر إعدادات المشروع لفتح نافذة اﻹعدادات ثم افتح في العمود اليميني القائمة "عرض display" ثم انقر على "نافذة window". واضبط بعد ذلك اتساع نافذة العرض viewport width على 480 وارتفاعها على 720. وتحت الخيار "تمدد Stretch " اضبط "الوضع Mode" على القيمة canvas_items ونسبة العرض على القيمة keep، حيث يساهم هذان الخياران في اتساق العرض على شاشات مختلفة الأبعاد. تنظيم المشروع سنصنع في هذا المشروع ثلاثة مشاهد مستقلة هي Player و Mob وHUD بحيث تجتمع كلها في المشهد الرئيسي Main. ومن اﻷفضل في المشاريع اﻷكبر أن تنشئ مجلدًا يضم المشاهد المختلفة والسكربتات الملحقة بها، لكن لمشروع صغير كهذا، بإمكانك تخزينها في المجلد الجذري للمشروع الذي يُعرّف بالعنوان //:res . وستجد مجلد المشروع في حاوية "نظام الملفات" في الزاوية اليسارية السفلى. الخلاصة تعرفنا في مقال اليوم على أولى مراحل برمجة لعبة ثنائية الأبعاد في محرك الألعاب جودو Godot، وبدأنا بتحديد الشخصيات والمشاهد التي تضمها اللعبة، وحددنا طريقة تنظيم ملفاتها، وندعوك لمتابعة السلسلة التالية من هذه المقالات لتتعرف على الخطوات التالية العملية لإنجاز اللعبة وبرمجتها. ترجمة -وبتصرف- للمقالين: your first 2D game و Setting up the project اقرأ أيضًا المقال السابق: تعلم الميزات الجديدة في محرك الألعاب جودو وطرح الأسئلة حوله إعداد محرك الألعاب جودو Godot للعمل مع قاعدة البيانات SQLite مدخل إلى محرك الألعاب جودو Godot مطور الألعاب: من هو وما هي مهامه
-
بعد أن اطلعت في مقالات سابقة على مفاهيم محرك الألعاب جودو، حان وقت الحديث عن التصميم الذي يميزه، فكل محرك ألعاب مختلف عن اﻵخر ويلبي حاجات مختلفة. إذ يقدم كل محرك ألعاب مجالًا من الميزات المختلفة لكن تصميم كل محرك ألعاب متفرد عن اﻵخر. ويقود هذا اﻷمر إلى سياقات عمل مختلفة وطرق مختلفة في هيكلة اﻷلعاب. ويعود السبب في ذلك إلى الفلسفة المختلفة في التصميم لكل محرّك. نساعدك في هذا المقال على فهم طريقة عمل جودو انطلاقًا من بعض الدعائم البنيوية للمحرك، فما نعرضه ليس قائمةً بالميزات ولا موازنة بين المحركات. ولكي تعرف ما يناسبك من محركات، لابد من اختبارها بنفسك وفهم تصميمها ومحدوديتها. التصميم والتأليف كائني التوجه يتبنى محرك الألعاب جودو مفهوم التصميم كائني التوجه كأساس له من خلال نظام المشاهد المرن والتسلسل الهرمي للعقد. كما يحاول أن يبتعد عن اﻷنماط البرمجية البحتة كي يقدم طريقة واضحة ومباشرة لهيكلة ألعابك. فمن ناحية أولى، يسمح لك جودو في تأليف أو تجميع المشاهد. إذ تستطيع إنشاء مشهد لأضواء وامضة BlinkingLight وآخر لمصابيح مكسورة BrokenLantern تستخدم اﻷضواء الوامضة والتعامل معها ككائنات جاهزة. وستتمكن باستخدامها من بناء مدينة مملوءة بالمصابيح المكسورة ومن ثم تغيّر لون الضوء الوامض وتخزّين التغيرات لتتغير عندها حالة المصابيح المكسورة في المدينة دفعة واحدة. وفوق كل ذلك يدعم جودو الوراثة وهو أحد المبادئ الأساسية في البرمجة كائنية التوجه oop، فقد يكون مشهد جودو سلاحًا أو شخصية أو غرضًا ما أو بابًا أو مستوى أو جزء من مستوى وبالمختصر أي شيء تريده. ويعمل المشهد عمل الصنف class في الشيفرة الصرفة، ما عدا أنك حر في تصميمه من خلال المحرر مستخدمًا الشيفرة فقط أو مزيجًا من الاثنين. تختلف المشاهد في جودو عن الكائنات الجاهزة prefab في محركات اﻷلعاب ثلاثية اﻷبعاد اﻷخرى في إمكانية الوراثة عنها أو توسيعها. فقد تُنشئ ساحرًا بتوسعة مشهد الشخصية مثلًا. وعندما تُحدّث الشخصية في المحرر ستتحدث معها شخصية الساحر أيضًا. سيمكنك ذلك من بناء مشروعك كي يتطابق التصميم مع هيكلية الشيفرة. وتجدر الملاحظة أن جودو يقدّم أنواعًا مختلفة من الكائنات التي تُدعى عقدًا Nodes، ولكل منها غاية محددة. والعقد أجزاء من شجرة وترث دائمًا من العقدة اﻷب وصولًا إلى الصنف الرئيسي Node. وعلى الرغم من وجود عقد في جودو مثل اﻷشكال المتصادمة التي تستخدمها فيزيائية العقدة اﻷب، تُعد معظم العقد مستقلة عن بعضها. وبعبارة أخرى، لا تعمل العقد كما تعمل المكوّنات components في محركات ألعاب أخرى. فالعقد Sprite2D و Node2D و CanvasItem و Node، تمتلك جميع الخاصيات والميزات للأصناف اﻵباء كالتحويلات أو القدرة على رسم أشكال محددة وتصييرها باستخدام معالج لوني shader محدد. حزم حصرية بالكامل يحاول محرك الألعاب جودو تزويد المستخدمين بأدوات خاصة تلبي معظم احتياجاتهم. إذ يمتلك المحرّك فضاء عمل مخصص لكتابة السكربتات، ومحرر رسوميات، ومحرر خرائط، ومحرر معالج لوني، ومنقح، ومحلل أداء، باﻹضافة إلى قدرته على عرض التغييرات مباشرة على جهازك المحلي أو جهاز متصل عبر الشبكة. إن الغاية من ذلك كله تقديم حزمة متكاملة من اﻷدوات ﻹنشاء اﻷلعاب تحسين تجربة المستخدمين. بإمكانك بالطبع العمل مع برامج خارجية نظرًا لدعمه للإضافات plug-in، والتي تستطيع أن تنشئها بنفسك أيضًا! ويعود وجود لغة GDScript الخاصة بجودو إلى هذا السبب جزئيًا إضافة إلى دعمه لغة #C. فلغة GDScript مصممة لتلائم حاجات المطورين والمصممين ومتكاملة تمامًا مع المحرّك والمحرر. وتتيح لك GDScript كتابة شيفرة اعتمادًا على صياغة اﻹزاحة أو المسافات البادئة indentation، وتتعرف على اﻷنواع، وتزودك بميزة اﻹكمال التلقائي. وتُعد هذه اللغة من لغات برمجة الألعاب وتوفر شيفرة سهلة ومفهومة وتضم أنواعًا مخصصة مثل Vectors و Colors. ومع GDExtension يمكنك كتابة شيفرة عالية اﻷداء باستخدام لغات مصرّفة مثل C و ++C و Rust وبايثون (باستخدام Cython) دون إعادة تصريف المحرّك. ملاحظة: لا يقدم فضاء العمل ثلاثي الأبعاد نفس العدد من الميزات التي يقدمها ثنائي اﻷبعاد، ويحتاج إلى برامج خارجية أو إضافات لإنشاء التضاريس، وتحريك شخصيات الألعاب المعقدة وهكذا. مع ذلك، يزودك جودو بواجهة برمجية متكاملة لتوسعة وظائف المحرر باستخدام شيفرة اللعبة، سنرى ذلك لاحقًا في هذا المقال. (محرر وهو إضافة على جودو 2 ويساعد على إدارة الحالة والانتقالات بصريًا) محرّك مفتوح المصدر يقدّم جودو قاعدة برمجية مفتوحة المصدر بالكامل وفق ترخيص MIT. ويعني ذلك أن كل التقنيات التي تأتي معه هي حرة أيضًا. وقد طوّرت معظم أجزاء المحرّك من الصفر من قبل مساهمين. ويمكن لأي شخص استخدام اﻹضافات المناسبة لمشروعه ولا يعني ذلك أن تُشحن هذه اﻹضافات مع المحرك. من اﻷمثلة عليها نجد Google AdMob أو FMOD. وأيًا منها يمكن أن يأتي على شكل إضافة مصدرها طرف آخر. وتعني القاعدة البرمجية المفتوحة من ناحية أخرى أنه بإمكانك التعلم من محرّك الالعاب وتوسيعه كما تشاء، وتستطيع تنقيح اﻷلعاب بسهولة، إذ يطبع جودو رسائل الخطأ من خلال متتبع المكدّس stack tracer حتى لو اتت اﻷخطاء من المحرّك نفسه. ملاحظة: لا يؤثر ذلك على العمل الذي تنفذّه على جودو إطلاقًا. فلن ترتبط أي قيمة نصيّة بالمحرّك أو أي شيء تفعله عليه. محرّك يطوّره مجتمعه يُطوّر محرك الألعاب جودو من قبل مجتمعه الخاص، فهو منهم ولهم ولجميع مطوري اﻷلعاب، وحاجات ونقاشات المستخدمين هو اﻷساس الذي يقود تطوير نواته. وتركّز الميزات الجديدة التي يقدمها مطوّرو النواة على ما يقدم الفائدة لمعظم مستخدميه، والقلة فقط من مطوري النواة يعملون رسميًا بدوام كامل. لكن المشروع يضم أكثر من 600 مساهم حتى لحظة كتابة هذه المقالة. فالمبرمجون المساهمون يعملون على تطوير الميزات التي يحتاجونها لأنفسهم لهذا سترى تحسينات من جميع النواحي تظهر معًا عند كل إصدار رئيسي للمحرك. محرر جودو هو في الواقع لعبة جودو يعمل محرر جودو على محرّك اللعبة ويستخدم واجهة المحرّك الرسومية ويمكنه إعادة تحميل الشيفرة والمشاهد مباشرة عند اختبار مشروعك أو تشغيل شيفرة اللعبة ضمن المحرر. أي بإمكانك استخدام نفس الشيفرة في والمشاهد في ألعابك أو بناء إضافات وتوسيع المحرر. ويعطي ذلك مصداقية ومرونة لواجهة المستخدم فهي تدعم المحرر ذاته. فعندما تستخدم التعليمة tool@ ستتمكن من تشغيل شيفرة اللعبة ضمن المحرر. (محرر RPG in a Box هو محرر صمم باستخدام جودو 2 ويستخدم واجهة جودو الرسومية لمنظومته البرمجية المبنية على أساس العقد) ضع التعليمة tool@ في بداية ملف GDScript وستعمل الشيفرة ضمن المحرر. يساعدك ذلك على إدراج وتصدير الإضافات وإنشاء إضافة مخصصة مثل محررات مخصصة للمراحل أو إنشاء سكربتات لها نفس العقد وترتبط بنفس الواجهة البرمجية التي تستخدمها في مشروعك. ملاحظة: كُتب محرر جودو بالكامل باستخدام لغة ++C وصُرّف إلى الصيغة الثنائية. ويعني ذلك أنه من غير الممكن إدراجه كمشروع نمطي على شكل ملف project.godot. محركان منفصلان ثنائي البعد وثلاثي البعد من جودو. يوفّر جودو محركين مخصصين ثنائي وثلاثي البعد. وكنتيجة لذلك ستكون وحدة القياس اﻷساسية للمشهد ثنائي البعد هي البكسل. وعلى الرغم من أن المحركين منفصلين، يمكنك تصيير المشاهد ثنائي البعد في المحرّك ثلاثي البعد والعكس صحيح، كما يمكنك كذلك تضمين الشخصيات والواجهات ثنائية البعد في العالم ثلاثي اﻷبعاد. الخلاصة بهذا تكون قد وصلت لنهاية مقال اليوم الذي ألقينا فيه نظرة عامة عن ميزات محرك الألعاب مفتوح المصدر جودو Godot، ووضحنا أسلوب التصميم المتبع في هذا المحرك وكيفية استخدامه في تطوير الألعاب، وأهم الميزات التي يدعمها مثل التصميم كائني التوجه والوراثة، بالإضافة إلى الأدوات والميزات الحصرية المتوفرة في المحرك، ودعمه لتصميم الألعاب ثنائية وثلاثية الأبعاد مع إمكانية تفاعلهما معًا في تطوير الألعاب، وسلطنا الضوء على الجهود المبذولة من قبل مجتمع مطوري جودو في تطوير المحرك وتحسينه باستمرار لضمان أفضل تجربة استخدام. ترجمة -وبتصرف- لمقال: Godot's design philosophy اقرأ أيضًا تعلم الميزات الجديدة في محرك الألعاب جودو وطرح الأسئلة حوله إعداد محرك الألعاب جودو Godot للعمل مع قاعدة البيانات SQLite تعرف على أهمية صناعة الألعاب الإلكترونية أشهر أنواع الألعاب الإلكترونية
-
يُعد جودو محرّك ألعاب غني بالميزات، وهنالك الكثير لتتعلمه. لهذا سنشرح في هذا المقال كيفية استخدام دليل العمل على اﻹنترنت، والمراجع إلى الشيفرة والانضمام إلى مجتمع جودو لتعلم ميزات وتقنيات جديدة. الاستفادة القصوى من هذا المقال ما سنوضحه في هذا المقال هو طريقة التعامل دليل الاستخدام "user manual" الخاص بمحرك ألعاب جودو والذي يوثّق جميع مفاهيم محرّك الألعاب وميزاته المتاحة. فعندما تتعلم موضوعًا جديدًا، يمكنك البدء في تصفح القسم المخصص لهذا الموضوع في دليل المستخدم هذا. إذ تتيح لك القائمة اليمينية استكشاف مواضيع عامة عن محرك الألعاب، بينما يساعدك شريط البحث على إيجاد صفحات محددة. فإن وجدت صفحة خاصة بالموضوع ستكون مرتبطة غالبًا بصفحات أخرى متعلقة به يترافق هذا الدليل مع مراجع للأصناف "class reference" تشرح لك كل صنف أو دالة أو خاصية متاحة في جودو. فبينما يتحدث الدليل عن الميزات العامة والمفاهيم وكيفية استخدام المحرر، يتحدث مرجع اﻷصناف عن طريقة استخدام الواجهة البرمجية المستخدمة في كتابة سكربتات جودو Godot. وبإمكانك الوصول إلى معلومات هذا المرجع عبر اﻹنترنت أو محليًا. فكي تتمكن من تصفحها محليًا عبر محرر جودو كل ما عليك هو بالانتقال إلى "مساعدة Help" > "البحث في المساعدة Search Help" أو بالضغط على المفتاح F1: أما إن أردت البحث عن طريق اﻹنترنت انتقل إلى قسم مرجع اﻷصناف، وستدلك صفحة المرجع على ما يلي: أين هو موقع الصنف في التسلسل الهرمي، إذ يمكنك النقر على الروابط العليا للانتقال إلى الأصناف اﻵباء والاطلاع على الخاصيات والتوابع الموروثة. ملخص عن وظيفة الصنف وتوضيح حالات استخدامه. شرح خاصيات الصنف وتوابعه و إشاراته و معداته وثوابته. الربط مع صفحات الدليل التي تشرح الصنف أكثر. ملاحظة: إن كان دليل المستخدم أو مرجع الأصناف مفقودًا أو لم يحو على معلومات كافية، يمكنك فتح طلب من مستودع توثيق جودو على جيت هب للإبلاغ عن اﻷمر. بإمكانك النقر مع الضغط على المفتاح Ctrl على أية رابط نصي يمثل اسم صنف أو خاصية أو تابع أو إشارة أو ثابت للانتقال إليه. تعلم التفكير مثل المبرمجين إن موضوع تعلم أساسيات البرمجة وطريقة تفكير مطوري اﻷلعاب خارج سياق توثيق جودو لكنه أمر أساسي لك إن اردت أن تصبح مطور ألعاب . لذا إن كنت جديدًا في عالم البرمجة وكنت مهتمًا بتخصص برمجة وصناعة الألعاب الإلكترونية فننصحك بأن تُلمَّ قبل ذلك بأياسيات البرمجة قبل البدء، ويمكنك الاعتماد على أحد المصدرين التاليين: أكاديمية حسوب التي تقدم لك كمًا كبيرًا من المقالات المتخصصة بتعلم مختلف لغات البرمجة إضافة إلى دورات تدريبية عملية تبدأ بك من الصفر وحتى الاحتراف صممها وقدمها مبرمجون محترفون على دراية كاملة بمتطلباتك وتطلعاتك. موسوعة حسوب التي تعد أكبر مرجع باللغة العربية لتوثيق أشهر لغات البرمجة ومن بينها Python و JavaScript والعديد من اللغات والتقنيات الأخرى. التعلّم من خلال مجتمع جودو يتميز محرّك جودو بمجتمع يتطور ويزداد تعداده باستمرار. فإن واجهتك مشكلة ما وأردت المساعدة في فهم طريقة عمل شيء ما، بإمكانك سؤال مستخدمين آخرين ينتمون إلى أي من مجتمعات جودو النشطة. وأفضل مكان لطرح اﻷسئلة وإيجاد حلول لاسئلة طرحت مسبقًا هو الموقع الرسمي للأسئلة واﻷجوبة ask.godotengine . إذ تظهر الإجابات في نتائج محرّك البحث وتُخزّن ليستفيد اﻵخرون من النقاشات التي تدور في المنصة. وبمجرد أن تطرح سؤالًا، بإمكانك أن تستخدم رابطه لمشاركته في منصات أخرى. لكن قبل أن تطرح سؤالًا، تأكد من وجود أجوبة مسبقة عنه في هذه المنصة أو ابحث عنها باستخدام محرّك البحث الذي تفضلّه. الطريقة الصحيحة لطرح الأسئلة حول محرك جودو إن طرحك للسؤال بشكل جيد وتقديم تفاصيل دقيقة يساعد اﻵخرين في اﻹجابة عن سؤالك بسرعة. وننصحك عند طرح السؤال أن يتضمن المعلومات التالية: وصف الغاية من السؤال: يجب أن تشرح ما الذي تحاول فعله من وجهة نظر تصميمية. فإن لم تتمكن من تصور طريقة لتطبيق الحل، قد تكون هناك حلول أخرى أبسط لتحقيق نفس الغاية. مشاركة رسالة الخطأ كما هي تمامًا: إن كان هناك خطأ. انسخ رسالة الخطأ بدقة من المنقح بالنقر على أيقونة "انسخ الخطأ Copy Error". فمعرفة رسالة الخطأ تساعد أعضاء المجتمع في تحديد السبب الذي أدى لوقوع المشكلة. مشاركة عينة من الشيفرة: إن كان الخطأ في الشيفرة. فلن يتمكن اﻵخرون من مساعدتك في حل المشكلة دون رؤية الشيفرة. لذا شارك الشيفرة على شكل نص مباشرة بنسخ ولصق جزء من الشيفرة في رسالتك أو استخدام مواقع مثل Pastebin لمشاركة الملفات الطويلة. مشاركة لقطة شاشة: لحاوية المشهد في المحرر إضافة إلى شيفرتك. فالشيفرة التي تكتبها تؤثر على عقد عقد المشاهد. إذًا عليك التفكير بالمشاهد كجزء من شيفرتك المصدرية. كذلك لا تستخدم هاتف محمول لالتقاط الصور، فالدقة المنخفضة والانعكاسات قد تُصعّب فهم الصورة. لهذا استخدم الأداة التي يوفّرها نظام التشغيل لديك لالتقاط صورة للشاشة (مثل الزر Print Screen). كما يمكنك استخدام ShareX في ويندوز مثلًا أو FlameShot في لينكس. مشاركة فيديو للعبتك وهي تعمل: أمر مفيد جدّا. يمكنك استخدام برامج مثل OBS Studio أو Screen to GIF لالتقاط فيديو لسطح مكتبك، ثم استخدام خدمات مثل streamable أو مزوّد سحابي لرفع ومشاركة الفيديو. اﻹشارة إلى نسخة جودو التي تستخدمها: وخاصة إن لم تكن النسخة مستقرة stable version. لأن الجواب قد يختلف نتيجة لتغير الميزات والواجهة باستمرار. باتباعك اﻹرشادات السابقة ستزيد فرص حصولك على الجواب الدقيق الذي تبحث عنه، وسيوفر وقتك ووقت اﻷشخاص الذين يساعدونك في حل مشكلتك. مصادر تعليمية حول محرك الألعاب جودو إن كنت تبحث عن دورات حول إنشاء نوع لعبة إلكترونية مثل خطوات إنشاء لعبة تقمص الأدوار "Role-Playing Games" أو غيرها من أنواع الألعاب الإلكترونية، ننصحك بالاطلاع على قسم المصادر والدورات التعليمية Tutorials and resources الذي يعرض محتوى متخصصًا يقدمه مجتمع جودو. وإذا كنت مهتمًا بمعرفة المزيد من التفاصيل عن محرك ألعاب جودو Godot أو لغة برمجة الألعاب GDScript وطريقة تطوير الألعاب ثنائية الأبعاد وثنائية الأبعاد باللغة العربية، فننصحك بالاطلاع على سلسلة المقالات المنشورة في أكاديمية حسوب تحت وسم godot، كما يمكنك الاطلاع على العديد من المقالات المفيدة في قسم مقالات صناعة الألعاب الذي ينشر بصورة دورية العديد من المقالات التي تفيدك كمطور ألعاب. ويمكنك كذلك طرح أي سؤال أو مشكلة تعترضك خلال برمجة لعبتك الخاصة في قسم الأسئلة والأجوبة في الأكاديمية أو في مجتمع حسوب IO. الخلاصة تعرفنا في مقال اليوم على طريقة الاستفادة من الميزات الجديدة التي يوفرها لك محرك الألعاب جودو Godot وأهمية التفكير البرمجي لك كمطور ألعاب وكيفية طرح سؤال حول أي مشكلة تقنية تصادفك خلال صناعة لعبتك بطريقة صحيحة وحلها بسرعة وكفاءة، وأخيرًا ختمنا المقال بمصادر تعليمية مفيدة حول Godot وتطوير الألعاب. ترجمة -وبتصرف- لمقال: Learning new features اقرأ أيضًا دليلك الشامل إلى بناء كاميرا خاصة بشاشات اللمس في محرّك اﻷلعاب جودو تعرف على محرر محرك اﻷلعاب جودو Godot إعداد محرك الألعاب جودو Godot للعمل مع قاعدة البيانات SQLite تشغيل محرك الألعاب جودو على بعض أنواع العتاد غير المدعوم تعرف على أشهر لغات برمجة الألعاب
-
سنعمل في هذا المقال على بناء كاميرا لشاشة لمس في محرك الألعاب جودو كي يتمكن اللاعب من تحريك الكاميرا حول محورها وتكبير وتصغير وتدوير الكاميرا عن طريق اللمس. تهيئة الكاميرا ثنائية البعد افتح محرك الألعاب جودو وأنشئ مشروعك الأول، وابدأ بعقدة من النوع CameraD2، ولجعلها حساسة للمس، سنضيف سكريبت يدير العملية، وذلك بالنقر عليها بالزر الأيمن للفأرة ثم اختيار "إلحاق نص برمجي attach script" ولنسمّه "TouchCameraController.gd". ملاحظة أضفنا أيقونة إلى المشهد لكي نرى الكائن الذي يتحرك. إعداد الكاميرا نحتاج بداية إلى مجموعة من المتغيرات للتحكم بالكاميرا التي تمثلها في جودو العقدة Camera2D. تمثل هذه المتغيرات سرعة الحركة المحورية وسرعة التكبير والتصغير، وسرعة الدوران، وأخرى للتحكم في إمكانية التحريك أو التكبير أو التدوير. وإضافة إلى ذلك، ننشئ متغيرات لتخزين حالة الكاميرا ومدخلات اللمس. إليك طريقة تعريف المتغيرات: extends Camera2D @export var zoom_speed: float = 0.1 @export var pan_speed: float = 1.0 @export var rotation_speed: float = 1.0 @export var can_pan: bool @export var can_zoom: bool @export var can_rotate: bool var start_zoom: Vector2 var start_dist: float var touch_points: Dictionary = {} var start_angle: float var current_angle: float قمنا في البداية بتوسيع الصنف Camera2D في Godot الذي يمثل كاميرا ثنائية الأبعاد 2D في جودو ثم حددنا مجموعة المتغيرات التي يمكن تعديلها وهي: zoom_speed: قيمة عددية تحدد سرعة عملية التكبير والتصغير للكاميرا. pan_speed: قيمة عددية تحدد سرعة عملية التحريك الجانبي للكاميرا. rotation_speed: قيمة عددية تحدد سرعة عملية تدوير الكاميرا. can_pan: قيمة بوليانية تحدد إمكانية التحريك الجانبي للكاميرا. can_zoom: قيمة بوليانية تحدد إمكانية التكبير أو التصغير للكاميرا. can_rotate: قيمة بوليانية تحدد إمكانية تدوير الكاميرا. ثم عرفنا المتغيرات التي ستخزن معلومات عن اللمس وهي: start_zoom لتخزين قيمة بداية عملية التكبير أو التصغير الحالي للكاميرا. start_dist لتخزين المسافة بين نقاط اللمس عند بداية التكبير أو التصغير. touch_points لتخزين مواقع نقاط اللمس على الشاشة لاستخدامها في حسابات التكبير والتدوير. start_angle لتخزين زاوية بداية التدوير لحساب الزاوية المطلوبة للتدوير. current_angle لتخزين الزاوية الحالية لعملية التدوير لتحديث تدوير العنصر المرئي بشكل مستمر. تحريك الكاميرا حول محورها لنبدأ اﻵن بتحريك الكاميرا حول محورها، وهي الحركة اﻷبسط التي تنفذها الكاميرا في المشهد. سنحتاج إلى وسيلة لترصّد بعض أحداث الدخل، لهذا سنتجاوز override الدالة (input(event_ للتحقق من أحداث اللمس touch والسحب drag على الشاشة والاستجابة لها بالشكل المناسب: func _input(event): if event is InputEventScreenTouch: _handle_touch(event) elif event is InputEventScreenDrag: _handle_drag(event) تخزّن الدالة (handle_touch(event_ موقع كل نقطة لُمست على الشاشة ضمن القاموس touch_points وتستخدم دليل اللمس touch index كمفتاح، وهذا أساسي لتتبع المواقع التي تُلمس على الشاشة. كما سنضبط قيمة المتغير start_dist على 0 إن كان عدد النقاط التي لُمست أقل من 2: func _handle_touch(event: InputEventScreenTouch): if event.pressed: touch_points[event.index] = event.position else: touch_points.erase(event.index) if touch_points.size() < 2: start_dist = 0 بإمكاننا تحريك الكاميرا حول محورها بالاستفادة من نقطة لمس واحدة، وذلك باستخدام الدالة (handle_drag(event_ المعرفة كالتالي: func _handle_drag(event: InputEventScreenDrag): touch_points[event.index] = event.position if touch_points.size() == 1 and can_pan: offset -= event.relative * pan_speed إن شغّلت اللعبة على محاكي أندرويد سترى كيف تتحرك الكاميرا: إضافة ميزة التكبير والتصغير سنضيف اﻵن إمكانية التكبير والتصغير (تقريب وإبعاد)، لهذا سنعدّل الدالة (handle_touch(event_ كي نعالج حالة وجود نقطتي لمس والتي سنحسب فيها المسافة اﻷولية بينهما ونخزّنها: func _handle_touch(event: InputEventScreenTouch): if event.pressed: touch_points[event.index] = event.position else: touch_points.erase(event.index) if touch_points.size() == 2: var touch_point_positions = touch_points.values() start_dist = touch_point_positions[0].distance_to(touch_point_positions[1]) start_zoom = zoom start_dist = 0 نضيف بعد ذلك إمكانية التحكم بالتكبير والتصغير إلى الدالة handle_drag(event)_، فعند وجود نقطتي لمس، نحسب المسافة الحالية بينهما وبناء عليها نضبط التكبير والتصغير: func _handle_drag(event: InputEventScreenDrag): touch_points[event.index] = event.position # Handle 1 touch point if touch_points.size() == 1 and can_pan: offset -= event.relative * pan_speed # Handle 2 touch points elif touch_points.size() == 2 and can_zoom: var touch_point_positions = touch_points.values() var current_dist = touch_point_positions[0].distance_to(touch_point_positions[1]) var zoom_factor = start_dist / current_dist zoom = start_zoom / zoom_factor limit_zoom(zoom) # This is about to be created! نستخدم الدالة (limit_zoom(zoom لمنع التكبير أو التصغير من تجاوز الحدود وسيقف التكبير أو التصغير عند حد معين: func limit_zoom(new_zoom: Vector2): if new_zoom.x < 0.1: zoom.x = 0.1 if new_zoom.y < 0.1: zoom.y = 0.1 if new_zoom.x > 10: zoom.x = 10 if new_zoom.y > 10: zoom.y = 10 انقر على زر التشغيل لترى النتيجة: إضافة الدوران لنضف أخيرًا إمكانية تدوير الكاميرا، لهذا سنقيس الزاوية الأولية بين نقطتي اللمس من خلال الدالة handle_touch(event)_: func _handle_touch(event: InputEventScreenTouch): if event.pressed: touch_points[event.index] = event.position else: touch_points.erase(event.index) if touch_points.size() == 2: if touch_points.size() == 2: var touch_point_positions = touch_points.values() start_dist = touch_point_positions[0].distance_to(touch_point_positions[1]) start_angle = get_angle(touch_point_positions[0], touch_point_positions[1]) start_zoom = zoom elif touch_points.size() < 2: start_dist = 0 نضيف بعد ذلك إمكانية التحكم بالدوران ضمن الدالة handle_drag(event)_: func _handle_drag(event: InputEventScreenDrag): touch_points[event.index] = event.position if touch_points.size() == 1: if can_pan: offset -= event.relative * pan_speed elif touch_points.size() == 2: var touch_point_positions = touch_points.values() var current_dist = touch_point_positions[0].distance_to(touch_point_positions[1]) var current_angle = get_angle(touch_point_positions[0], touch_point_positions[1]) #this will be created below var zoom_factor = start_dist / current_dist if can_zoom: zoom = start_zoom / zoom_factor if can_rotate: rotation -= (current_angle - start_angle) * rotation_speed start_angle = current_angle # حدث الزاوية اﻷولية إلى الزاوية الحالية limit_zoom(zoom) تٌستخدم الدالة (get_angle(p1, p2 لحساب الزاوية: func get_angle(p1: Vector2, p2: Vector2) -> float: var delta = p2 - p1 return fmod((atan2(delta.y, delta.x) + PI), (2 * PI)) جرّب تدوير اﻷيقونة اﻵن! السكريبت المكتمل إليك الشيفرة الكاملة: extends Camera2D @export var zoom_speed: float = 0.1 @export var pan_speed: float = 1.0 @export var rotation_speed: float = 1.0 @export var can_pan: bool @export var can_zoom: bool @export var can_rotate: bool var start_zoom: Vector2 var start_dist: float var touch_points: Dictionary = {} var start_angle: float var current_angle: float func _ready(): start_zoom = zoom func _input(event): if event is InputEventScreenTouch: _handle_touch(event) elif event is InputEventScreenDrag: _handle_drag(event) func _handle_touch(event: InputEventScreenTouch): if event.pressed: touch_points[event.index] = event.position else: touch_points.erase(event.index) if touch_points.size() == 2: var touch_point_positions = touch_points.values() start_dist = touch_point_positions[0].distance_to(touch_point_positions[1]) start_angle = get_angle(touch_point_positions[0], touch_point_positions[1]) start_zoom = zoom elif touch_points.size() < 2: start_dist = 0 func _handle_drag(event: InputEventScreenDrag): touch_points[event.index] = event.position if touch_points.size() == 1: if can_pan: offset -= event.relative * pan_speed elif touch_points.size() == 2: var touch_point_positions = touch_points.values() var current_dist = touch_point_positions[0].distance_to(touch_point_positions[1]) var current_angle = get_angle(touch_point_positions[0], touch_point_positions[1]) var zoom_factor = start_dist / current_dist if can_zoom: zoom = start_zoom / zoom_factor if can_rotate: rotation -= (current_angle - start_angle) * rotation_speed start_angle = current_angle # Update the start_angle to the current_angle for the next drag event limit_zoom(zoom) func limit_zoom(new_zoom: Vector2): if new_zoom.x < 0.1: zoom.x = 0.1 if new_zoom.y < 0.1: zoom.y = 0.1 if new_zoom.x > 10: zoom.x = 10 if new_zoom.y > 10: zoom.y = 10 func get_angle(p1: Vector2, p2: Vector2) -> float: var delta = p2 - p1 return fmod((atan2(delta.y, delta.x) + PI), (2 * PI)) الخلاصة صممنا في هذا المقال سكريبت كاميرا تتجاوب مع أفعال اللمس على الشاشة في محرك الألعاب جودو 4، ويمكنها التحرك حول محورها أو التكبير والتصغير أو الدوران. انتبه فقط إلى ضرورة ربط السكريبت بعقدة من النوع Camera2D واستمتع بعملك! ترجمة -وبتصرف- لمقال Building a touchscreen camera in Godot 4: A comprehensive guide اقرأ أيضًا تشغيل محرك الألعاب جودو على بعض أنواع العتاد غير المدعوم تعرف على أشهر محركات الألعاب تعرف على أشهر لغات برمجة الألعاب مدخل إلى محرك الألعاب جودو
-
نتحدث في مقالنا عن أمثل الطرق ﻹعداد وتصدير الأصول أو الموجودات assets من بليندر Blender إلى محرّك اﻷلعاب جودو Godot، ونغطي خلاله العمل مع الخامات texture والتعامل مع قنوات لونية مختلفة، وبعض الإعدادات الشائعة لملفات المشاهد ثلاثية اﻷلعاب gLTF، ثم نتحدث عن فعالية استخدام هذه التنسيق. إعداد الخامة Texture عليك بداية إعداد الخامة texture، لهذا يمكنك الانتقال فورًا إلى التحرير اللوني shading، وقد تلاحظ وجود كائنين. والسبب في ذلك أننا سنستعرض تقنيتين مختلفتين لهما فائدة كبيرة جدًا، وستلاحظ أن معاملات الكائن اﻷول جاهزة وفي مكانها. وفي الفقرات التالية نوفر لك دليلًا سريعَا يوضح الدمج مع بقية الأصول التصميمية في ملف gLTF الذي سنصدّره والذي يستخدم لنقل وتحميل النماذج ثلاثية الأبعاد. القناة Alpha وخريطة Albedo تمثّل Albedo الخريطة اللونية التي يمكن سحبها إلى القاعدة اللونية Base Color. وإن وجدت القناة Alpha، سيتوجب عليك سحبها إلى مكانها. أما خريطة Albedo فهي الخريطة اللونية التي تحتاجها وبإمكانك سحبها إلى القاعدة اللونية. وبالعودة إلى قناة Alpha، وإن كان لديك أية قناة قد هيأتها سابقًا، فعليك وضعها ضمن القسم Alpha. وإن أردتها أن تعمل ضمن محرك اﻷلعاب الذي تريده، انتقل إلى المواد material وبعدها إلى وضع المزج blend mode وغيّرها إلى اقتصاص ألفا Alpha clip. وتذكر أن الطريقة التي تُعد فيها القناة هنا هي نفسها التي ستظهر عندما يتطلب اﻷمر تغيير بعض اﻹعدادات. ستجد في لوحة المواد لديك نمطًا للمزج تصل إليه كالتالي viewport display ثم settings ثم Blend Mode، وسيؤثر ذلك على طريقة عرض محرك اﻷلعاب لخرائطك: واضح Opaque: وهو اﻹعداد الافتراضي، تُرى فيه المواد بشكل كامل ولا يسمح بأي وضع شفاف، أي يتجاهل هذا الخيار أية معلومات تقدمها القناة Alpha ويتعامل مع المواد على أنها ظاهرة تمامًا. اقتصاص ألفا Alpha clip: ويُعرف هذا النمط أيضًا باختبار ألفا Alpha Test أو الاقتصاص Cutout، ويستخدم معلومات قناة Alpha ﻹنشاء فصل ثنائي واضح بين المناطق كاملة الوضوح وكاملة الشفافية. وهذا أمر مثالي للكائنات التي تتمتع بشفافية محددة بدقة مثل الأسوار وأوراق اﻷشجار أو القصاصات الورقية، إذ لا تريد حينها مناطق نصف شفافة. تظليل ألفا Alpha Hashed: يسمح هذا النمط باﻷوضاع نصف الشفافة للمواد، ويعطي أفضل النتائج عند تجميع الكائنات الشفافة فوق بعضها على شكل طبقات. يستخدم نمط التشويش العشوائي dithered pattern لمحاكاة الشفافية، وينتج عنه خامة مشوشة لكنها تعطي توزيعًا أفضل عمومًا للسطوح الشفافة مقارنة بنمط مزج ألفا Alpha Blend. مزج ألفا Alpha Blend: يسمح هذا النمط بالوضع نصف الشفاف الكامل، وهو مثالي لمواد مثل الزجاج أو السوائل، إذ يمزج قناة Alpha مع القناة اللونية ليعطي تمثيلًا دقيقًا للشفافية. مع ذلك قد يواجه هذا النمط مشاكل مع توزيع العمق اللوني وخاصة عند تتداخل عدة كائنات شفافة. المظهر المعدني والخشونة يستخدم مخطط عمل يعتمد على التصيير الفيزيائي Physical Based Rendering ليحاكي المواد الحقيقة في محركات اﻷلعاب ثلاثية اﻷبعاد. الخرائط المعدنية تحدد الخرائط المعدنية Metallic Maps أية أقسام من نموذجك لها مظهر معدني وتلك التي ليس لها هذا المظهر. ففي الخامة ذات التدرجات الرمادية، تعبر القيمة (1.0) للون اﻷبيض أن السطح معدني، بينما تعبر القيمة (0.0) عن سطح غير معدني. وعليك الانتباه إلى أن هذه الخاصية قد تكون ثنائية وغير متدرجة في أغلب اﻷحيان، فإما أن يكون السطح معدنيًا أو غير معدني. لهذا سيكون لهذه الخريطة قيم سوداء تمامًا أو بيضاء تمامًا غالبًا، ونادرًا ما تستخدم قيم تدريجية (رمادية). خرائط الخشونة تتحكم خرائط الخشونة Roughness Maps بتفصيل نعومة أسطح المواد وتحدد مدى الخشونة أو النعومة التي تبديها. ففي هذه اﻷرضية ذات التدرج الرمادي، تشير قيم اللون اﻷبيض إلى سطح خشن، يعكس الضوء باتجاهات مختلفة، معطيًا مظهرًا ليس لامعًا. بينما تشير قيم اللون الأسود إلى سطح ناعم يعكس الضوء باتجاهات محددة أكثر، معطيًا مظهرًا لامعًا. لكن كيف يمكن تصدير كل ذلك؟ كيف تُنفّذ هذه الإعدادات لتصديرها لاحقًا من الجيد فصل اﻷرضيات التي تحجب محيط الكائن وخرائط الخشونة والمظهر المعدني (ORM اختصارًا) وإسنادها إلى القناتين اللونيتين الزرقاء والخضراء وهو طريقة جيدة في حزم اﻷرضيات لجعلها أكثر فعالية. وعندما يكون لديك عدة خرائط متصلة مع خرائط ORM ستُجمّع كلها ضمن خامة ORM واحدة. وتأكد من ضبط خرائط الخشونة والمظهر المعدني والخرائط النمطية normal map على قيم غير لونية كي يكون الفضاء اللوني صحيحًا وبالتالي ستنقل البيانات بشكل صحيح. وتذكر أيضًا أن أية تغييرات تجريها على اﻷرضيات ستُصدّر أيضًا. حجب محيط المادة إن حجب المحيط Ambient Occlusion عملية مزعجة للكثيرين، وخاصة عند تصديرها من بليندر. والحل اﻷبسط هو تحديد عقدة الخامة ومضاعفتها بالضغط على المفتاحين Shift + D، ثم تجميعها بالضغط على المفتاحين CTRL + G والذي يُنشئ مجموعة من اﻷرضيتين في الوسط. احذف الخريطة الموجودة في الوسط: ستجد على اليمين المدخلات inputs والمخرجات outputs ضمن النافذة الفرعية "Group": أزل المدخلات فلن نحتاجها، ثم غيّر اسم الشعاع Vector تحت المدخلات إلى Occlusion وغيّر نوعه إلى float اضغط على Tab وستظهر عقدة مجموعة جديدة: غيّر اسم العقدة إلى "glTF Settings" كما في الصورة التالية تمامًا، فأي خطأ سيمنعه من العمل: صل اﻵن خريطة الحجب إلى عقدة المجموعة "glTF Settings": تصدير الخريطة النمطية الخريطة النمطية normal map هو نمط من اﻷرضيات المستخدمة في النماذج ثلاثية اﻷبعاد لمحاكاة التفاصيل الصغيرة للسطح مثل النتوءات دون الحاجة ﻹضافة مضلعات جديدة في النموذج. تُعد الخريطة النمطية بسيطة، واحرص على عدم استخدام قيم لونية عند ضبطها ثم صلها مع عقدة الخرائط النمطية وبعدها إلى normal. هذه هي الطريقة اﻷبسط لتصديرها. إعدادات إضافية أخرى ستجد في نافذة اﻹعدادات settings الخيار "Backface Culling" (ويعني إخفاء الوجه عند النظر من الخلف)، فإن فعّلته هنا سيُفعّل في محرًك اﻷلعاب الذي تنقل ملف اﻹعدادات إليه. عند تفعيل هذا الخيار، يتحقق محرك الرسوميات من توجّه كل مضلّع، فغن كان مواجهًا للكاميرا فسيُصيّر بشكل اعتيادي، وإن كان معاكسًا لها، سيتجاوزه محرّك الألعاب ولن يصيّره. ولهذا الأمر إيجابيات على اﻷداء، فهو يقلل عدد المضلعات التي يجدر بالمحرّك معالجتها بشكل واضح. ولكي تعمل هذه التقنية جيدًا، ينبغي أن يكون النموذج مغلقًا بالشكل الصحيح، أي لا يحتوي أية ثقوب تسمح بالنظر من خلالها إلى الوجه الخلفي لمضلعات أخرى. فإن فّعلّ خيار إخفاء الوجه عند النظر من الخلف وكان هناك ثقوب، فستظهر بعض أجزاء النموذج وكأنها مفقودة عند النظر من زاوية معينة. ومن المهم الانتباه إلى أن تفعيل هذا الخيار ليس محبذًا النماذج المسطحة والرقيقة مثل اﻷوراق او غيرها من المواد الشفافة أو نصف الشفافة والتي يمكن أن يُرى منها وجهي المضلع، بعد ذلك يمكنك تصدر الكائن بالانتقال إلى File ثم Export GLTF. ما الذي يجب تذكره عند تصدير GLTF؟ ينبغي الانتباه إلى عدة أشياء عند تصدير GLTF، لهذا سنناقش إعدادات تصدير النموذج ثلاثي اﻷبعاد بصيغة GLTF: التنسيق Format: أكثر التنسيقات استخدامًا هو صيغة GLB الثنائية، لأنها تضم النموذج ثلاثي اﻷبعاد واﻷرضيات في ملف واحد. مع ذلك يعطيك تنسيقا (glTF(GLTF + BIN + textures و glTF Embedded تحكمًا أوسع لأنهما يفصلان بين المكونات. ويُعد تنسيق (glTF(GLTF + BIN + textures مفيدُا إن أردت مشاركة اﻷرضيات مع كائنات أخرى. التضمين include: تحدد في هذا القسم أية أجزاء من ملف بلندر ستصدره. فالخيار "خيارات منتقاة Selected Options" يُصدّر فقط النماذج التي اخترتها، وجميع الخيارات في المجموعة الفعّالة حاليًا في حال اخترت "المجموعة الفعالة Active Collection". التحويل transform: إن كنت تواجه مشاكل مع توجّه النموذج في محرّك اﻷلعاب، بإمكانك تغيير المحورين "أعلى Up" و "للأمام Forward". وتذكّر اختلاف التوجّهات في محركات الألعاب المختلفة. تصدير المواد Material Export: عليك التأكد من تفعيل هذا الخيار. الصور Images: بإمكانك اختيار صيغة الصور التي تريدها من هذا القسم. هندسة النموذج Geometry: يتضمن هذا القسم خيارات التصدير المتعلقة بهندسة النموذج، فإذا كان نموذجك يستخدم ألوان Vertex فعّل الخيار المتعلق به وإلا فقم بتعطيله، اما خيار Apply Modifiers فهو يطبق جميع المعدلات على النموذج الخاص بك قبل التصدير. التحريك Animation: يساعدك هذا القسم في تصدير الرسوم المتحركة، وبإمكانك تصدير الرسوم المتحركة النمطية normal animations ومفاتيح الشكل shape keys ورسوم اﻷغلفة skinning كي تتحكم بها في محرّك اﻷلعاب الذي تريد. إدراج ملف glTF في محرك اﻷلعاب جودو عند اكتمال تصدير الملف، افتح محرّك اﻷلعاب جودو واستورد هذا الملف. وتأكد أن الصور المضمّنة في تنسيق glTf Embedded قد ضُبطت على "استخراج اﻷرضيات". ومن المفترض أن يتلائم الملف المصدّر جيدًا. في حالتنا الموضحة بالصور التالية، اخترنا اقتصاص ألفا Alpha clip عند التصدير، لكنه ظهر في محرك ألعاب جودو على شكل مقص ألفا Alpha Scissor. لاحظ كيف ظهرت الخرائط بالشكل الصحيح في جودو! وقد تلاحظ كيف تبدو غريبة ومليئة بألوان زاهية، يُدعى هذا اﻷمر خامة ORM أي أن اﻷرضيات قد حُزّمت ضمن خرائط مختلفة وأقنية مختلفة للخامة مما يجعل هذه الخرائط أكثر فعالية. وقد تلاحظ أيضًا ظهور قسم الانبعاثات Emision والخريطة النمطية Normal Map أيضًا. الخلاصة باتباعك الخطوات التي شرحناها في هذا المقال، ستتمكن من تصدير عناصر أو أصول الألعاب الخاصة بك بفعالية من بليندر إلى جودو Godot. لا تتردد في ترك أية تساؤلات أو اقتراحات في قسم التعليقات أسفل الصفحة لتعم الفائدة لجميع المهتمين بتصميم الألعاب الإلكترونية. ترجمة -وبتصرف- لمقال: How to Efficiently export Assets from Blender to your game engine اقرأ أيضًا مطور الألعاب: من هو وما هي مهامه بلندر للمبتدئين: كيف تصمّم وتحرّك روبوت - نمذجة الجسم والرأس كيف تصمّم وتحرّك روبوت: خامات بلندر ومفاتيح الشكل إنشاء مجسم ثلاثي الأبعاد لشجرة باستخدام برنامج بلندر
-
نتحدث في هذا المقال عن استخدام محرر قواعد البيانات SQLite للعمل مع جودو، ونعرض بداية اﻷسباب التي تدفع مطوري اﻷلعاب للعمل مع محرر قواعد بيانات أصلًا، ونتعرف بعدها على محرر قواعد البيانات SQLite والذي يُعد قاعدة بيانات مضمّنة صغيرة تسمح للمستخدم تخزين البيانات والاستعلامات ضمنها. لكن لماذا على استخدام قاعدة بيانات هذه؟ إن الميزة المفيدة لهذه القاعدة هو قدرتها على استيعاب كل البيانات في منطقة واحدة، واﻷهم من كل ذلك هو القدرة على الحصول على البيانات بأبسط طريقة. حتى تبدأ العمل معنا في هذا المقال، افتح محرك الألعاب جودو وأنشئ مشروعًا جديدًا. إعداد قاعدة البيانات إذا كنت مطور ألعاب إلكترونية فقد تحتاج لاستخدام قواعد البيانات في لعبتك من أجل تخزين بيانات اللاعبين وحفظ المستوى أو النقاط التي حصلوا عليها، ولتجهيز قاعدة البيانات SQLite التي سنستخدمها في هذا المقال، حمّل متصفح قواعد البيانات DB Browser for SQLite الذي يساعدك في إدارة قاعدة البيانات من خلال واجهة رسومية يمكنك من خلالها إنشاء قاعدة البيانات وتحريرها بسهولة. سنبدأ بإنشاء قاعدة بيانات جديدة، لهذا افتح متصفح قاعدة البيانات وانقر على النافذة "قاعدة بيانات جديدة New Database". وانتقل بعد ذلك إلى المجلد الذي يضم مشروع جودو الذي أنشأته سابقًا وأنشئ ضمنه مجلدًا جديدًا يحمل الاسم "datastore"، فلنختر الاسم "data" لقاعدة بياناتنا الجديدة. تظهر بعد ذلك نافذة "تحرير تعريف الجدول Edit table definition"، اختر عندها الاسم "Scoreboard" لهذا الجدول وسنخزن في هذا الجدول نتيجة كل لاعب أو عدد النقاط التي حصل عليها في اللعبة. لنبدأ بإنشاء أول اﻷعمدة "id" بالنقر على الزر "Add"، وستكون عناصره من النوع الصحيح INTEGER وانقر على الخيارات "غير فارغ NN" و"تزايد تلقائي AI"، مما يسمح له بأن يكون مرجعًا لنا للحصول على البيانات من الجدول أو إلحاق بيانات أخرى. أنشئ تاليًا حقلًا field نصيًا TEXT وسمه "name" وذلك لتخزين اسم المستخدم الذي نريد معرفة نتيجته. ويستخدم النوع السابق لتخزين أكثر من ا تيرابايت من البيانات النصية، فانتبه إلى ذلك! ثم أنشئ أخيرًا حقل النتيجة "score" واجعله من النوع الصحيح INTEGER. انقر اﻵن على الزر "موافق OK" وانتقل إلى مشاهدة قاعدة البيانات من خلال النقر على "تصفح البيانات Browse Data"، وسترى جدولك وقد ظهر. بإمكانك اﻵن من هذه النافذة أن تدخل البيانات يدويًا إن أردت، وذلك بالنقر على أيقونة اﻹضافة ومن ثم إدخال ما تريد. إن انتقلنا إلى "تنفيذ استعلام execute SQL"سترى المكان الذي تنفذ فيه الاستعلامات التي نريد على قاعدة البيانات. سنجرّب الاستعلامات بإدخال بعض البيانات، لهذا اكتب الاستعلام التالي: insert into scoreboard (name,score) values ('nori',50),('finepointcgi',20) عند تنفيذ هذا الاستعلام على النحو التالي: سترى النتيجة التالية: وعند تنفيذ الاستعلام Select * from scoreboard، سترى نتيجة عملك. ولننتقل اﻵن إلى محرك الألعاب جودو من أجل استكمال عملية ربطه مع قاعدة البيانات SQLite. تنزيل اﻹضافة الخاصة بقاعدة البيانات SQlite لننتقل اﻵن إلى جودو ثم نفتح النافذة الفرعية "مكتبة الأصول أو مكتبة الموجودات Asset library" التي تمثل مستودعًا لأي أدوات أو موارد إضافية تريد تضمينها في اللعبة، ثم اكتب sqlite واختر الإضافة التي تملك الرقم 2shady4u فهي إضافة جيدة وذات أداء مستقر. جرّب بعد ذلك تثبيتها وانتظر لحين اكتمال التنزيل. سنضيف تاليًا عقدة، لهذا سننشئ سكريبت ونسمّيه "MainSystem.gd" وسيحتوي هذا الملف على كافة السكريبتات والتعليمات التي تتفاعل مع البيانات، لذا علينا تحميل أن نقوم بتحميل قاعدة البيانات في بدايته، وكي تتمكن من القيام بالتحميل المسبق للبيانات عليك استخدام الدالة preload مع تمرير العنوان التالي لوسيط لهذه الدالة: res://addons/godot-sqlite/bin/gdsqlite.gdns إدخال بيانات إلى قاعدة البيانات الآن انسخ الشيفرة التالية إلى ملف السكريبت الذي أنشأته في الخطوة السابقة: const SQLite = preload("res://addons/godot-sqlite/bin/gdsqlite.gdns") var db var db_name = "res://DataStore/database" func _ready(): db = SQLite.new() db.path = db_name func commitDataToDB(): db.open_db() var tableName = "PlayerInfo" var dict : Dictionary = Dictionary() dict["Name"] = "this is a test user" dict["Score"] = 5000 db.insert_row(tableName,dict) ننشئ في الكود أعلاه المتغيرين db و db_name بعد تحميل قاعدة البيانات، ونسند للثاني عنوان قاعدة البيانات "res://DataStore/database". ومن أجل تهيئة قاعدة البيانات، نستدعي التابع ()SQLite.new داخل الدالة ()ready ونسند قيمته إلى المتغير db، وسيُهيئ ذلك صنفًا لإعداد قاعدة البيانات. نحتاج بعد ذلك إلى ضبط مسار قاعدة البيانات كالتالي db.path= db_name ونضيف بعدها دالة جديدة تُدعى ()commiteDataToDB سنستخدمها ﻹيصال البيانات إلى قاعدة البيانات. ننشئ ضمن الدالة ()commiteDataToDB قناة اتصال مع قاعدة البيانات لهذا نستدعي التابع ()db.open_db الذي يهيئ قناة الاتصال مع قاعدة البيانات ويسمح لنا بتنفيذ استعلامات عليها. ولاختيار اسمٍ للجدول، نعرّف متغيرًا بالاسم tablename ونضبطه قيمته على "PlayerInfo". ثم ننشئ قاموسًا dict يحدد المعلومات التي نريد إدراجها في قاعدة البيانات، ويكون مفتاح القاموس بمثابة العمود والقيمة بمثابة الصف عند إضافة معلومات القاموس إلى قاعدة البيانات من خلال التعليمة INSERT. نستدعي أخيرًا الدالة (db.insert_row(tableName,dict ﻹضافة القاموس (الذي يمثل المُدخل الجديد) إلى قاعدة البيانات. قراءة البيانات من قاعدة البيانات كما هو الحال عند الكتابة إلى قاعدة البيانات، لا بد من فتح قناة اتصال مع قاعدة البيانات واختيار الجدول الذي نريد القراءة منه. ثم ننشئ بعد ذلك الاستعلام الذي نريد باستدعاء التابع ()db.query وكتابة تعليمات الاستعلام ضمنه كمعامل نصي. وبمجرد استدعاء هذا التابع سيتصل مع قاعدة البيانات وينفذ الاستعلام ويخزن النتيجة ضمن المصفوفة query_result. لا بد بعد ذلك من التنقل بين عناصر المصفوفة من العنصر 0 وصولًا إلى آخر عنصر الذي تمثله الخاصية size للمصفوفة query_result للحصول على الكائنات الموجودة في قاعدة البيانات، ومن ثم طباعة النتيجة. func readFromDB(): db.open_db() var tableName = "PlayerInfo" db.query("select * from " + tableName + ";") for i in range(0, db.query_result.size()): print("Qurey results ", db.query_result[i]["Name"], db.query_result[i]["Score"]) الخلاصة هذا هو اﻷمر برمّته! فقد تعلمنا في مقال اليوم طريقة إضافة البيانات إلى قاعدة بيانات ومن ثم تعاملنا معها. ولعللك لاحظت أن استخدام قواعد البيانات أسهل وأسرع من استخدام الملفات لتخزين البيانات في لعبتك الإلكترونية. وتسمح لك باسترجاع البيانات التي تحتاجها فقط بدلًا من قراءة الكثير من الملفات. وعندما ترى أن بياناتك أضخم من عدة ملفات، من الجيد عندها الانتقال إلى قاعدة بيانات. ترجمة وبتصرف للمقال: Setting up Godot to work with SQLite اقرأ أيضًا تعرف على أهمية صناعة الألعاب الإلكترونية مدخل إلى محرك الألعاب جودو تعرف على محرر محرك اﻷلعاب جودو Godot كيف ومتى نستخدم SQLite
-
نتحدث في هذا المقال عن بعض المشاكل التي يتعرض لها مستخدمو محرك الألعاب جودو Godot 4 عند محاولة العمل على عتاد لا يدعمه محرّك اﻷلعاب. فإن كنت تعمل على أجهزة حواسيب مصنّعة قبل عام 2013، فقد لا تملك برامج تشغيل العتاد Vulkan حيث تدعم هذه الأجهزة OpenGL 3.0 فقط من أجل عرض الرسومات ثلاثية الأبعاد على حاسوبك. وبالتالي إذا حاولت تشغيل محرك الألعاب جودو 4 ضمن بيئة عمل وهمية VirtualBox، ففي هذه الحالة قد تصادفك بعض العقبات. وسنوضح لك في هذا المقال طريقة للالتفاف على تلك المشاكل والعقبات وسبل حلها، فإذا كانت لديك أي مشكلات في التعامل مع إقلاع محرك ألعاب جودو 4 على جهازك تابع معنا قراءة هذا المقال. أين تكمن المشكلة في جودو 4؟ لنفترض أن ثبّت محرك ألعاب جودو 4 وأنت متحمس بالفعل لبدء العمل على صناعة لعبتك الإلكترونية، ثم نقرت نقرة مزدوجة على أيقونة البرنامج، وفوجئت بطهور رسالة خطأ تخبرك بفشل تهيئة برنامج تشغيل بطاقة الفيديو لديك لأنها لا تدعم Vulkan أو OpenGL كما هو موضح في الصورة التالية: لحل هذه المشكلة أمامك خيارات سنشرحهمها في الفقرات التالية. تحديث برامج تشغيل العتاد أو الالتفاف على الموضوع لديك اﻵن خياران، الأول هو أن تحدّث برنامج تشغيل بطاقة الفيديو لديك من خلال التوجه إلى موقع الويب الخاص بمصنع بطاقة العرض لديك، ومن ثم تنزيل وتثبيت آخر إصدار لهذه البطاقة. وفي حال لم يكن الخيار الأول متاحًا لديك، يمكن في هذه الحالة فتح محرر سطر اﻷوامر Command prompt أو الطرفية على جهازك ثم تكتب الأمر Godot، بعدها ابحث عن الملف التنفيذي للمحرك جودو، ثم شغله بعد إضافة الوسيط التالي لأمر التشغيل كما يلي: >Godot_v4.2.1-beta3_mono_win64.exe --rendering-drive opengl3 كما هو هو مبين في الشاشة التالية: ستعمل هذه الحيلة في أغلب اﻷحيان وسيُحمّل برنامج جودو ويعمل بشكل صحيح على جهازك. حل مشكلة النقر على زر التشغيل Play قد يبدو كل شيء على ما يرام حتى الآن، لكن قد تفاجئ بعد أن تنقر على زر التشغيل، بظهور رسالة الخطأ نفسها من جديد. لا تقلق يمكنك أيضًا إصلاح هذا الأمر من خلال الانتقال إلى القائمة مشروع Project ثم اختيار إعدادات المشروع Project Settings وبعدها ثم بتفعيل الخيار إعدادات متقدمة Advanced Settings كما في الصورة التالية. بعد أن تظهر لك النافذة أعلاه انتقل ذلك إلى قسم المحرر Editor ثم اختر الخيار تشغيل Run ثم اجعل قيمة المتغير Main Run Arguments كما يلي: rendering-drive opengl3-- وانقر بعد ذلك على زر إغلاق Close ومن المفترض أن يعمل زر التشغيل بشكل صحيح ودون أي مشكلات. خطوات إضافية للعمل على VitrualBox أو على جهاز محدد إن كنت تعمل على الجهاز الافتراضي VirtualBox، أو لم تنجح الخطوات السابقة لحل المشكلة، عليك في هذه الحالة اتباع بعض الخطوات الإضافية. انتقل إلى المتصفح وحمّل برامج تشغيل العتاد Mesa dist Windows في حال كان نظام تشغيل حاسوبك هو ويندوز Windows، أما إن كنت تعمل على إحدى توزيعات نظام التشغيل لينكس Linux فلن تحتاج إلى ذلك. حمّل اﻹصدار MingW من برنامج تشغيل العتاد Mesa وبرنامج ضغط الملفات 7-Zip الذي سوف تحتاجه لاستخراج ملفات برنامج تشغيل العتاد. عند اكتمال عملية التنزيل انتقل إلى مجلّد تنزيلات downloads، ثم استخرج الملفات باستخدام البرنامج 7-zip ثم افتح المجلد الناتج وشغّل الملف System Wide Deploy الذي سيخبرك بأنه سيثبّت برامج تشغيل العتاد OpenGL الخاص بالحواسب المكتبية. اختبار برامج تشغيل العتاد عليك تاليًا اختبار كلا برنامجي تشغيل العتاد OpenGL الخاص بسطح المكتب، ومايكروسوفت OpenGL على DirectX 3D12. سيعمل برامج تشغيل العتاد الأول على المعالج المركزي CPU وسيحاول الثاني العمل على المعالج الرسومي GPU. تشغيل جودو 4 عد مجددًا إلى محرر سطر اﻷوامر واكتب الأمر التالي: Godot –rendering-driver opengl3 ويجب أن يعمل جودو كما هو مطلوب، وتذكر أن ما فعلناه هنا هو الالتفاف حول المشكلة، فلا تتوقع أداءً استثنائيًا! لكن على اﻷقل ستشغّل محرك ألعاب جودو 4 بصورة صحيحة. أما إن كنت تعمل على توزيعة لينكس منت Mint، فلن تحتاج لعملية الالتفاف تلك، وسيعمل جودو حتى على العتاد غير المدعوم لأن برنامج تشغيل العتاد Mesa مضمّن معه. الخلاصة قد يكون تشغيل محرك الألعاب جودو 4 على عتاد لا يدعمه أمرًا صعبًا على مطور الألعاب الإلكترونية، لكن حل هذه المشكلة ليس مستحيلًا! كل ما عليك هو تجربة الخطوات التي شرحناها في المقال وتستأنف العمل على جودو، وإن واجهتك أي مشكلة في التعامل مع محرك الألعاب جودو لا تتردد في طرحها في قسم التعليقات أسفل المقال لنساعدك على حلها. ترجمة -وبتصرف- للمقال Making Godot 4 work on (some)unsupported hardware اقرأ أيضّا مدخل إلى محرك الألعاب جودو Godot تعرف على محرر محرك اﻷلعاب جودو Godot تعرف على أشهر محركات الألعاب Game Engines
-
نعرض في هذا المقال لمحة موجزة عن واجهة محرك الألعاب جودو Godot، ونلقي نظرة على شاشاتها المختلفة وقوائمها كي تعرف كيف ترتب الواجهة بما يلائمك. مدير المشروع وهي أول نافذة تظهر عندما تُشغّل جودو Godot، وتضم نافذتين فرعيتين أولهما "المشاريع الخاصة Local projects" التي تتيح لك إدارة المشاريع الموجودة على حاسوبك وإدراج أو إنشاء مشاريع جديدة وغيرها. أما النافذة الأخرى فهي "مشاريع مكتبة الملحقات Asset Library Projects" التي تساعدك على البحث عن مشاريع تجريبية في مكتبة الموجودات مفتوحة المصدر وتضم الكثير من المشاريع التي يطوّرها مجتمع جودو. تستطيع أيضًا تغيير لغة المحرر الافتراضية (اﻹنكليزية) من خلال النقر على زر القائمة المنسدلة أعلى ويمين نافذة المحرك. نظرة أولى إلى محرر جودو عندما تفتح مشروعًا موجودًا أو تنشئ مشروعًا جديدًا، سيعرض لك المحرّك نافذة المحرر كما في لقطة الشاشة التالية: تتكون النافذة من قوائم وشاشات رئيسية وأزرار للتحكم أعلى النافذة: ستجد في منتصف الشاشة نافذة العرض مع شريط أدواتها في الأعلى، وستجد ضمن الشريط أدوات لتحريك أو تحجيم أو قفل عقد المشهد. وستجد على طرفي نافذة العرض حاويات docks وفي أسفلها لوحة أدوات سفلية. يتغيّر شريط اﻷدوات وفقًا لسياق العمل والعقدة المختارة. إليك لقطة شاشة شريط أدوات مشهد ثنائي البعد: وشريط أدوات مشهد ثلاثي اﻷبعاد: لنلق نظرة اﻵن إلى الحاويات وأولها الحاوية "نظام الملفات FileSystem" التي تعرض قائمة بملفات المشروع بما في ذلك السكربتات والصورة ومقاطع الصوت وغيرها: أما الحاوية الثانية فهي "المشهد Scene" التي تعرض قائمة بالعقد الفعّالة: وتتيح لك حاوبة "الفاحص Inspector" تعديل خصائص العقدة المختارة: تظهر لوحة اﻷدوات السفلية تحت نافذة العرض وتضم طرفية التنقيح debug console ومحرر الرسوم المتحركة والمازج الصوتي وغيرهم من اﻷدوات. تشغل اللوحة مساحة لا بأس بها لهذا تكون مخفية افتراضيًا. عندما تنقر على أي عنصر من اللوحة تتمدد شاقوليًا. إليك لقطة شاشة للوحة وهي تعرض المحرر الرسومي: الشاشات الرئيسية اﻷربعة هناك أربعة أزرار للشاشات الرئيسية أعلى المحرر وهي "الثنائي 2D" و "الثلاثي 3D" و "Script" و "AssetLib". الشاشة 2D ستستخدم الشاشة ثنائية البعد 2D لمختلف أنواع الألعاب، فإضافة إلى اﻷلعاب ثنائية اﻷبعاد ستستخدم هذه الشاشة لبناء الواجهات. الشاشة 3D تتعامل في النافذة ثلاثية اﻷبعاد مع الشبكات واﻷضواء ومراحل تصميم اﻷلعاب ثلاثية الأبعاد: ملاحظة: بالنقر على الزر "perspective" تحت شريط اﻷدوات سيفتح لك قائمة بخيارات تتعلق بالمشهد ثلاثي اﻷبعاد. الشاشة Script تعرض هذه الشاشة محرر الشيفرة المتكامل والمزود بمنقح وآلية ﻹكمال الشيفرة ومراجع مدمجة إلى الشيفرة: الشاشة AssetLib تضم الشاشة مكتبة من الإضافات مفتوحة المصدر والسكربتات وغيرها من الموجودات التي تساعد في مشروعك: مراجع مدمجة إلى اﻷصناف يزوّدك جودو بمراجع مدمجة إلى اﻷصناف وتوابعها وخاصياتها وثوابتها وإشاراتها ويساعدك في البحث عنها بسهولة بإحدى الطرق التالية: الضغط على زر F1 (أو Alt+Space في ماك أو fn+F1 في الحواسب المحمولة التي تحتوي الزر fn). النقر على الزر "البحث في المساعدة Search help" في أعلى ويمين شاشة السكربت الرئيسية. النقر على قائمة "مساعدة Help" ثم "البحث في المساعدة Search Help". النقر على اسم الصنف أو الدالة او المتغير المدمج مع الضغط على الزر Ctrl. عند استخدام أية طريقة من الطرق السابقة تنبثق نافذة اكتب فيها ما تريد البحث عنه، كما يمكنك استخدامها لتصفح الكائنات والتوابع المتاحة. انقر نقرة مضاعفة على العنصر لفتح الصفحة المتعلقة به ضمن نافذة السكربت. ترجمة -وبتصرف-للمقال First look at Godot's editor. اقرأ أيضًا مدخل إلى محرك الألعاب جودو Godot مدخل إلى صناعة ألعاب المتصفح برمجة لعبة متاهة باستخدام محرك يونيتي Unity
-
يساعدك هذا المقال على تقدير إذا ما كان محرّك الألعاب جودو Godot ملائمًا لاحتياجاتك، إذ سنقدم لك بعض الميزات العامة للمحرك كي نعرض ما يمكنك إنجازه، واﻹجابة عن أسئلة مثل " مالذي عليّ معرفته كي أبدأ العمل؟". لن تكون هذه المقدمة شاملة، لكننا سنقدّم الكثير من المفاهيم العامة في سلسلة المقالات المتتالية. ما هو محرك الألعاب جودو Godot جودو Godot هو محرك ألعاب ثنائية وثلاثية أبعاد عام اﻷغراض يدعم مختلف أنواع المشاريع، يساعدك في بناء اﻷلعاب والتطبيقات التي يمكن نشرها على الحواسب أو أجهزة الهاتف المحمول وعلى الويب أيضًا. يمكنك أيضًا بناء ألعاب مخصصة لمنصات الألعاب console games لكن ذلك يتطلب مهارات برمجية عالية أو الاستعانة بمطوّر ليساعدك على ذلك. ملاحظة: لا يمكن أن يقدّم لك مطورو جودو وسيلة مفتوحة المصدر لنقل اللعبة إلى منصات اﻷلعاب نظرًا لشروط الترخيص التي يضعها صانعو تلك المنصات. وبصرف النظر عن المحرّك الذي تستخدمه، سيكون إصدار نسخة من اللعبة على منصة أمرًا مجهدًا. ما الذي يمكن لمحرك جودو فعله؟ طوّر محرّك جودو أصلًا في المنزل من قبل استدوديو ألعاب أرجنتيني، وقد بدأ العمل عليه عام 2001 ثم حُسِّن كثيرًا منذ ذلك الحين وحتى إطلاق النسخة مفتوحة المصدر منه عام 2014. من اﻷلعاب التي طوّرت باستخدام جودو نذكر "Ex-Zodiac" و "Helms of Fury": ومن التطبيقات التي تعتمد عليه نجد برنامج الرسم النقطي مفتوح المصدر "Pixelorama" وكذلك "voxel RPG creator": كيف يعمل محرك جودو وكيف يبدو؟ يأتي المحرّك مع محرر ألعاب كامل الميزات وأدوات أصلية مدمجة معه لتقدم حلولًا لمعظم المهام الشائعة.من هذه اﻷدوات محرر شيفرة ومحرر رسوم متحركة ومحرر خرائط tilemap editor ومحرر المعالج اللوني shader ومنقح debugger ومحلل أداء Profiler وغيرها. يجاهد الفريق لتقديم محرر ألعاب غني بالميزات مع تجربة مستخدم تتطور باستمرار، وطالما أن هناك دائمًا ما يمكن تحسينه، سيستمر تطوير واجهة المحرّك. بإمكانك أيضًا العمل على برمجيات خارجية أخرى إن أردت، إذ يقدّم المحرك دعمًا رسميًا للمشاهد ثلاثية اﻷبعاد المصممة على بلندر Blender والإضافات الخاصة ببرنامج فيجوال ستديو كود و إيماكس Emacs لدعم البرمجة باستخدام GDScript و #C. كما يدعم المحرّك أيضًا فيجيوال ستوديو ولغة #C على ويندوز. لغات البرمجة في جودو بإمكانك استخدام لغة GDScript وهي لغة برمجة عالية التكامل خاصة بمحرك جودو وتتمتع بصياغة سهلة، أو يمكن استخدام لغة #C الشائعة الاستخدام في عالم الألعاب. هاتان اللغتان هما اللغتان الرئيسيتان المدعومتان من قبل محرك جودو. لكن باستخدام اﻹضافات عبر تقنية GDExtension، ستتمكن من كتابة ألعاب أو خوارزميات باستخدام لغة C أو لغة ++C دون إعادة تصريف المحرّك. وتستطيع استخدام هذه التقنية أيضًا في دمج مكتبات يؤمنها طرف آخر وغيرها من أدوات تطوير البرمجيات SDK مع المحرّك. كما يمكنك إضافة وحدات برمجية جاهزة وميزات إلى المحرّك وهي مجانية ومفتوحة المصدر. ما الذي علي معرفته لاستخدام محرك جودو؟ يُعد محرّك جودو محركًا مبنيًا على الكثير من الميزات، ومع وجود اﻵلاف منها ستجد أن هنالك الكثير لتتعلمه. وللاستفادة من كامل إمكانياته، لا بد أن تمتلك خلفية جيدة في البرمجة. ومع أننا نحاول جعل المحرّك سهل الاستخدام، سيفيدك تعلّم التفكير مثل المبرمجين. ويعتمد المحرّك على البرمجة كائنية التوجه، لهذا ستساعدك معرفة بعض المفاهيم مثل الأصناف والكائنات في كتابة شيفرة أكثر فعالية. إما إذا كنت جديدًا في عالم البرمجة، ننصحك بالاطلاع على دورات تعلم البرمجة التي تقدمها أكاديمية حسوب والتي تأخذك خطوة بخطوة إلى طريق احتراف البرمجة. المفاهيم الرئيسية لمحرك اﻷلعاب جودو يتمحور عمل أي محرك ألعاب حول مجموعة من المفاهيم اﻷساسية التي تٌستخدم لبناء التطبيقات. وفي جودو، تُمثّل اللعبة على شكل شجرة tree مكوّنة من عقد nodes تتجمع مع بعضها لتشكل مشهدًا scene، وترتبط هذه العقد ببعضها كي تكون قادر على التخاطب فيما بينها باستخدام إشارات signals. سنلقي نظرة سريعة في هذا المقال على المفاهيم اﻷربعة السابقة ونتعلم طبيعة عمل المحرك. المَشاهد scenes تُجزّأ اللعبة في جودو إلى مشاهد يمكن استخدامها بشكل متكرر، وقد يكون المشهد شخصية أو سلاحًا أو قائمة ضمن واجهة المستخدم أو بيت أو مستوى كامل للعبة وعمومًا أي شيء قد تفكّر به. تتمتع مشاهد جودو بالمرونة، إذ تؤدي دور الكائنات الجاهزة prefab والمشاهد في محركات ألعاب أخرى. بإمكانك أيضًا إنشاء مشاهد متداخلة، كأن تضع شخصية ضمن مرحلة عن طريق سحب وافلات مشهد ضمن آخر ليكون ابنًا له. العقد nodes يتكون المشهد من عقدة أو أكثر وتمثّل هذه العقد أصغر الكتل البنائية التي ترتبها في الشجرة. إليك مثالًا عن عقدة لشخصية في لعبة: تتكون الشخصية من عقدة نوعها CharacterBody2D وتُدعى "Player" وضمنها ثلاث عقد أبناء هي Camera2D و Sprite2D و CollisionShape2D. ملاحظة: تنتهي أسماء العقد السابقة باللاحقة "2D" لأن المشهد ثنائي البعد. وتنتهي أسماء العقد في المشاهد ثلاثة اﻷلعاب باللاحقة "3D". وانتبه إلى أن العقد من النوع "الفراغي Spatial" لم تُعد تُسمّى "Node3D" ابتداءًا من الإصدار 4 لجودو. لاحظ كيف تبدو العقد والمشاهد متطابقة في المحرر، فعندما تُخزّن شجرة عقد على أنها مشهد، سيعرض المحرر عقدة واحدة ويخفي بنيتها الداخلية ضمنه. يقدّم جودو مكتبة قابلة للتوسّعة تضم أنواعًا مختلفة من العقد التي يمكن دمجها أو توسعتها لبناء عقد أقوى. فكل ما ستبنيه وتتعامل معه سواء في التصاميم ثنائية أو ثلاثية اﻷبعاد هي العقد. شجرة المشهد scene tree تتجمع كل مشاهد اللعبة في شجرة من المشاهد أو (شجرة مشهد scene tree)، وشجرة المشاهد هي شجرة من العقد لكن من اﻷسهل تخيّل اللعبة كمشاهد لأنها تمثّل الشخصيات واﻷسلحة واﻷبواب والواجهات بطريقة أوضح. اﻹشارات signals ترسل العقد إشارات عندما تقع بعض الأحداث، وتساعدك هذه الميزة في تأسيس اتصالات بين العقد دون أن تفعل ذلك يدويًا ضمن الشيفرة، مما يمنحك مرونة في هيكلة المشاهد. ملاحظة: تُعد اﻹشارات نسخة جودو من من نمط "المراقب observer" في محركات أخرى. تُرسل اﻷزرار إشارات عند النقر عليها مثلًا، وبالتالي يمكنك الاتصال بهذه اﻹشارة لتشغيل شيفرة محددة كاستجابة لحدث معين، كبدء اللعبة أو عرض قائمة. وقد تخبرك إشارات أخرى مدمجة عن تصادم كائنين أو عند دخول شخصية منطقة محددة وغيرها الكثير. وبإمكانك تعريف إشارات جديدة مخصصة للعبتك. خلاصة تعرفنا في هذا المقال على المفاهيم البنيوية اﻷريعة لمحرك اﻷلعاب جودو والعقد والمشاهد وشجرة المشاهد واﻹشارات وهي ما ستتعامل معه دائمًا. فالعقد هي الكتل البنائية اﻷصغر في اللعبة والتي تجمع بينها لتشكل مشهدًا ثم تجمع المشاهد لتشكل شجرة مشاهد. ثم تستخدم بعد ذلك اﻹشارات كي تتفاعل العقد مع اﻷحداث التي تقع في عقد أخرى أو فروع أخرى من شجرة المشاهد. قد تخطر في بالك اﻵن الكثير من اﻷسئلة، لهذا نطلب منك التروي ومتابعة ما ستعرضه في مقالات أخرى لتحصل على الكثير من اﻷجوبة. ترجمة -وبتصرف- للمقال Introduction to Godot والمقال Overview of Godot's key concepts. اقرأ أيضًا أشهر أنواع الألعاب الإلكترونية مطور الألعاب: من هو وما هي مهامه أشهر لغات برمجة الألعاب