يتطور محرك Godot بسرعة كبيرة ويكسب قلوب مطوري الألعاب من جميع أنحاء العالم. ربما هذه هي الأداة الأكثر سهولة وسهولة للتعلم لإنشاء الألعاب ، وللتأكد من ذلك ، حاول إنشاء لعبة صغيرة ثنائية الأبعاد. لفهم جيد لعملية تطوير اللعبة ، يجب أن تبدأ بألعاب ثنائية الأبعاد - سيؤدي هذا إلى خفض عتبة الدخول إلى نظام لعبة أكثر جدية. على الرغم من أن الانتقال إلى 3D في حد ذاته ليس صعبًا كما قد يبدو ، بعد كل شيء ، يمكن استخدام معظم الوظائف في Godot Engine بنجاح في كل من 2D و 3D.
مقدمة
أبسط شيء يمكنك التفكير فيه هو لعبة تجمع فيها شخصيتنا الرئيسية العملات المعدنية. لتعقيدها قليلاً ، أضف عقبة ووقتًا كعامل محدد. ستحتوي اللعبة على 3 مشاهد: Player
و Coin
و HUD
(لم يتم النظر في هذه المقالة) ، والتي سيتم دمجها في مشهد Main
واحد.

إعدادات المشروع
قبل أن تغوص في كتابة النصوص البرمجية (البرامج النصية) ، والتي تمثل ما يقرب من 80-90٪ من إجمالي الوقت المنقضي في إنشاء اللعبة ، فإن أول شيء نقوم به هو إعداد مشروعنا المستقبلي. في المشاريع الكبيرة ، من المفيد إنشاء مجلدات منفصلة لتخزين البرامج النصية والمشاهد والصور والأصوات ، ويجب أن نأخذ هذا في الاعتبار بالتأكيد ، لأن من يعرف ما ستكون النتيجة النهائية.
أريد إجراء حجز على الفور لأن هذه المقالة تفترض أنك معتاد قليلاً على محرك Godot ولديك بعض المعرفة والمهارات في استخدام هذه الأداة ، على الرغم من أنني سأركز على حقيقة أنك تواجه محرك Godot للمرة الأولى ، ما زلت أنصحك أولاً بالتعرف على المكون الأساسي للمحرك ، ودراسة بناء جملة GDScript والتوصل إلى فهم للمصطلحات المستخدمة (العقد ، والمشاهد ، والإشارات ، وما إلى ذلك) ، ثم العودة هنا فقط ومتابعة التعارف.
في قائمة البرنامج ، انتقل إلى Project -> Project Settings
.
انحراف صغير آخر. سأقدم دائمًا أمثلة استنادًا إلى حقيقة أن المستخدم النهائي يستخدم واجهة اللغة الإنجليزية للمحرك ، على الرغم من حقيقة أن محرك Godot يدعم اللغة الروسية. يتم ذلك من أجل التخلص من سوء الفهم أو الإحراج المحتمل المرتبط بالترجمة غير الصحيحة / غير الدقيقة لعناصر معينة من واجهة البرنامج.
ابحث عن قسم Display/Window
واضبط العرض على 800
والارتفاع على 600
. أيضًا في هذا القسم ، قم بتعيين Stretch/Mode
إلى 2D
و Aspect
to Keep
. سيؤدي هذا إلى منع تمدد وتشويه محتويات النافذة عند تغيير حجمها ، ولكن لمنع تغيير حجم النافذة ، قم ببساطة بإلغاء تحديد المربع Resizable
لتغيير الحجم. أنصحك باللعب مع هذه الخيارات.
انتقل الآن إلى قسم العرض Rendering/Quality
وقم بتشغيل Use Pixel Snap
في الجزء الأيمن. ما هذا؟ إحداثيات المتجهات في محرك Godot هي أرقام نقطة عائمة. نظرًا لأن الكائنات لا يمكن رسمها بنصف بكسل فقط ، فإن هذا التناقض يمكن أن يسبب عيوبًا مرئية للألعاب التي تستخدم بكسل. وتجدر الإشارة إلى أن هذا الخيار في 3D غير مجدي. ضع هذا في الاعتبار.
مشهد لاعب
لنبدأ في إنشاء المشهد الأول - Player
.
ميزة أي مشهد هي أنهم في البداية مستقلون عن أجزاء أخرى من اللعبة وهذا يجعل من الممكن اختبارها بحرية والحصول على النتيجة التي تم وضعها في الأصل فيها. بشكل عام ، يعد تقسيم كائنات اللعبة إلى مشاهد أداة مفيدة في إنشاء ألعاب معقدة - من السهل التقاط الأخطاء ، وإجراء تغييرات على المشهد نفسه ، في حين أن أجزاء أخرى من اللعبة لن تتأثر ، يمكن أن تعمل أيضًا كنماذج لألعاب أخرى وستعمل بالتأكيد أيضًا. كما عملوا قبل النقل.
إنشاء مشهد هو إجراء بسيط للغاية - في علامة التبويب Scene
، انقر فوق +
(إضافة / إنشاء) وحدد العقدة Area2D
وقم بتغيير اسمه على الفور حتى لا يتم الخلط بينه وبين. هذه هي العقدة الرئيسية ، ولتوسيع الوظائف تحتاج إلى إضافة العقد الفرعية. في حالتنا ، هذه هي AnimatedSprite
و CollisionShape2D
، لكن دعنا لا نندفع ، ولكن لنبدأ بالترتيب. بعد ذلك ، "أغلق" العقدة الجذرية على الفور:

إذا تم تغيير شكل تصادم الجسم (CollisionShape2D) أو العفريت (AnimatedSprite) ، وتمتد بالنسبة للعقدة الأصلية ، فسيؤدي ذلك بالتأكيد إلى أخطاء غير متوقعة ، وبعد ذلك سيكون من الصعب تصحيحها. مع تمكين هذا الخيار ، سينتقل "الوالد" وجميع "أطفاله" معًا دائمًا. يبدو الأمر مضحكًا ، ولكن استخدام هذه الميزة مفيد للغاية.
AnimatedSprite
Area2D
عقدة مفيدة للغاية إذا كنت بحاجة إلى معرفة حدث تداخل مع كائنات أخرى أو عن تصادمها ، ولكنها في حد ذاتها غير مرئية للعين وجعل كائن Player
مرئيًا يضيف AnimatedSprite
. يخبرنا اسم العقدة أننا سنتعامل مع الرسوم المتحركة والعفاريت. في نافذة Inspector
، انتقل إلى معلمة Frames
وقم بإنشاء SpriteFrames
جديد. العمل مع لوحة SpriteFrames
هو إنشاء الرسوم المتحركة اللازمة وتحميل SpriteFrames
المتحركة المقابلة لها. لن نحلل بالتفصيل جميع مراحل إنشاء الرسوم المتحركة ، وترك هذا للدراسة المستقلة ، سأقول فقط أنه يجب أن يكون لدينا ثلاثة رسوم متحركة: walk
(الرسوم المتحركة للمشي) ، idle
(حالة الراحة) die
(الموت أو الرسوم المتحركة الفاشلة). لا تنس أن قيمة SPEED (FPS)
يجب أن تكون 8
(على الرغم من أنه يمكنك اختيار قيمة مختلفة - مناسبة).

شكل التصادمات 2 د
لكي تكتشف Area2D
التصادمات ، يجب أن تزودها بشكل كائن. يتم تحديد الأشكال بواسطة معلمة Shape2D
وتتضمن مستطيلات ودوائر ومضلعات وأنواع أخرى أكثر تعقيدًا من الأشكال ، ويتم تحرير الأحجام بالفعل في المحرر نفسه ، ولكن يمكنك دائمًا استخدام Inspector
لمزيد من الضبط الدقيق.
السيناريوهات
الآن ، من أجل "إحياء" كائن لعبتنا ، تحتاج إلى تعيين نص برمجي له ، يتم بموجبه تنفيذ الإجراءات التي حددناها ، والموصوفة في هذا السيناريو. في علامة التبويب Scene
، أنشئ نصًا برمجيًا ، واترك الإعدادات الافتراضية ، واحذف جميع التعليقات (الأسطر التي تبدأ بعلامة "#") وتابع الشروع في تعريف المتغيرات:
export (int) var speed var velocity = Vector2() var window_size = Vector2(800, 600)
يتيح لك استخدام الكلمة الأساسية export
تعيين قيمة متغير speed
في نافذة لوحة Inspector
. هذه طريقة مفيدة جدًا إذا أردنا الحصول على قيم مخصصة مناسبة للتحرير في نافذة Inspector
. اضبط Speed
على 350
. ستحدد قيمة velocity
اتجاه الحركة ، وحجم window_size
هو المنطقة التي تقيد حركة اللاعب.
مزيد من ترتيب إجراءاتنا كما يلي: نستخدم الدالة get_input()
للتحقق مما إذا كان إدخال لوحة المفاتيح يتم. ثم سنقوم بتحريك الكائن ، وفقًا للمفاتيح المضغوطة ، ثم تشغيل الرسوم المتحركة. سينتقل اللاعب في أربعة اتجاهات. بشكل افتراضي ، يحتوي محرك Godot على أحداث مخصصة لمفاتيح الأسهم ( Project -> Project Settings -> Input Map
) ، حتى نتمكن من تطبيقها على مشروعنا. لمعرفة ما إذا تم الضغط على مفتاح ما ، يجب عليك استخدام Input.is_action_pressed()
عن طريق Input.is_action_pressed()
اسم الحدث الذي تريد تتبعه.
func get_input(): velocity = Vector2() if Input.is_action_pressed("ui_left"): velocity.x -= 1 if Input.is_action_pressed("ui_right"): velocity.x += 1 if Input.is_action_pressed("ui_up"): velocity.y -= 1 if Input.is_action_pressed("ui_down"): velocity.y += 1 if velocity.length() > 0: velocity = velocity.normalized() * speed
للوهلة الأولى ، يبدو كل شيء جيدًا ، ولكن هناك فارق بسيط واحد صغير. سيؤدي الجمع بين عدة مفاتيح تم الضغط عليها (على سبيل المثال ، لأسفل ولليسار) إلى إضافة المتجهات وفي هذه الحالة سوف يتحرك اللاعب بشكل أسرع مما لو انتقل ببساطة لأسفل. لتجنب ذلك ، سنستخدم طريقة normalized()
- ستعيد طول المتجه إلى 1
.
لذا ، تم تتبع أحداث ضغطات المفاتيح ، والآن تحتاج إلى تحريك كائن Player
. _process()
وظيفة _process()
ذلك ، والذي يُطلق عليه في كل مرة يحدث فيها تغيير في الإطار ، لذا يُنصح باستخدامها لتلك الكائنات التي ستتغير غالبًا.
func _process(delta): get_input() position += velocity * delta position.x = clamp(position.x, 0, window_size.x) position.y = clamp(position.y, 0, window_size.y)
آمل أن تلاحظ معلمة delta
، والتي يتم ضربها بدورها بالسرعة. من الضروري شرح ما هو عليه. تم تكوين محرك اللعبة مبدئيًا للعمل بسرعة 60 إطارًا في الثانية. ومع ذلك ، قد تكون هناك حالات يتباطأ فيها الكمبيوتر أو محرك Godot نفسه. إذا كان معدل الإطارات غير متسق (الوقت المستغرق لتغيير الإطارات) ، فسيؤثر ذلك على "سلاسة" حركة كائنات اللعبة (نتيجة لذلك ، تكون الحركة "متشنجة"). يحل "Godot Engine" هذه المشكلة (مثل معظم المحركات المشابهة) من خلال إدخال متغير delta
- فهو يعطي القيمة التي تم تغيير الإطارات لها. بفضل هذه القيم ، يمكنك "محاذاة" الحركة. انتبه إلى وظيفة أخرى رائعة - clamp
(إرجاع القيمة ضمن معلمتين محددتين) ، بفضلها يمكننا تحديد المنطقة التي يمكن Player
تحريكها ببساطة عن طريق تحديد الحد الأدنى والحد الأقصى لقيمة المنطقة.
لا تنسى تحريك وجوهنا. يرجى ملاحظة أنه عندما يتحرك الكائن إلى اليمين ، تحتاج إلى قلب AnimatedSprite
(باستخدام flip_h
) وسوف ينظر بطلنا في الاتجاه حيث يتحرك مباشرة عند التحرك. تأكد من أنه في AnimatedSprite
تم تمكين معلمة Playing
حتى يبدأ تشغيل الرسوم المتحركة.
if velocity.length() > 0: $AnimatedSprite.animation = "walk" $AnimatedSprite.flip_h = velocity.x < 0 else: $AnimatedSprite.animation = "idle"
الولادة والموت
عند بدء اللعبة ، يجب إبلاغ المشهد الرئيسي بالمشاهد الرئيسية حول استعدادها لبدء لعبة جديدة ، في حالتنا ، يجب عليك إبلاغ كائن Player
عن بداية اللعبة وتعيين المعلمات الأولية لها: موضع المظهر ، الرسوم المتحركة الافتراضية ، تشغيل set_process
.
func start(pos): set_process(true)
نوفر أيضًا حدث وفاة لاعب عندما ينفد الوقت أو set_process (false)
اللاعب عقبة ، وتعيين set_process (false)
سيؤدي إلى عدم تنفيذ وظيفة _process ()
لهذا المشهد.
func die(): $AnimatedSprite.animation = "die" set_process(false)
إضافة التصادمات
جاء الدور لجعل اللاعب يكتشف التصادمات بالقطع النقدية والعقبات. يتم تنفيذ ذلك بسهولة أكبر باستخدام الإشارات. تعد الإشارات طريقة رائعة لإرسال رسالة حتى تتمكن العقد الأخرى من اكتشافها والاستجابة لها. تحتوي معظم العقد بالفعل على إشارات مضمنة ، ولكن من الممكن تحديد إشارات "المستخدم" لأغراضها الخاصة. تضاف الإشارات إذا أعلنت عنها في بداية النص:
signal pickup signal die
تصفح قائمة الإشارات في نافذة Inspector
(علامة التبويب Node
) ، وانتبه إلى إشاراتنا والإشارات الموجودة بالفعل. الآن نحن مهتمون بـ area_entered ()
، يفترض أن الكائنات التي سيحدث معها التصادم هي أيضًا من النوع Area2D
. نقوم بتوصيل إشارة area_entered ()
باستخدام زر Connect
، وفي نافذة Connect Signal
، حدد العقدة المميزة (Player) ، واترك الباقي افتراضيًا.
func _on_Player_area_entered( area ): if area.is_in_group("coins"):
حتى يمكن الكشف عن الأشياء والتفاعل معها بسهولة ، يجب تحديدها في المجموعات المناسبة. تم حذف إنشاء المجموعات نفسها الآن ، ولكن بالتأكيد سنعود إليها لاحقًا. تحدد وظيفة pickup()
سلوك العملة (على سبيل المثال ، يمكنها تشغيل الرسوم المتحركة أو الصوت ، وحذف كائن ، وما إلى ذلك).
مشهد العملة
عند إنشاء مشهد بعملة واحدة ، تحتاج إلى القيام بكل ما قمنا به مع مشهد "Player" ، باستثناء أنه لن يكون هناك سوى رسم متحرك واحد (تمييز) في AnimatedSprite
. يمكن زيادة Speed (FPS)
إلى 14
. سنقوم أيضًا بتغيير مقياس AnimatedSprite
- 0,5, 0,5
. وأبعاد CollisionShape2D
يجب أن تتوافق مع صورة العملة ، الشيء الرئيسي هو عدم تغيير حجمها ، أي تغيير الحجم باستخدام العلامات المناسبة في النموذج في محرر ثنائي الأبعاد ، والذي
يضبط نصف قطر الدائرة.

المجموعات هي نوع من تصنيف العقد التي تسمح لك بتحديد العقد المماثلة. لكي يتفاعل كائن Player
مع اللمس بالقطع النقدية ، يجب أن تنتمي القطع النقدية إلى المجموعة ، دعنا نسميها coins
. حدد العقدة Area2D
(بالاسم "Coin") وقم بتعيين علامة لها في Node -> Groups
علامة التبويب Node -> Groups
، وإنشاء المجموعة المقابلة.

للعقدة Coin
، قم بإنشاء برنامج نصي. سيتم استدعاء وظيفة pickup()
من خلال البرنامج النصي لكائن Player
وستخبر العملة بما يجب فعله عندما تعمل. queue_free()
طريقة queue_free()
بأمان عقدة من الشجرة بكل العقد التابعة لها queue_free()
الذاكرة ، ولكن لن يعمل الحذف على الفور ، سيتم نقلها أولاً إلى قائمة الانتظار ليتم حذفها في نهاية الإطار الحالي. هذا أكثر أمانًا من حذف العقدة على الفور ، لأن "المشاركين" الآخرين (العقد أو المشاهد) في اللعبة قد يحتاجون إلى وجود هذه العقدة.
func pickup (): queue_free ()
الخلاصة
يمكننا الآن إنشاء المشهد Main
، وسحب المشهدين: Player
Coin
بالماوس إلى محرر ثنائي الأبعاد ، والتحقق من كيفية عمل حركة اللاعب واتصاله بالعملة عن طريق بدء المشهد (F5). حسنًا ، أنا مستعجل لأقول أن الجزء الأول من إنشاء لعبة "Like Coins" قد انتهى ، دعني آخذ إجازة وأشكر الجميع على انتباههم. إذا كان لديك ما تقوله ، فقم بتكملة المادة أو رأيت أخطاء في المقالة ، فتأكد من الإبلاغ عنها بكتابة تعليق أدناه. لا تخف من أن تكون قاسيًا وحاسمًا. ستخبرك "تعليقاتك" بما إذا كنت أتحرك في الاتجاه الصحيح وما الذي يمكن إصلاحه لجعل المواد أكثر إثارة للاهتمام وفائدة.