تنفيذ Path Finder لوكلاء AI مع NavMesh

الصورة

اتباع المسار والسيطرة على حركة المرور


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

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

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

المتطلبات الفنية


يتطلب Unity 2017 مثبتًا على نظام يعمل بنظام التشغيل Windows 7 SP1 + أو 8 أو 10 أو Mac OS X 10.9+. لن تعمل التعليمات البرمجية في هذه المقالة على نظامي التشغيل Windows XP و Vista ، ولم يتم اختبار إصدارات خادم Windows و OS X.

يمكن العثور على ملفات التعليمات البرمجية لهذا المنشور على GitHub .

لمعرفة الكود عمليًا ، شاهد هذا الفيديو .

شبكة التنقل


دعنا نكتشف كيفية استخدام مولد شبكة التنقل المدمج في Unity ، والذي يمكن أن يبسط بشكل كبير البحث عن مسارات وكلاء AI. في المراحل الأولى من Unity 5.x ، أصبحت وظيفة NavMesh متاحة لجميع المستخدمين ، بما في ذلك أولئك الذين لديهم تراخيص الإصدار الشخصي ، على الرغم من أنها كانت في وقت سابق وظيفة لـ Unity Pro فقط. قبل إصدار 2017.1 ، تم تحديث النظام لتوفير سير عمل قائم على المكونات ، ولكن نظرًا لأنه يتطلب حزمة إضافية قابلة للتنزيل ، والتي كانت متاحة وقت كتابة هذا الإصدار فقط في إصدار المعاينة ، فإننا سنلتزم بسير العمل القياسي القائم على المشهد. لا تقلق ، مفاهيم كلا النهجين متشابهة ، وعندما يصل التنفيذ النهائي أخيرًا إلى 2017.x ، لا ينبغي أن تكون هناك تغييرات كبيرة.

تعرف على المزيد حول نظام مكونات NavMesh في Unity على GitHub .

الآن سوف نستكشف كل الاحتمالات التي يمكن أن يوفرها لنا هذا النظام. للبحث عن مسارات AI ، يجب تقديم المشهد بتنسيق محدد ؛ على خريطة ثنائية الأبعاد ، يتم استخدام شبكة (صفيف) ثنائية الأبعاد للبحث عن المسارات باستخدام خوارزمية A *. يحتاج عملاء منظمة العفو الدولية إلى معرفة مكان العوائق ، وخاصة العوائق الثابتة. التعامل مع التصادمات بين الأجسام المتحركة ديناميكيًا هو مشكلة أخرى يشار إليها عادة باسم سلوك التوجيه. تحتوي Unity على أداة مدمجة لتوليد NavMesh ، تمثل المشهد في سياق مناسب لوكلاء الذكاء الاصطناعي للعثور على المسار الأمثل للهدف. للبدء ، افتح مشروعًا تجريبيًا وانتقل إلى مشهد NavMesh.

بطاقة الدراسة


بعد فتح مشهد NavMesh التجريبي ، يجب أن يبدو في لقطة الشاشة:


عقبة ومنحدر المشهد

سيكون هذا هو وضع الحماية الخاص بنا لشرح واختبار وظائف نظام NavMesh. يشبه المخطط العام لعبة في نوع RTS (استراتيجية الوقت الحقيقي). نحن نقود دبابة زرقاء. انقر على نقاط مختلفة بحيث يتحرك الخزان باتجاههم. المؤشر الأصفر هو الهدف الحالي للدبابات.

ملاحة ثابتة


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


خاصية الملاحة الثابتة

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

تحميص شبكة ملاحة


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

عند فتح النافذة ، سترى علامات تبويب فردية. سيبدو شيء من هذا القبيل:


نافذة التنقل

في حالتنا ، تعرض لقطة الشاشة السابقة علامة التبويب Bake ، ولكن في المحرر الخاص بك ، يمكن تحديد أي علامة تبويب بشكل افتراضي.

لنلق نظرة على كل علامة تبويب ، بدءًا من اليسار والانتقال إلى اليمين. لنبدأ بعلامة التبويب الوكلاء ، والتي تبدو كما لو كانت لقطة الشاشة تظهر:


علامة تبويب الوكلاء

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

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

بعد ذلك ، لدينا علامة التبويب المناطق ، والتي تبدو مثل تلك الموضحة في لقطة الشاشة هذه:


كما ترى في لقطة الشاشة ، توفر Unity عدة أنواع من المجالات التي لا يمكن تغييرها: Walkable و Not Walkable و Jump . بالإضافة إلى التسمية وإنشاء مناطق جديدة ، يمكنك تعيين تكلفة هذه المناطق حول التنقل فيها.

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

ربما تكون علامة التبويب Bake الثالثة هي الأهم. يسمح لك بإنشاء NavMesh نفسه للمشهد. يجب أن تكون على دراية ببعض الخيارات. تبدو علامة التبويب Bake على النحو التالي :


علامة التبويب خبز

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

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

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


تظهر المناطق الزرقاء NavMesh. أدناه سنعود إلى هذا. في هذه الأثناء ، دعنا ننتقل إلى علامة التبويب الأخيرة - الكائن ، الذي يبدو كما يلي:


تُستخدم الأزرار الثلاثة الموضحة في لقطة الشاشة السابقة - All و Mesh Renderers و Terrains - كمرشحات المشهد. تكون مفيدة عند العمل في مشاهد معقدة مع العديد من الكائنات في التسلسل الهرمي. يؤدي تحديد خيار إلى تصفية النوع المطابق من التسلسل الهرمي ، مما يسهل تحديدها. يمكنك استخدام الأزرار لاستكشاف المشهد الخاص بك بحثًا عن الكائنات التي تريد تعليمها كملاحة ثابتة.

استخدام عامل Nav Mesh


الآن بعد أن أعددنا المشهد باستخدام NavMesh ، نحتاج إلى طريقة للوكيل لاستخدام هذه المعلومات. لحسن الحظ ، يحتوي Unity على مكون Nav Mesh Agent الذي يمكنك سحبه إلى شخصية. في مشهد المثال الخاص بنا ، يوجد كائن لعبة يسمى Tank ، والذي تم إرفاق مكون بالفعل به. انظر إلى التسلسل الهرمي وسترى شيئًا مثل هذا:


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

  • نوع الوكيل : هل تتذكر علامة التبويب الوكلاء في نافذة التنقل ؟ يمكن تحديد أنواع الوكيل القابلة للتعيين هنا.
  • Auto Traverse Off Mesh Link : يتيح هذا الخيار للوكلاء استخدام ميزة Off Mesh Links تلقائيًا ، والتي سنناقشها أدناه.
  • قناع المنطقة : هنا يمكنك تحديد المناطق التي تم تكوينها في علامة التبويب المناطق في نافذة التنقل .

هذا كل شيء. يقوم هذا المكون ب 90٪ من العمل الشاق من أجلنا: تمهيد الطريق وتجنب العقبات وما إلى ذلك. الشيء الوحيد الذي تحتاجه هو تحويل نقطة الهدف إلى الوكيل. دعونا نلقي نظرة على هذه المشكلة.

تحديد نقطة الهدف


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

هذه فئة بسيطة تقوم بثلاثة أشياء:

  • "يطلق" الشعاع من الكاميرا إلى وضع الماوس في العالم
  • تحديثات موقف علامة
  • تحديث خاصية الوجهة لجميع وكلاء NavMesh.

الرمز بسيط للغاية. الفصل كله كما يلي:

using UnityEngine; using UnityEngine.AI; public class Target : MonoBehaviour { private NavMeshAgent[] navAgents; public Transform targetMarker; private void Start () { navAgents = FindObjectsOfType(typeof(NavMeshAgent)) as NavMeshAgent[]; } private void UpdateTargets ( Vector3 targetPosition ) { foreach(NavMeshAgent agent in navAgents) { agent.destination = targetPosition; } } private void Update () { if(GetInput()) { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hitInfo; if (Physics.Raycast(ray.origin, ray.direction, out hitInfo)) { Vector3 targetPosition = hitInfo.point; UpdateTargets(targetPosition); targetMarker.position = targetPosition; } } } private bool GetInput() { if (Input.GetMouseButtonDown(0)) { return true; } return false; } private void OnDrawGizmos() { Debug.DrawLine(targetMarker.position, targetMarker.position + Vector3.up * 5, Color.red); } } 

تحدث الإجراءات التالية هنا: في طريقة البدء ، نقوم بتهيئة الصفيف navAgents باستخدام طريقة FindObjectsOfType () .

تمر طريقة UpdateTargets () عبر مصفوفة navAgents وتعين نقطة الهدف لها في Vector3 المحدد. هذا هو مفتاح الرمز. يمكنك استخدام أي آلية للحصول على نقطة الهدف ، ولكي ينتقل الوكيل إلى هناك ، ما عليك سوى تعيين حقل NavMeshAgent.destination ؛ سيقوم الوكيل بالباقي.

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

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

المكعبات الحمراء مرتفعة للغاية. المنحدر المؤدي إلى أعلى منصة حادة للغاية بالنسبة لإعدادات Max Slope ، ولا يمكن للوكيل تسلقه. توضح لقطات الشاشة التالية كيف تؤثر إعدادات Max Slope على NavMesh:


NavMesh مع منحدر أقصى = 45

إذا قمت بتغيير قيمة Max Slope إلى شيء مثل 51 ، ثم نقرت على زر Bake مرة أخرى لخبز NavMesh مرة أخرى ، فستكون النتائج كما يلي:


NavMesh مع منحدر أقصى = 51

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

التطبيق خارج شبكة الروابط


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


الدوائر الزرقاء مع خطوط الربط هي اتصالات.

يمكن للوحدة إنشاء هذه الروابط بطريقتين. أول ما اعتبرناه بالفعل. هل تتذكر قيمة Jump Jump في علامة التبويب Bake في نافذة التنقل ؟ تستخدم الوحدة تلقائيًا هذه القيمة لإنشاء هذه الارتباطات عند خبز NavMesh. حاول تغيير القيمة في مشهد الاختبار لدينا إلى 5 والخبز مرة أخرى. انظر - المنصات متصلة الآن؟ وذلك لأن الشبكات أصبحت الآن ضمن العتبة المحددة الجديدة.

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


يمكنك بالفعل مشاهدة ما يحدث ، ولكن دعنا نحلل العملية التي تسمح لهم بالاتصال. في حالتنا ، اتصلت بالكرة في البداية اليمنى ، والكرة في الطرف الأيسر. ستفهم قريبا لماذا. بعد ذلك ، أضفت مكون Off Mesh Link إلى النظام الأساسي على اليمين (بالنسبة إلى لقطة الشاشة السابقة). ستلاحظ أن المكون يحتوي على حقول البداية والنهاية . كما قد تخمن ، سنقوم بسحب المجالات التي تم إنشاؤها سابقًا إلى الفتحات المقابلة - مجال البداية في حقل البداية والمجال النهائي في مجال النهاية . سيبدو المفتش كما يلي:


تؤخذ قيمة تجاوز التكلفة في الاعتبار عند إعطائها قيمة موجبة. يطبق عامل التكلفة عند استخدام هذه العلاقة بدلاً من الطريق الأكثر فعالية من حيث التكلفة إلى الهدف.

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

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


كما ترى ، لا تزال الفجوة الأصغر متصلة تلقائيًا ، والآن لدينا اتصال جديد تم إنشاؤه بواسطة مكون Off Mesh Link بين المجالين. ابدأ تشغيل وضع Play وانقر فوق النظام الأساسي البعيد. كما هو متوقع ، يمكن للوكيل الآن الانتقال إلى النظام الأساسي غير المتصل:


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

هذا البرنامج التعليمي جزء من Unity 2017 Game AI Programming - Third Edition by Ray Barrera و Aung Sithu Kyaw و Thet Naing Swe.

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


All Articles