تطوير الذكاء الاصطناعي للوحدة: برنامج تعليمي لآلة الحالة المحدودة

نشرت: 2022-03-11

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

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

تشريح ولايات ميكرونيزيا الموحدة

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

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

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

تصور ولايات ميكرونيزيا الموحدة

لنفكر في مثال لعبة الأركيد الكلاسيكية Pac-Man. في الحالة الأولية للعبة (حالة "المطاردة") ، فإن الشخصيات غير القابلة للعب هي أشباح ملونة تلاحق اللاعب وتتفوق عليه في النهاية. تنتقل الأشباح إلى حالة الهروب عندما يأكل اللاعب حبيبة طاقة ويختبر زيادة في القوة ، ويكتسب القدرة على أكل الأشباح. تتهرب الأشباح ، التي أصبحت الآن زرقاء اللون ، من اللاعب حتى تنتهي مهلة الطاقة وتعود الأشباح إلى حالة المطاردة ، حيث يتم استعادة سلوكياتهم وألوانهم الأصلية.

يكون شبح Pac-Man دائمًا في إحدى حالتين: المطاردة أو التهرب. بطبيعة الحال ، يجب أن نقدم مرحلتين - أحدهما من المطاردة إلى التهرب ، والآخر من التهرب إلى المطاردة:

رسم بياني: على اليسار توجد حالة المطاردة. يؤدي السهم (الذي يشير إلى أن اللاعب أكل الحبيبات الكهربائية) إلى حالة التهرب على اليمين. السهم الثاني (الذي يشير إلى انتهاء مهلة الطاقة الكهربية) يؤدي إلى حالة المطاردة على اليسار.
التحولات بين حالات Pac-Man Ghost

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

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

قابلية التوسع

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

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

تنفيذ ولايات ميكرونيزيا الموحدة في الوحدة

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

قبل أن نضيف الذكاء الاصطناعي إلى هذا المشروع الأولي الجاهز للترجمة ، ضع في اعتبارك البنية المقترحة:

الرسم التخطيطي: سبعة مربعات تتصل ببعضها البعض ، موصوفة بترتيب الظهور ، من اليسار / أعلى: يشتمل المربع المسمى BaseStateMachine على + CurrentState: BaseState. BaseStateMachine يتصل بـ BaseState بسهم ثنائي الاتجاه. يشتمل المربع المسمى BaseState على + Execute (BaseStateMachine): باطل. BaseState يتصل BaseStateMachine مع سهم ثنائي الاتجاه. تتصل الأسهم أحادية الاتجاه من State و RemainInState بـ BaseState. يتضمن المربع المسمى الحالة + تنفيذ (BaseStateMachine): باطل ، + إجراءات: قائمة & lt ؛ إجراء & GT ؛ و + انتقال: قائمة & lt ؛ انتقال & GT ؛. الحالة تتصل بـ BaseState بسهم أحادي الاتجاه ، بالإجراء بسهم أحادي الاتجاه المسمى "1" ، وبالانتقال بسهم أحادي الاتجاه مكتوب عليه "1." يشتمل المربع المسمى RemainInState على + Execute (BaseStateMachine): باطل. يتصل RemainInState بـ BaseState بسهم أحادي الاتجاه. يتضمن المربع المسمى الإجراء + تنفيذ (BaseStateMachine): باطل. السهم أحادي الاتجاه المسمى "1" من State يتصل بالإجراء. يتضمن المربع المسمى Transition + Decide (BaseStateMachine): void و + TransitionDecision: Decision و + TrueState: BaseState و + FalseState: BaseState. يتصل الانتقال بـ Decision بسهم أحادي الاتجاه. السهم أحادي الاتجاه المسمى "1" من الحالة يتصل بـ Transition. يشتمل المربع المسمى القرار + Decide (BaseStateMachine): bool.
هيكل ولايات ميكرونيزيا الموحدة المقترحة

في لعبتنا النموذجية ، يقوم العدو (شخصية غير قابلة للعب ممثلة بكبسولة زرقاء) بدوريات. عندما يرى العدو اللاعب (يمثله كبسولة رمادية) ، يبدأ العدو في تتبع اللاعب:

رسم تخطيطي: خمسة مربعات متصلة ببعضها البعض ، موصوفة بترتيب الظهور ، من اليسار / أعلى: المربع المسمى باترول يتصل بالمربع المسمى IF لاعب في خط الرؤية بسهم أحادي الاتجاه ، والمربع المسمى Patrol Action مع سهم أحادي الاتجاه يسمى "الحالة". المربع المسمى IF player في خط البصر ، مع "قرار" إضافي ، أسفل المربع مباشرة. المربع المسمى IF player في خط البصر يتصل بالمربع المسمى Chase بسهم أحادي الاتجاه. سهم أحادي الاتجاه من المربع المسمى Patrol يتصل بالمربع المسمى IF player في خط الرؤية. يتصل المربع المسمى Chase بالمربع المسمى Chase Action بسهم أحادي الاتجاه يسمى "state". سهم أحادي الاتجاه من المربع المسمى IF لاعب في خط الرؤية يتصل بالمربع المسمى Chase. سهم سهم أحادي الاتجاه من المربع المسمى باترول يتصل بالمربع المسمى Patrol Action. سهم سهم أحادي الاتجاه من المربع المسمى Chase يتصل بالمربع المسمى Chase Action.
المكونات الأساسية لعينة لعبة الشبح FSM

على عكس Pac-Man ، لن يعود العدو في لعبتنا إلى الحالة الافتراضية ("الدورية") بمجرد أن يتبع اللاعب.

إنشاء الفصول

لنبدأ بإنشاء فصولنا. في مجلد scripts الجديد ، سنضيف جميع الكتل الإنشائية المعمارية المقترحة مثل نصوص C #.

تنفيذ فئة BaseStateMachine

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

يشير رمز BaseStateMachine إلى الحالة الحالية وينفذها لتنفيذ الإجراءات ومعرفة ما إذا كان هناك ما يبرر الانتقال:

 using UnityEngine; namespace Demo.FSM { public class BaseStateMachine : MonoBehaviour { [SerializeField] private BaseState _initialState; private void Awake() { CurrentState = _initialState; } public BaseState CurrentState { get; set; } private void Update() { CurrentState.Execute(this); } } }

تنفيذ فئة BaseState

حالتنا من النوع BaseState ، والتي نستمدها من ScriptableObject . يتضمن BaseState طريقة واحدة ، Execute ، واتخاذ BaseStateMachine كوسيطة لها وتمرير الإجراءات والانتقالات إليها. هكذا تبدو BaseState :

 using UnityEngine; namespace Demo.FSM { public class BaseState : ScriptableObject { public virtual void Execute(BaseStateMachine machine) { } } }

تطبيق RemainInState State والبقاء في الدولة

نشتق الآن فئتين من BaseState . أولاً ، لدينا فئة State ، التي تخزن الإشارات إلى الإجراءات والانتقالات ، وتتضمن قائمتين (واحدة للإجراءات ، والأخرى للانتقالات) ، وتتجاوز وتستدعي القاعدة Execute على الإجراءات والانتقالات:

 using System.Collections.Generic; using UnityEngine; namespace Demo.FSM { [CreateAssetMenu(menuName = "FSM/State")] public sealed class State : BaseState { public List<FSMAction> Action = new List<FSMAction>(); public List<Transition> Transitions = new List<Transition>(); public override void Execute(BaseStateMachine machine) { foreach (var action in Action) action.Execute(machine); foreach(var transition in Transitions) transition.Execute(machine); } } }

ثانيًا ، لدينا فئة RemainInState ، والتي تخبر FSM عندما لا تقوم بإجراء انتقال:

 using UnityEngine; namespace Demo.FSM { [CreateAssetMenu(menuName = "FSM/Remain In State", fileName = "RemainInState")] public sealed class RemainInState : BaseState { } }

لاحظ أن هذه الفئات لن يتم تجميعها حتى نضيف FSMAction و Decision و Transition .

تنفيذ فئة FSMAction

في الرسم التخطيطي المقترح لهندسة FSM ، يُطلق على فئة FSMAction الأساسية "الإجراء". ومع ذلك ، سننشئ فئة FSMAction الأساسية ونستخدم اسم FSMAction (نظرًا لأن Action قيد الاستخدام بالفعل بواسطة مساحة اسم System .NET).

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

تبدو فئة FSMAction المجردة كما يلي:

 using UnityEngine; namespace Demo.FSM { public abstract class FSMAction : ScriptableObject { public abstract void Execute(BaseStateMachine stateMachine); } }

تنفيذ Decision Transition

لإنهاء ولايات ميكرونيزيا الموحدة ، سنحدد فئتين إضافيتين. أولاً ، لدينا Decision ، فئة مجردة تحدد منها جميع القرارات الأخرى سلوكها المخصص:

 using UnityEngine; namespace Demo.FSM { public abstract class Decision : ScriptableObject { public abstract bool Decide(BaseStateMachine state); } }

الفئة الثانية ، Transition ، تحتوي على كائن Decision وحالتين:

  • حالة للانتقال إليها إذا كان Decision يسفر عن صواب.
  • حالة أخرى للانتقال إليها إذا أسفر Decision عن خطأ.

تبدو هكذا:

 using UnityEngine; namespace Demo.FSM { [CreateAssetMenu(menuName = "FSM/Transition")] public sealed class Transition : ScriptableObject { public Decision Decision; public BaseState TrueState; public BaseState FalseState; public void Execute(BaseStateMachine stateMachine) { if(Decision.Decide(stateMachine) && !(TrueState is RemainInState)) stateMachine.CurrentState = TrueState; else if(!(FalseState is RemainInState)) stateMachine.CurrentState = FalseState; } } }

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

إنشاء إجراءات وقرارات مخصصة

الآن ، مع إنجاز المهام الثقيلة ، نحن جاهزون لتنفيذ الإجراءات والقرارات المخصصة في مجلد scripts الجديد.

تنفيذ فصول Patrol Chase

عندما نقوم بتحليل المكونات الأساسية لنموذج مخطط FSM الخاص بنا ، نرى أن NPC الخاص بنا يمكن أن يكون في واحدة من حالتين:

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

يمكننا إعادة استخدام تنفيذ النقل الحالي عبر واجهة المستخدم الرسومية الخاصة بشركة Unity ، كما سنناقش لاحقًا. هذا يترك إجراءين ( PatrolAction و ChaseAction ) وقرارًا لنا للترميز.

يتجاوز إجراء حالة الدورية (المشتق من FSMAction الأساسي) طريقة Execute للحصول على عنصرين:

  1. PatrolPoints ، الذي يتتبع نقاط الدوريات.
  2. NavMeshAgent ، تنفيذ Unity للملاحة في الفضاء ثلاثي الأبعاد.

يتحقق التجاوز بعد ذلك مما إذا كان وكيل AI قد وصل إلى وجهته ، وإذا كان الأمر كذلك ، ينتقل إلى الوجهة التالية. تبدو هكذا:

 using Demo.Enemy; using Demo.FSM; using UnityEngine; using UnityEngine.AI; namespace Demo.MyFSM { [CreateAssetMenu(menuName = "FSM/Actions/Patrol")] public class PatrolAction : FSMAction { public override void Execute(BaseStateMachine stateMachine) { var navMeshAgent = stateMachine.GetComponent<NavMeshAgent>(); var patrolPoints = stateMachine.GetComponent<PatrolPoints>(); if (patrolPoints.HasReached(navMeshAgent)) navMeshAgent.SetDestination(patrolPoints.GetNext().position); } } }

قد نرغب في التفكير في تخزين مكونات PatrolPoints و NavMeshAgent . سيسمح لنا التخزين المؤقت بمشاركة ScriptableObject s للإجراءات بين الوكلاء دون تأثير تشغيل GetComponent على كل استعلام لجهاز الحالة المحدودة.

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

 using System; using System.Collections.Generic; using UnityEngine; namespace Demo.FSM { public class BaseStateMachine : MonoBehaviour { [SerializeField] private BaseState _initialState; private Dictionary<Type, Component> _cachedComponents; private void Awake() { CurrentState = _initialState; _cachedComponents = new Dictionary<Type, Component>(); } public BaseState CurrentState { get; set; } private void Update() { CurrentState.Execute(this); } public new T GetComponent<T>() where T : Component { if(_cachedComponents.ContainsKey(typeof(T))) return _cachedComponents[typeof(T)] as T; var component = base.GetComponent<T>(); if(component != null) { _cachedComponents.Add(typeof(T), component); } return component; } } }

مثل نظيرتها PatrolAction ، تتجاوز فئة ChaseAction طريقة Execute للحصول على مكونات PatrolPoints و NavMeshAgent . على النقيض من ذلك ، بعد التحقق مما إذا كان وكيل الذكاء الاصطناعي قد وصل إلى وجهته ، يحدد إجراء فئة ChaseAction الوجهة إلى Player.position :

 using Demo.Enemy; using Demo.FSM; using UnityEngine; using UnityEngine.AI; namespace Demo.MyFSM { [CreateAssetMenu(menuName = "FSM/Actions/Chase")] public class ChaseAction : FSMAction { public override void Execute(BaseStateMachine stateMachine) { var navMeshAgent = stateMachine.GetComponent<NavMeshAgent>(); var enemySightSensor = stateMachine.GetComponent<EnemySightSensor>(); navMeshAgent.SetDestination(enemySightSensor.Player.position); } } }

تنفيذ فئة InLineOfSightDecision

القطعة الأخيرة هي فئة InLineOfSightDecision ، والتي ترث Decision الأساسي وتحصل على مكون EnemySightSensor للتحقق مما إذا كان اللاعب في مرمى نظر NPC:

 using Demo.Enemy; using Demo.FSM; using UnityEngine; namespace Demo.MyFSM { [CreateAssetMenu(menuName = "FSM/Decisions/In Line Of Sight")] public class InLineOfSightDecision : Decision { public override bool Decide(BaseStateMachine stateMachine) { var enemyInLineOfSight = stateMachine.GetComponent<EnemySightSensor>(); return enemyInLineOfSight.Ping(); } } }

إرفاق السلوكيات بالدول

نحن مستعدون أخيرًا لإرفاق سلوكيات بوكيل Enemy . يتم إنشاؤها في نافذة مشروع محرر الوحدة.

إضافة Patrol Chase

دعونا ننشئ حالتين ونطلق عليهما اسم "باترول" و "تشيس":

  • انقر بزر الماوس الأيمن> إنشاء> FSM> State

بينما هنا ، لنقم أيضًا بإنشاء كائن RemainInState :

  • انقر بزر الماوس الأيمن> إنشاء> FSM> البقاء في الحالة

حان الوقت الآن لإنشاء الإجراءات التي قمنا بترميزها للتو:

  • انقر بزر الماوس الأيمن> إنشاء> FSM> إجراء> باترول
  • انقر بزر الماوس الأيمن> إنشاء> FSM> إجراء> مطاردة

لترميز Decision :

  • انقر بزر الماوس الأيمن> إنشاء> FSM> القرارات> في خط البصر

لتمكين الانتقال من PatrolState إلى ChaseState ، فلنقم أولاً بإنشاء كائن انتقالي قابل للبرمجة:

  • انقر بزر الماوس الأيمن> إنشاء> FSM> انتقال
  • اختر الاسم الذي يعجبك. دعوت العدو المرقط.

سنقوم بملء نافذة المفتش الناتجة على النحو التالي:

تتضمن شاشة Spotted Enemy (Transition) أربعة أسطر: تم تعيين قيمة البرنامج النصي على "Transition" وهي غير نشطة. تم تعيين قيمة القرار على "LineOfSightDecision (في خط البصر)." تم تعيين قيمة True State على "ChaseState (State)." تم تعيين قيمة False State على "RemainInState (Remain In State)."
ملء نافذة مفتش العدو المرقط (الانتقال)

ثم نكمل حوار مفتش Chase State على النحو التالي:

تبدأ شاشة Chase State (State) بالتسمية "Open". بجانب التسمية "البرنامج النصي" يتم تحديد "الحالة". بجانب تسمية "الإجراء" ، يتم تحديد "1". من القائمة المنسدلة "Action" ، يتم تحديد "Element 0 Chase Action (Chase Action)". هناك علامة زائد وعلامة ناقص تليها. بجانب تسمية "انتقالات" ، يتم تحديد "0". من القائمة المنسدلة "الانتقالات" ، يتم عرض "القائمة فارغة". هناك علامة زائد وعلامة ناقص تليها.
ملء نافذة مفتش Chase State

بعد ذلك ، سنكمل مربع حوار حالة الدوريات:

تبدأ شاشة حالة (حالة) الدورية بعلامة "فتح". بجانب التسمية "البرنامج النصي" يتم تحديد "الحالة". بجانب تسمية "الإجراء" ، يتم تحديد "1". من القائمة المنسدلة "الإجراء" ، يتم تحديد "العنصر 0 إجراء دورية (إجراء دورية)". ويتبع ذلك علامة زائد وناقص. بجانب تسمية "انتقالات" ، يتم تحديد "1". من القائمة المنسدلة "الانتقالات" ، يتم عرض "العنصر 0 SpottedEnemy (الانتقال)". هناك علامة زائد وعلامة ناقص تليها.
ملء نافذة مفتش الدولة الدورية

أخيرًا ، سنضيف مكون BaseStateMachine إلى كائن العدو: في نافذة مشروع Unity Editor ، افتح أصل SampleScene ، وحدد كائن العدو من لوحة Hierarchy ، وفي نافذة المفتش ، حدد Add Component> Base State Machine :

شاشة Base State Machine (Script): بجانب تسمية "Script" باللون الرمادي ، يتم تحديد "BaseStateMachine" وإلغاء تنشيطه. بجانب تسمية "الحالة الأولية" ، يتم تحديد "PatrolState (State)".
إضافة مكون آلة الحالة الأساسية (البرنامج النصي)

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

أنت الآن جاهز للعب نموذج اللعبة ولاحظ أن العدو سوف يتبع اللاعب عندما يخطو اللاعب إلى خط رؤية العدو.

استخدام ولايات ميكرونيزيا الموحدة لخلق تجربة مستخدم ممتعة وتفاعلية

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

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