اول لعبة لك بالمحرك #2 جعل اللاعب يتحرك

في درس اليوم سنجعل شخصية اللاعب تتحرك في الاتجاهات الثمانية المعرفة
ونضع لها انيميشن بسيط يعمل كلما تحرك اللاعب
فقط تأكد من أنك تابعت الدرس السابق لكي تواصل معنا اليوم

الدرس السابق : أول لعبة لك بالمحرك #1 المقدمة

ايضا وجب التنبيه أنني عندما اتكلم عن الاتجاهات سوف استخدم اتجاهات عقارب الساعة
مثلا عندما اقول “سنجعل اللاعب يتحرك باتجاه الساعة الثالثة”
فانا اعني انه سيتحرك إلى اليمين
فقط لجعل الامور واضحة ولكي تفهم جيدا عما نتكلم 

****************

تأكد من اختيار المشروع الذي بدأناه في الدرس السابق
وتأكد من اختيار محرر الـ2D
بعدها اضف العقدة المسماة Area2D من مجموعة الـ scene لكي تكون العقدة الاساسية للاعب
تستطيع كتابة اسمها او فقط جزء من اسمها يشير إليها
وقم بتسميتها Player مع P وليس p لانها ستفرق في المستقبل عند كتابة الاكواد
add_area2D.gif

العقدة Area2D
والتي تعني منطقة هي مجرد كائن يعبر عن منطقة معينة وبما انها 2D أي أنها ذات بعدين (طول وعرض)
ولها الكثير من الخصائص المهمة التي نحتاجها
هناك أنواع عقد ثانية مختلفة تشبهها ولكننا نحتاج إلى هذه العقدة بسبب بعض الخصائص والتي ساتكلم عنها في الدروس القادمة
يمكننا استخدام هذه العقدة لجعل الشخصية تتحرك
عن طريق تغير احداثياتها من خلال السكربت الخاص بنا اذا ما ضغط المستخدم على زر معين
ولكن ما لاحظت فانه هناك علامة باللون الاصفر وعليها علامة تعجب بجانب اسم العقدة
العقدة area2D تعني منطقة ولكننا لم نحدد ما هو شكل هذه المنطقة
هل هو مربع أو دائرة أو شكل بيضاوي أو حتى شكل غريب
لذلك يجب تحديد المنطقة الخاصة بها
ولتحديد المنطقة هناك عقدة (الكيان) والتي تسمي CollisionShape2D
ولانها عقدة 2D فلها بعدان فقط كما قلنا
ولكن قبل إضافة تلك العقدة نحتاج إلى معرفة اللاعب الخاص بنا وكيف سيكون شكله لنستطيع تحديد الشكل الخاص به بدقة
تأكد من تحديد العقدة Player ثم اضف اليها العقدة AnimatedSprte احذر هناك العقدة AnimatedSprte3D
وهذه لا نحتاجها الان لاننا لسنا في درس 3D

عقدة AnimatedSprte
تخيل أن لديك كتاب صغير او مذكرة صغيرة ورسمت عليها في كل مرة رسمة وتتغير رسمتك بطريقة تدريجية في كل صفحة
اعتقد اننا كلنا جربنا عمل مثل هذه المذكرة
وعند قلب الصفحات بسرعة ستشعر كان الرسمة خاصتك تتحرك أليس كذلك
flip book.gif
وهذا بالضبط هو عمل هذه العقدة ستقوم
بعمل أكثر من مجموعة من الصور وتستطيع أنت تحديد أيهما يعمل وايهم لا يعمل
ومتى يتم ايقافها ومتي يتم تشغيلها وعدد الصور التي ستقلب في الثانية الواحدة والصفحات تسمي الفريمات (frames)

وكما تري فانه ايضا عقدة AnimatedSprte لديها نفس العلامة الصفراء
ولكن هذه المرة لتقول لك انها ليس فيها اي صور لإستخدامها
قم بتحديد العقدة AnimatedSprteframes.gif
وعند مجموعة الخصائص inspector
وعند الخاصية frames أمامها اضغط علي null ثم new spriteframes ثم اضغط عليها مجددا
inspector >> frames >> new sprteframes

* مستقبلا لن أضع صور متحركة عن أمور مشابهة فقط ساقوم بتحديد مكان الخاصية (الا اذا احتاج الامر الى ذلك)

اعتقد الان ان العلامة الصفراء قد اختفت وظهر لك هذا في اسفل المحرر

في الجانب يوجد كلمة default وهي من المفترض أن تكون مجموعة من الصور
قم بتغير اسمها الى “right” ثم اضف واحدة اخري عن طريق علامة الملف التي بجانبها (+) الموجودة تحت اسم animations
وقم بتسميتها “up”
الان في مجموعة الملف file system اذهب الى هذا المسار res://dodge_assets/player
ستجد هناك صور للاعب خاصتنا
ضع الصور playerGrey_up1 + 2 عن طريق السحب والإفلات
في المجموعة up التي أنشأناها ثم قم بنفس مع الصور playerGrey_walk1 + 2 وضعهم في المجموعة right
sprites.gif
وهذا هو شكل اللاعب خاصتنا
لكنه كبير بعض الشئ بالنسبة لمشهد أو نافذة اللعبة
لذلك لنقم بتغير بتصغير حجمه قليلا
قم بتحديد عقدة AnimatedSprte ثم inspector >> transform >> scale
ستجد هناك x للعرض و y للطول
في الـ scale اذا كانت اي بعد منهم = 1 إذا فهو البعد الاصلي واقل بعد هو 0.00001
لذلك أي قيمة بينهم سيتم احتسابها كأنها درجة مئوية
1 = 100%
0.00001 = 0.0000.1%
لنقم بتصغير حجم شخصية اللاعب الى النصف
اجعل كلا من الطول والعرض = 0.5 (تأكد من الضغط على enter بعد الإنهاء)

وهكذا نكون انتهينا من صورة الشخصية

نرجع الان الي عقدة Area2D
الان لنقم بوضع الشكل لها
لانها مجرد شبح ليس له كيان لنقم اولا بتحديد العقدة Area2D ثم اضف اليها العقدة CollisionShape2D
وبهذه الطريقة تمت ازالة العلامة الصفراء من Area2D وظهرت اخري في عقدة CollisionShape2D
وهذه تنبيه علي انك لم تحدد هل الشكل مجرد مربع او دائرة أم ماذا؟
حدد العقدة CollisionShape2D ثم
inspector >> shape >> null >> new CapsuleShape2D

اخترنا الشكل البيضاوي لانه الاقرب الى شخصية اللاعب
لكن الشكل البيضاوي صغير قليلا على الشخصية لذلك سنقوم بتغير الحجم قليلا
كبر حجم الشكل عن طريق الدوائر التي بجانب الشكل بما يتناسب مع الصورة

ملاحظة: تأكد دائما من اختيار الدوائر الداخلية وليست الخارجية لتغيير حجم الشكل(الكيان)

تبقى الآن شئ آخر قبل البدء في كتابة السكربت
اذا ما جربت تغير موضع اللاعب في المحرر
ستجد أن الصورة في مكان والكيان في مكان آخر (اضغط على ctrl + z للرجوع)
وهذه مشكلة لاننا نحتاج الي جعل العقدتين متشابكتين يتحركان مع بعضهما البعض
لذلك قم باختيار عقدة الأب Area2D ثم اضغط علي هذه العلامة

لتتاكد ان جميع ابناء هذه العقدة سيتحركون معا حتي الأب سيتحرك معهم

ان لم تفهم معنى مصطلحات الاب والابناء فراجع هذه التدوينة

واخيرا قد انتهينا من وضع العقد المطلوبة فقط قم بعمل save لهذه ال scene تجنبا لأي مشاكل في المستقبل
فلا نعلم ما قد يحدث
ثم حدد عقدة Player وقم بإدراج سكربت لها عن طريق هذه الايقونة

ستظهر هذه الواجهة
تستطيع الضغط فقط على create لان محرك جودو وضع الاعدادات الافتراضية

لكن سوف اقوم بشرحها لان عملي هو شرح المحرك بما اني اول عربي يشرحه
بداية يطلب منك اختيار لغة البرمجة التي سيتم استعمالها
وهي افتارضيا GDScript لذلك لا تغيرها
بعدها عند template يسألك عن شكل السكربت ستتعرف علي شكل السكربت وستفهم بقية الخيارات لكن لانها اول مرة اتركها كما هي
في Built-in script إذا كانت on سيتم عمل السكربت بجانب ملف الـ scene وسيتم اغلاق path لانها لا فائدة منها
أما لو كانت off تستطيع تغيير مكان السكربت الي اي مكان تريده عن طريق path
في الصندوق معلومات عن السكربت هل هو موجود من الاساس من أنه سيتم عمل سكربت جديد
الان اضغط على create
وسيتم نقلك إلى السكربت داخل محرر الاكواد

GDScript

اذا كان ورائك اي شئ قم به الان لكي تفرغ تماما لـ GDScript
فكاول مرة لك معها انا لا اريد تترك لك انطباع سئ

(اهتم بها لكي تهتم بك)

كما قلنا من قبل أكثر من مرة فهي لغة مشابهة جدا للغة بايثون مع بعض الفروقات البسيطة
منها (تستيطع  رؤية هذه التدوينة لمعلومات اكثر عن اللغة)
* عند تعريف اي متغير سيتم تعريفه عن طريق var name حيث “name” هو اسم المتغير
* عند عمل فانكشن جديدة يكون اسمها func name حيث “name” هو اسم الفانكشن
* هناك بعض الفانكشن المعروفة مسبقا ولها فؤاد خاصة تختلف عن غيرها ولا يمكن عمل مثلها الا بطرق خاصة سيتم شرح البعض منها داخل هذه التدوينة
* اذا استعملت مكاتب من قبل في لغة بايثون ستعرف اذا كيفية استخدام الأوامر الجديدة ولكن حتى ولو لم تجرب فستكون هذه أول مرة لك (تعلم جيدا وركز جيدا فيها)

من المفترض أن لديك هذه الواجهة

كما ترى فإنه هناك الكثير من التعليقات وكلها فقط تجعلك تفهم كيفية عمل البرنامج
قم بسمح كل ما في السكربت ولكن اترك السطر الاول فهو ما يحدد لاي عقدة هذا السكربت بدون هذا السطر لن يعمل السكربت وسيعطيك خطأ
ملحوظة
العقدة التي تم اختيارها ثم تم الضغط على ايقونة السكربت فهي التي سيتم التحكم فيها
اي ان السكربت هو من سيتحكم في تلك العقدة مع ابنائها
هناك طريقة للتحكم في الاباء من الابناء لكن الاسهل هو جعل الأب من له السكربت
نحتاج إلى تحديد سرعة اللاعب لذلك كتبنا السطر الأول ولم نسند إليه قيمة
ونحتاج إلى معرفة حجم نافذة اللعبة لكي لا يخرج اللاعب منها
أتريد أن ترى مفاجئة
حدد العقدة Area2D وانظر داخل المجموعة inspector
ستجد انه تم اضافة خاصية speed اليها وانها تقبل قيم رقمية
الأمر export هو الذي يضع الخاصية داخل الـ inspector
من ثم يتم تحديد نوع القيم المدخلة اليه ثم يتم تحديد اسم المتغير الخاص بها
طبعا تستطيع تغير الاسماء ولكن الأفضل تركها كما لكي لا يتعقد الأمر عند حدوث أي مشكلة

الآن لديك طريقتين لإدخال سرعة اللاعب اما عن طريق inspector >> speed
او عن طريق وضع علامة = ثم القيمة بعد speed في السكربت (لتكون القيمة الافتراضية ما لم يتم تغييرها من inspector)
ضع لها حاليا قيمة 400 بالطريقة التي تعجبك
ثم قم باضافة هذين السطرين
يمكنك زيارة هذه التدوينة للمزيد من المعلومات عن الدالتين ready, process

قم بتحديد العقدة Area2D ثم
inspector >> transform >> position
وضع اي ارقام بداخلها
ستجد ان الشخصية قد تحركت من مكانها او بمعني اخر تغيرت احداثياتها
وهذا هو ما سنفعله
حيث في كل مرة يضغط فيها المستخدم على أحد المفاتيح سيتحرك اللاعب بمقدار 1 بيكسل
وطبعا لان func _process معنا سيتم استدعاؤها 60 مرة في الثانية فيتحرك اللاعب بمقدار 1 * 60 بيكسل للثانية الواحدة

اضف هذا الكود بعد func ready_procsess.PNG

النقطة رقم (1)
اعتقد انك عندما كنت في الابتدائية او الاعدادية فقد درست الرسوم البيانية وتعرف التمثيل البياني هذا

وكما لاحظت فإن (س = x) و (ص = y) إذا كنت درستها باللغة العربية
و Vector2 هي لتعريف هذا التمثيل مع فرق ان y و y- انقبلت أماكنهم
فأصبح الموجب هو الموجود في الاسفل والسالب هو الموجود في الأعلى
لكن كيف يفيدونا ذلك ؟
لانه هذه هي الوسيلة التي سوف نستعملها لجعل اللاعب يتحرك
فحين نقول له velocity.x = 15
ستتغير إحداثياته وتصبح بدلا من (0, 0) إلى (0, 15) ايه انه اتجه الي عقرب الساعة 3
ستفهم الأمر أكثر عند النقطة (3)

النقطة رقم (2)
Input.is_action_pressed(“ui_right”):
مفتاح الاتجاه الايمن يرمز له بـ”ui_right” (في نهاية الدرس سوف اقول لك كيف تعرف الرموز وكيف تغير اسمائها)
والدالة
Input.is_action_pressed
تعود بـ true اذا ما تم الضغط علي المفتاح الموجود رمزه بين القوسين وستظل true عندما يكون المفتاح مضغوط مثل ازرار الاسهم التي تجعل اللاعب يتحرك
بعكس الدالة Input.is_action_just_pressed والتي ستكون true مرة واحدة فقط حتى ولو كان المفتاح مضغوط
ما ان يتراجع المستخدم عن الضغط على المفتاح سترجع ويكون لها القابلية أن تكون true مجددا
مثلا في العاب الشوتر التي تحتاج إلى ضرب العدو فتقوم بالضغط علي زر مسافة فيقوم اللاعب بإطلاق رصاصة واحدة
تحتاج الى الضغط مجددا على مفتاح المسافة لكي يطلق رصاصة أخرى وهكذا

النقطة رقم (3)
هنا سنجعل احداثيات اللاعب تزداد بمقدار 1 افقيا (أي في اتجاه عقرب الساعة 3)
اي انه اذا كانت إحداثياته = (0, 0) ستصبح (0, 1)
حتي ان كانت (20, 20) ستصبح (20, 21)
في السطور التي بعدها كتبنا نفس الاوامر لكن مع اختلاف نوع الاحداثيات وهل هي تزداد أو تنقص
لذلك لاحظ جيدا عندما نقول velocity.x وبين velocity.y
وفرق جيدا ما بين =- و =+
استعمل هذه الصورة لمعرفة كيفية استخدام الإحداثيات

النقطة رقم (4)
اعتقد هذه بسيطة
هي مجرد امر لمعرفة إذا ما كان اللاعب تتغير إحداثياته ام لا
لماذا ؟
لانه ــ مثلا ــ إذا كانت سرعة اللاعب = 1
وضغط على زر السهم لأعلى سيتحرك اللاعب بسرعة 1
لكن اذا ضغط علي زر السهم لاعلي وزر السهم الأيمن (سيذهب اللاعب باتجاه ما بين الساعة الثانية والساعة الواحدة)
وستكون سرعته 2 أي أنه سيتحرك بسرعة (زر السهم لأعلى + زر السهم الأيمن)
لذلك ستكون سرعته أكبر من المعتاد و ستحتاج الي تغيرها
عن طريق النقطة رقم 5
وايضا سنحتاج إلى جعل الانيميشن الخاص به يتحرك هو الاخر
عن طريق النقطة رقم 6

النقطة رقم (5)
استخدمنا .normalized() * speed
لكي نجعل حركة اللاعب سواء في الاتجاهات الجانبية (مثل الاتجاه ما بين الساعة الثانية والساعة الواحدة) أو الاتجاهات الاساسية (مثل الاتجاه إلى الساعة الثانية عشر او الاتجاه الى الساعة الثالثة) بنفس الحجم
ثم ضربناها في السرعة الاساسية خاصتنا (لانها سرعة اللاعب الاصلية)

النقطة رقم (6)
وهذه مهمة جدا
فمثلا إذا كنا نريد استدعاء خاصية ما من العقدة الابن فكان قديما نستخدم
get_node() ونكتب اسم العقدة داخل الاقواس ثم نستدعي الخاصية
لكن الان اصبح الامر اسهل فقط اكتب هذه العلامة ($) ثم اكتب اسم العقدة الابن ملاصقا بعدها للعلامة
بعد ذلك نكتب نقطة فقط للتمهيد إلى أننا سوف نستدعي خاصية من تلك العقدة
والخاصية التي استدعيناها تسمي play وهي لجعل الانيميشن يبدأ
ثم استعملنا else
وستعمل عندما يتوقف اللاعب عن الحركة (إقرأ النقطة رقم 4 لتفهم الامر اكثر)
وعندها استخدمنا الخاصية stop لجعل انيميشن اللاعب يتوقف

جرب اضغط على F6 او اضغط علي play scene فى اعلى يمين المشروع
لكي نقوم بتشغيل اللعبة خاصتنا
جرب حرك الشخصية عن طريق الاسهم

نعم للاسف الانيميشن يعمل لكن اللاعب لا يتحرك من مكانه من الأساس
وطبعا هذه المشكلة سوف تواجهك
بسبب اننا لم نحدد أي من العقد التي تتحرك وتتغير احداثياتها
أي أننا فقط قمنا بوضع القيم التي نحتاجها داخل المتغير velocity ولم نستخدم ذلك المتغير تغيير احداثيات العقدة التي نحتاجها
أتذكر عندما قلت لك طريقة استدعاء عقدة الابن وكيفية استدعاء خاصية منها
هذه نستخدمها عندما يكون السكربت في عقدة الأب
ولكن ماذا اذا اردنا استدعاء خاصية من عقدة الأب نفسه وليس الابن
اتذكر عندما قلت حدد العقدة الأب ثم اذهب الى inspector >> transform >> position
هذه هي الخاصية التي سوف نستخدمها لجعل الشخصية تتحرك بالسرعة التي نريدها
لذلك قم باضافة هذا الكود الي func _process

نقوم في السطر الأول بجعل الاحداثيات تتغير مع المتغير velocity ونضربها بالدلتا (وهي متغير ثابت لـ فانكشن)
ثم بعد ذلك نعطي امر بان نجعل اللاعب سرعته = 0 ما إذا حاول الخروج من شاشة اللعبة

قم الآن بتشغيل اللعبة عن طريق F6 او عن طريق الضغط على play scene فى اعلى يمين واجهة المشروع
جرب الضغط على مفاتيح الأسهم
ستجد ان اللاعب بدأ بالتحرك وانه لا يستطيع الخروج خارج الشاشة

نرجع الان الى الكود خاصتنا
اضف هذا الكود داخل func _process

بهذه الطريقة سيعمل الانيميشن بطريقة أفضل
جرب الآن واضغط على F6
وقم بتحريك الشخصية ستجد ان الانيميشن يعمل حاليا بكفائة عالية
يمكنني شرح الكود الذي كتبناه في النهاية

ولكن هذه المرة سوف اترك لكم انتم المحاولة في معرفة كيفية عمله
وحاولوا ان تضعو استنتاجاتكم من خلال هذا الجروب —> https://goo.gl/czu3zz
وساقوم بالرد عليكم اذا كانت اجابتكم صحيحة أم لا
اذا اردت مساعدة بسيطة فارجع وانظر الي الكيفية التي جعلنا بها الانيميشن يتحرك
وهكذا نكون انتهينا من شرح كيفية جعل اللاعب يتحرك

ملحوظة
كيف يمكنك معرفة رموز مفاتيح التشغيل (الكيبورد) وكيفية تغييرها
اذهب الي project >> project settings ثم الى input map

1- خريطة المدخلات
2- لاضافة رمز جديد (ويكون مجرد اسم يمكنك تحديده كيفما شئت)
3- اسم رمز موجود من الاساس
4- اضافة المفتاح الخاص بالرمز (من الممكن ان يتحمل الرمز اكثر من مفتاح
5- ازالة المفتاح من الرمز
6- المفتاح نفسه
وهناك بجانب الايقونة الخاص برقم 5 يوجد ايقونة القلم وهي لتغير المفتاح نفسه ووضع مفتاح جديد

وهكذا اكون ختمت هذا الدرس وحاليا انت تعرف كيف تجعل اللاعب يتحرك
هناك طرق أخرى لجعل اللاعب يتحرك ولكن هذه ستكون في دروس أخرى في المستقبل

اول لعبة لك بالمحرك #3 الاعداء والمشهد الرئيسي

إذا كان هناك شئ لم اشرح بالتفصيل او اذا كان هناك شئ لم تفهمه
اترك تعليق او اسئل في احد هذه الجروبات علي الفيس بوك (ستجدني هناك)
تطوير الالعاب
جودو بالعربي
او يمكنك مراسلة المدونة عبر البريد

godotarabic@gmail.com

نُشرت بواسطة

light

مطور العاب مستقل, يامل بنشر محتوي تطوير الالعاب في الوطن العربي

7 رأي حول “اول لعبة لك بالمحرك #2 جعل اللاعب يتحرك”

    1. extends Area2D

      export (int) var speed
      var screensize

      func _ready():
      screensize = get_viewport().size

      func _process(delta):
      var velocity = Vector2()
      if Input.is_action_pressed(“ui_right”):
      velocity.x += 1
      if Input.is_action_pressed(“ui_left”):
      velocity.x -= 1
      if Input.is_action_pressed(“ui_down”):
      velocity.y += 1
      if Input.is_action_pressed(“ui_up”):
      velocity.y -= 1
      if velocity.length() > 0:
      velocity.normalized() * speed
      $AnimatedSprite.play()
      else:
      $AnimatedSprite.stop()

      position += velocity * delta
      position.x = clamp(position.x,0,screensize.x)
      position.y = clamp(position.y,0,screensize.y)

      if velocity.x != 0:
      $AnimatedSprite.animation = “right”
      $AnimatedSprite.flip_v = false
      $AnimatedSprite.flip_h = velocity.x 0

      إعجاب

    2. اسف علي الرد المتاخر
      الافضل لو كنت دخلت الجروب الخاص بالفيس بوك لان الكثيريون نشطون هناك ويمكن بسهولة ايجاد علي لمشكلتك
      بالنسبة لمشكلة
      فهل انت متاكد انك وضعت قيمة للمتغير speed؟

      إعجاب

    3. لا أتذكر , اذا كنت وضعت قيمة له 🙂 , كان منذ زمن بعيد
      ولا أمتلك حساب بالفيس بوك

      إعجاب

    4. مرحبا انا عندي مشكلة في انو لما اجرب ادخل اشوف اذا كل شيء تمام مايدخلني لكي اجرب وش الحل

      إعجاب

أضف تعليق