إنشاء بسيط C # AI في الوحدة

الصورة

تتطلب أي لعبة تقريبًا ذكاءً اصطناعيًا (AI) يتفاعل مع المستخدم ، غالبًا في شكل قوة معادية للاعب. في بعض الحالات ، يجب أن يساعد الذكاء الاصطناعي اللاعب ، وفي حالات أخرى يجب عليه محاربته ، ولكن جميع الشخصيات التي يتحكم فيها الكمبيوتر لديها بعض أوجه التشابه. اعتمادًا على متطلبات المشروع ، قد يستخدم الذكاء الاصطناعي سلوكيات بسيطة أو معقدة. قد تكون هذه المتطلبات دبلوماسية مع لاعب آخر أو تجوال بسيط ذهابًا وإيابًا على المنصة. مع ذلك ، من الضروري التأكد من أن الذكاء الاصطناعي يؤدي وظيفته بكفاءة.

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

تحضير


نحتاج أولاً إلى إنشاء مشروع ثلاثي الأبعاد. انقر على زر جديد في أعلى النافذة بعد بدء الوحدة ، كما هو موضح في الشكل 1.


الشكل 1: إنشاء مشروع جديد

قم بتسمية مشروع الذكاء الاصطناعي الخاص بك وتأكد من أنه مشروع ثلاثي الأبعاد. بعد اختيار مكان على الكمبيوتر لتخزين المشروع ، انقر فوق الزر إنشاء مشروع في الأسفل ، كما هو موضح في الشكل 2.


الشكل 2: شاشة إعداد المشروع

بعد إنشاء المشروع ، فإن أول شيء يتعين علينا القيام به هو إعداد المجلدات في نافذة الأصول لتنظيم عملنا. انقر بزر الماوس الأيمن على نافذة الأصول وحدد إنشاء ← مجلد لإنشاء مجلد جديد. اسم هذا المجلد. ثم قم بإنشاء مجلد ثانٍ واسمه نصوص برمجية . يوضح الشكل 3 كيف يجب أن يبدو.


الشكل 3: إنشاء مجلد جديد

بعد كل هذا ، يجب أن تبدو نافذة الأصول مثل تلك المعروضة في الشكل 4.


الشكل 4: نافذة الأصول .

بعد ذلك ، قم بإنشاء أرضية تقف عليها جميع الكائنات. في نافذة Hierarchy ، حدد Create → 3D Object → Plane لإنشاء كائن مستوي سيتم استخدامه كأرضية.


الشكل 5: إنشاء كائن طائرة.

قم بتسمية كائن Floor هذا وقم بتغيير قيمة مقياس X إلى 7 ، وقيمة مقياس Z إلى 3. بعد ذلك ، يجب أن تبدو نافذة المفتش مع كائن الطابق المحدد كما هو موضح في الشكل 6.


الشكل 6: تحديد خصائص كائن الطابق .

نحتاج الآن إلى إنشاء مادة جديدة لـ Floor لتمييزها عن باقي العناصر التي سيتم وضعها في المشهد. في مجلد المواد بنافذة الأصول ، قم بإنشاء مادة جديدة بالنقر بزر الماوس الأيمن على نافذة الأصول واختيار إنشاء → مادة .


الشكل 7: إنشاء مادة جديدة

عند الانتهاء ، قم بتسمية الطابق بالمادة.


الشكل 8: مواد الأرضيات .

في الجزء العلوي من نافذة المفتش مع تحديد Floor ، حدد منتقي الألوان.


الشكل 9: اختيار منتقي الألوان.

بالطبع ، يمكنك اختيار أي لون للأرضية ، ولكن في هذا المثال اخترت اللون الأحمر البني ، كما هو موضح في الشكل 10.


الشكل 10: منتقي الألوان.

حدد كائن Floor في نافذة Hierarchy ، وفي مكون Mesh Renderer ، حدد السهم الصغير بجوار المواد .


الشكل 11: التحضير لتغيير جوهري.

اسحب مادة الأرضية من نافذة الأصول إلى حقل العنصر 0 لمكون شبكة التقديم في نافذة المفتش .


الشكل 12: تعريف مادة الأرضية بأنها مادة كائن الأرض .

بعد الانتهاء من كائن Floor ، نحتاج إلى إنشاء حول منطقة الجدار حتى لا يتمكن اللاعب من السقوط من الحافة. ارجع إلى Create → 3D Object → Plane لإنشاء مستوى جديد. نسمي هذا المستوى من الحائط ونقوم بتعيينه على نفس الأبعاد مثل Floor ، أي ، X Scale بقيمة 7 و Z Scale بقيمة 3. ثم قم بإنشاء ثلاثة جدران أخرى ، حدد كائنًا واضغط على Ctrl + D ثلاث مرات. بعد ذلك ، ضع الجدران حولها الجنس وفقا للبيانات الواردة في الجدول.

العنوان
الموضع العاشر
المركز ص
الموقف ض
تناوب x
التناوب ض
الجدار
-35
21
0
0
-90
حائط (1)-1
11
-15
90
0
حائط (2)
-1
11
13.5
-90
0
حائط (3)
34
21
0
0
90

الجدول 1: مواقع وتدوير جميع كائنات الجدار .

بعد الانتهاء من كل هذا ، تحتاج إلى تغيير موضع الكاميرا بحيث تبدو على الأرض من الأعلى. حدد كائن الكاميرا الرئيسي واضبط موضع Y على 30 ، وضعية Z على 0 ، وتدوير X على 80.


الشكل 13: إعداد كائن الكاميرا.

المشهد جاهز ، لذا فقد حان الوقت لإنشاء شخصية اللاعب. في نافذة Hierarchy ، انقر فوق Create → 3D Object → Sphere لإنشاء كائن sphere. قم بتسمية مشغل هذا الكائن ، ثم انقر فوق الزر Add Component في أسفل نافذة المفتش .


الشكل 14: إضافة مكون جديد.

الآن ابحث عن Rigidbody . بعد ذلك ، حدد مكون Rigidbody من القائمة وأضف Rigidbody إلى كائن Player .


الشكل 15: إضافة مكون Rigidbody .

بعد ذلك ، تحتاج إلى تعيين علامة للاعب ، والتي ستكون مفيدة لنا لاحقًا في الرمز. انقر فوق القائمة المنسدلة Tag في الزاوية العلوية اليسرى من نافذة المفتش وحدد علامة Player .


الشكل 16: تحديد علامة جديدة.

نحن بحاجة إلى تعيين موقف اللاعب حتى لا يكون تحت كائن Floor . في المثال ، قمت بوضع اللاعب في الزاوية اليسرى العليا مع وضع X مساوٍ لـ 26 ، وموضع Y يساوي 1 ، وموضع Z يساوي -9.


الشكل 17: وضع اللاعب.

لكي يعمل رمزنا المستقبلي بشكل صحيح ، نحتاج بالطبع إلى إرفاقه بالكائن. ارجع إلى نافذة التسلسل الهرمي وحدد هذه المرة إنشاء ← كائن ثلاثي الأبعاد ← مكعب . اتصل بمكعب الحرس هذا ، وأضف مكون Rigidbody ومكون وكيل NavMesh إليه باستخدام زر إضافة مكون في نافذة المفتش . بعد ذلك ، ضعه في مكان ما في الزاوية اليسرى العليا من المشهد. بعد ذلك ، ستبدو نافذة المفتش لكائن الحرس كما يلي:


الشكل 18: كائن حارس في نافذة المفتش .

ويجب وضع هذا الكائن على النحو التالي:


الشكل 19: وضع كائن الحرس .

أخيرًا ، نحتاج إلى كائن يستخدم كـ "عين" كائن الحرس ، والذي سيخطر الحرس بأن اللاعب يلمسه. للمرة الأخيرة ، انتقل إلى نافذة التسلسل الهرمي وحدد إنشاء ← كائن ثلاثي الأبعاد ← كرة لإنشاء كائن كرة آخر. اسم هذا الكائن الكائن. هذه المرة لا نحتاج إلى إضافة أي مكونات أخرى إليه. ومع ذلك ، سوف نقوم بتغيير حجم الكائن. بعد اختيار Looker ، قم بتغيير متغيرات مكون التحويل التالية في نافذة المفتش .

  • المقياس X عند 9.
  • مقياس Y عند 0.5.
  • مقياس Z عند 9.

بعد ذلك ، ضع جهاز Looker بحيث يقع في الجزء العلوي الأوسط من الأرضية ، كما هو موضح في الشكل 20.


الشكل 20: موقع المشاهد.

إنه الوقت المناسب لإعطاء Looker مادة فريدة حتى تتمكن من رؤية أنها تستحق التجنب. في مجلد المواد بنافذة الأصول ، انقر بزر الماوس الأيمن وقم بإنشاء مادة جديدة. اسمها Looker وإعطائها لون أحمر فاتح. بعد ذلك ، قم بتعيين هذه المادة كمادة لكائن Looker لتغيير لونها. بعد ذلك ، يجب أن يبدو المشهد كما يلي:


الشكل 21: كائن Looker بمواد جديدة.

الشيء الوحيد المتبقي لنا هو إنشاء شبكة ملاحة للحرس ، والتي يمكن أن تتحرك عليها. توجد قائمة Window أعلى محرر الوحدة. اختر Window → Navigation لفتح نافذة التنقل الموضحة في الشكل 22.


الشكل 22: نافذة الملاحة .

حدد كائن الطابق في التسلسل الهرمي ، ثم في نافذة التنقل ، حدد خانة الاختيار التنقل الثابت .


الشكل 23: ملاحة ثابتة .

بعد ذلك ، حدد خيار Bake في أعلى النافذة.


الشكل 24: قم بالتبديل إلى قائمة الخبز .

تفتح قائمة Bake ، حيث يمكنك تغيير خصائص شبكة التنقل التي سننشئها. في مثالنا ، لا شيء يحتاج إلى تغيير. فقط انقر على زر Bake في أسفل اليمين.


الشكل 25: إنشاء شبكة تنقل جديدة.

عند هذه النقطة ، ستطلب منك الوحدة حفظ المشهد. احفظه ، وبعد ذلك سيتم إنشاء شبكة تنقل. الآن سيبدو المشهد على النحو التالي:


الشكل 26: المشهد الحالي مع شبكة التنقل المضافة.

الآن تم تكوين كل شيء في Unity ، لذا فقد حان الوقت لإنشاء النصوص البرمجية اللازمة لعمل المشروع. في نافذة الأصول ، انقر بزر الماوس الأيمن وحدد إنشاء → C # Script . قم بتسمية مشغل البرنامج النصي هذا. كرر هذه العملية مرتين أخريين ، مع إنشاء نصوص برمجية باسم Guard و Looker .


الشكل 27: إنشاء نص جديد.

بعد ذلك ، سيبدو مجلد البرامج النصية في نافذة الأصول كما يلي:


شكل 28: مجلد البرامج النصية

أولاً ، سنبدأ في كتابة كود البرنامج النصي للاعب . انقر نقرًا مزدوجًا فوق البرنامج النصي Player في نافذة الأصول لفتح Visual Studio وبدء إنشاء التعليمات البرمجية.

كود


نص اللاعب بسيط للغاية ، كل ما يفعله هو السماح للمستخدم بتحريك كائن الكرة. بموجب إعلان الصف ، نحتاج إلى الحصول على رابط لمكون Rigidbody الذي أنشأناه سابقًا في المشروع.

private Rigidbody rb; 

بعد ذلك مباشرة ، في وظيفة البدء ، نطلب من Unity ضبط مكون Rigidbody الحالي لكائن Player على rb .

 rb = GetComponent<Rigidbody>(); 

بعد ذلك ، سيبدو البرنامج النصي Player كما يلي:


شكل 29: نص لاعب في الوقت الحالي

الآن بعد تعيين قيمة rb ، نحتاج إلى السماح لكائن Player بالتحرك عندما نضغط على مفاتيح الأسهم. لتحريك الكائن ، سنستخدم الفيزياء ، مع تطبيق القوة على الكائن عندما يضغط المستخدم على مفاتيح الأسهم. للقيام بذلك ، ما عليك سوى إضافة التعليمات البرمجية التالية إلى وظيفة التحديث :

 if (Input.GetKey(KeyCode.UpArrow)) rb.AddForce(Vector3.forward * 20); if (Input.GetKey(KeyCode.DownArrow)) rb.AddForce(Vector3.back * 20); if (Input.GetKey(KeyCode.LeftArrow)) rb.AddForce(Vector3.left * 20); if (Input.GetKey(KeyCode.RightArrow)) rb.AddForce(Vector3.right * 20); 

بهذا نكون قد أكملنا نص المشغل . سيبدو النص النهائي كما يلي:


شكل 30: نص المشغل النهائي.

حفظ عملك والعودة إلى الوحدة. هذه المرة ، حدد برنامج الحرس النصي في نافذة الأصول . لجعل كود الحرس يعمل ، تحتاج إلى إضافة البناء باستخدام إلى أعلى البرنامج النصي.

 using UnityEngine.AI; 

بعد ذلك ، أعلن عن المتغيرات التالية فورًا بعد تعريف الفئة.

 public GameObject player; private NavMeshAgent navmesh; 

يتم استخدام كائن المشغل كقيمة متغير المشغل لكائن الحرس . سيكون مفيدًا في وقت لاحق عندما نأمر كائن الحرس بمطاردة اللاعب. ثم يتم التصريح عن متغير navmesh لتلقي مكون NavMeshAgent للكائن. سنستخدمه لاحقًا عندما يبدأ الحرس في مطاردة اللاعب بعد أن يعلم أن اللاعب يلمس كائن Looker . في وظيفة البدء ، نحتاج إلى تعيين قيمة متغير navmesh إلى مكون NavMesh للكائن:

 navmesh = GetComponent<NavMeshAgent>(); 

بعد ذلك ، في وظيفة التحديث ، نضيف سطرًا واحدًا من التعليمات البرمجية:

 navmesh.destination = player.transform.position; 

يحدد هذا الخط وجهة كائن الحرس . في حالتنا ، سيأخذ الموقع الحالي لكائن اللاعب وينتقل إلى هذه النقطة. بعد العملية ، سيتابع الكائن اللاعب باستمرار. السؤال هو ، كيف تتم عملية الاستجابة؟ لن يتم ترميزه في برنامج الحرس النصي ، ولكن في برنامج Looker . قبل الانتقال إلى البرنامج النصي Looker ، انظر إلى الشكل 31 للتحقق من كود البرنامج النصي Guard .


شكل 31: نص الحرس النهائي.

داخل Looker ، نحتاج مرة أخرى إلى التصريح عن المتغيرات التالية:

 public GameObject guard; private float reset = 5; private bool movingDown; 

بعد ذلك ، نعلق على وظيفة البدء ، والتي لا نحتاجها في هذا البرنامج النصي. دعنا ننتقل إلى وظيفة التحديث وإضافة التعليمات البرمجية التالية:

 if (movingDown == false) transform.position -= new Vector3(0, 0, 0.1f); else transform.position += new Vector3(0, 0, 0.1f); if (transform.position.z > 10) movingDown = false; else if (transform.position.z < -10) movingDown = true; reset -= Time.deltaTime; if (reset < 0) { guard.GetComponent<Guard>().enabled = false; GetComponent<SphereCollider>().enabled = true; } 

هذا هو المكان الذي تتم فيه الإجراءات الرئيسية للمشروع ، لذلك دعونا نحلل الشفرة. أولاً ، اعتمادًا على قيمة المتغير المنطقي moveDown ، فإن الكائن الذي يتم إرفاق هذا البرنامج النصي به سوف يتحرك لأعلى أو لأسفل. بمجرد أن يصل إلى نقطة معينة ، سيغير اتجاهه. بعد ذلك ، سيخفض Looker قيمة إعادة الضبط بناءً على الوقت الحقيقي. بمجرد أن يصبح المؤقت أقل من الصفر ، سيأخذ نص الحرس من كائن الحرس ويعطله ، وبعد ذلك يبدأ كائن الحرس في الانتقال إلى آخر موضع معروف للاعب حتى هذه اللحظة ، ثم يتوقف. يقوم Looker أيضًا بتشغيل المصادم مرة أخرى حتى تبدأ العملية بأكملها من جديد. الآن يبدو نصنا كما يلي:


شكل 32: سكريبت Looker

الحديث عن المصادمات: حان الوقت لإنشاء رمز صراع يعيد تعيين مؤقت Looker ويتضمن البرنامج النصي Guard . في وظيفة التحديث ، قم بإنشاء الكود التالي:

 private void OnCollisionEnter(Collision collision) { if (collision.gameObject.tag == "Player") { guard.GetComponent<Guard>().enabled = true; reset = 5; GetComponent<SphereCollider>().enabled = false; } } 

يتعرف OnCollisionEnter Unity تلقائيًا على أنه رمز تصادم ، وبالتالي ينفذه عند حدوث تصادم مع كائن آخر. في حالتنا ، يتحقق أولاً لمعرفة ما إذا كان الكائن المتصادم يحتوي على علامة Player . إذا لم يكن الأمر كذلك ، فإنه يتجاهل بقية التعليمات البرمجية. خلاف ذلك ، فإنه يقوم بتشغيل البرنامج النصي الحرس ، وتعيين مؤقت إعادة الضبط إلى 5 (أي خمس ثوان) ، وتعطيل المصادم الخاص به بحيث لا يزال بإمكان اللاعب التحرك من خلال الكائن وعدم التعثر عن طريق الخطأ في كائن Looker . تظهر الوظيفة في الشكل 33.


الشكل 33: رمز التصادم في Looker.

هذا كل رمز المشروع جاهز! يمكنك القيام ببعض الأشياء قبل الانتهاء من المشروع. حفظ كل العمل والعودة إلى الوحدة.

اكتمال المشروع


لإكمال المشروع ، نحتاج فقط إلى إرفاق نصوص بالكائنات المقابلة وتعيين العديد من المتغيرات. أولاً ، انتقل من نافذة التنقل إلى نافذة المفتش :


الشكل 34: الانتقال إلى نافذة المفتش.

بعد ذلك ، لنبدأ بكائن " المشغل" . حدده في نافذة التسلسل الهرمي ، ثم في الجزء السفلي من نافذة المفتش انقر على الزر إضافة مكون وأضف البرنامج النصي للمشغل . يؤدي هذا إلى إكمال كائن Player .


شكل 35: مكون البرنامج النصي للمشغل

بعد ذلك ، حدد كائن الحرس . كما كان من قبل ، قم بإرفاق البرنامج النصي الحرس للكائن. هذه المرة نحتاج أن نقول للحارس من هو اللاعب. للقيام بذلك ، اسحب كائن Player من التسلسل الهرمي إلى حقل Player الخاص بمكون البرنامج النصي Guard ، كما هو موضح في الشكل 36.


الشكل 36: جعل كائن المشغل قيمة حقل المشغل .

نحتاج أيضًا إلى تعطيل برنامج الحرس النصي. في مشروعنا ، سوف يطارد اللاعب اللاعب بعد تشغيل نصه. يجب تضمين نص الحرس هذا فقط بعد أن يلمس اللاعب كائن Looker . كل ما عليك فعله هو إلغاء تحديد المربع المجاور لنص الحرس (البرنامج النصي) في المكون:


شكل 37: تعطيل سكريبت الحرس

أخيرًا ، دعنا ننتقل إلى كائن Looker وإرفاق البرنامج النصي Looker إليه. هذه المرة ، سيحتاج كائن Looker إلى كائن Guard كقيمة لمتغير Guard الخاص به. تمامًا كما قمنا بتعيين كائن Player لمتغير Player في البرنامج النصي Guard ، سنفعل الشيء نفسه مع كائن Guard والنصي النصي Looker . اسحب الحرس من التسلسل الهرمي إلى حقل الحرس في البرنامج النصي لـ Looker . وانتهى المشروع! انقر على زر التشغيل في أعلى محرر الوحدة لاختبار مشروعك.


الشكل 38: اختبار المشروع.

حاول تحريك كائن Player إلى كائن Looker (تذكر أن النقل يتم باستخدام الأسهم!). لاحظ أنه بعد ذلك ، سيبدأ كائن الحرس في مطاردة اللاعب. سيستمر في المطاردة لمدة 5 ثوانٍ ، وبعد ذلك سيستسلم.


الشكل 39: مشروع مكتمل بالكامل قيد التنفيذ.

الخلاصة


هذا الذكاء الاصطناعي بسيط للغاية ، ولكن يمكن توسيعه بسهولة. لنفترض ، إذا تخيلنا أن كائن Looker عبارة عن كاميرا ، وأن الحارس ينظر من خلالها للعثور عليك ، فسيكون من المنطقي أن يمنح كائن الحرس زوجًا من عينيه. يمكن للاعب الاقتراب من الكاميرات ، ولكن يجب أن تأخذ بعين الاعتبار عيون الحارس. يمكنك أيضًا دمج هذا المشروع مع مفهوم العثور على المسار : امنح الحارس المسار الذي سيتبعه ، وبالتالي خلق بيئة أكثر إثارة للاهتمام للاعب.

يمكن تطوير مثل هذا الذكاء الاصطناعي البسيط بعدة طرق مختلفة. قد لا ترغب في القيام بأي شيء مما سبق وأن تقرر القيام بشيء خاص بك. أنصحك بالتجربة ، ربما سيكون لديك فكرة عن مشروع مثير للاهتمام ، يستحق الإنهاء.

Source: https://habr.com/ru/post/ar419641/


All Articles