في عملية برمجة الكيانات داخل اللعبة ، تنشأ المواقف عندما يتعين عليهم التصرف في ظروف مختلفة بطرق مختلفة ، مما يشير إلى استخدام 
الحالات .
ولكن إذا قررت استخدام القوة الغاشمة ، فسوف تتحول الشفرة بسرعة إلى فوضى متشابكة مع العديد من العبارات المتداخلة.
للحصول على حل رشيق لهذه المشكلة ، يمكنك استخدام نمط تصميم الحالة. سنكرس هذا البرنامج التعليمي له!
من البرنامج التعليمي لك:
- تعلم أساسيات قالب الولاية في الوحدة.
- سوف تتعلم ما هي آلة الدولة ومتى تستخدمها.
- تعرف على كيفية استخدام هذه المفاهيم للتحكم في حركة شخصيتك.
ملاحظة : هذا البرنامج التعليمي مخصص للمستخدمين المتقدمين ؛ من المفترض أنك تعرف بالفعل كيفية العمل في الوحدة ولديك مستوى متوسط من المعرفة بـ C #. بالإضافة إلى ذلك ، يستخدم هذا البرنامج التعليمي Unity 2019.2 و C # 7.
الحصول على العمل
تحميل 
مواد المشروع . قم بفك ضغط 
الملف المضغوط وفتح مشروع 
بدء التشغيل في الوحدة.
هناك عدة مجلدات في المشروع ستساعدك على البدء. يحتوي المجلد 
Assets / RW على مجلدات 
Animations و 
Materials و 
Models و 
Prefabs و 
Resources و 
Scenes و 
Scripts و 
Sounds ، المسماة وفقًا للموارد التي تحتوي عليها.
لإكمال البرنامج التعليمي ، سنعمل فقط مع 
المشاهد والبرامج 
النصية .
انتقل إلى 
RW / Scenes وافتح 
Main . في وضع اللعبة ، سترى شخصية في غطاء محرك السيارة داخل قلعة من القرون الوسطى.
انقر فوق " 
تشغيل" ولاحظ كيفية تحرك 
الكاميرا لتناسب إطار 
الحرف . في الوقت الحالي ، في لعبتنا الصغيرة لا توجد تفاعلات ، سنعمل عليها في البرنامج التعليمي.
استكشف الشخصية
في 
التسلسل الهرمي ، حدد 
الحرف . تحقق من 
المفتش . سترى 
مكونًا بنفس الاسم يحتوي على منطق التحكم في 
الأحرف .
افتح 
Character.cs الموجود في 
RW / Scripts .
ينفذ البرنامج العديد من الإجراءات ، لكن معظمها ليس مهمًا بالنسبة لنا. الآن ، دعنا ننتبه إلى الطرق التالية.
- Move: يتحرك الحرف ، مستقبلاً قيم نوع تعويم- speedمثل سرعة الحركة و- rotationSpeedبسرعة السرعة الزاوية.
- ResetMoveParams:- ResetMoveParamsهذه الطريقة تعيين المعلمات المستخدمة لتحريك الحركة والسرعة الزاوية للحرف. يتم استخدامه فقط للتنظيف.
- SetAnimationBool: يقوم بتعيين- paramالحركة الأساسية من النوع Bool إلى القيمة.
- CheckCollisionOverlap: يستقبل- Vector3وإرجاع- boolيحدد ما إذا كان هناك أي مصادمات داخل نصف القطر المحدد من- TriggerAnimation:- TriggerAnimation- paramالرسوم المتحركة- TriggerAnimation.
- ApplyImpulse:- ApplyImpulseنبضة على الحرف تساوي- forceمعلمة الإدخال- forceالنوع- Vector3.
أدناه سترى هذه الأساليب. في البرنامج التعليمي لدينا ، محتوياتها والعمل الداخلي ليست مهمة.
ما هي آلات الدولة
جهاز الحالة هو مفهوم تقوم فيه الحاوية بتخزين حالة شيء ما في لحظة زمنية معينة. استنادًا إلى بيانات المدخلات ، يمكن أن يوفر استنتاجًا اعتمادًا على الحالة الحالية ، ويمرر هذه العملية إلى حالة جديدة. آلات الدولة يمكن أن تمثل على شكل 
مخطط الدولة . يتيح لك إعداد مخطط الحالة التفكير في جميع الحالات المحتملة للنظام والتحولات بينهما.
آلات الدولة
آلات الحالة المحدودة أو 
FSM (آلة الحالة المحدودة) هي واحدة من أربع مجموعات رئيسية من 
الآلات . Automata هي نماذج مجردة من الآلات البسيطة. يتم دراستها في إطار 
نظرية الأتمتة - الفرع النظري لعلوم الكمبيوتر.
باختصار:
- يتكون FSM من كمية محدودة من الشرط . في أي وقت معين ، واحدة فقط من هذه الحالات نشطة .
- تحدد كل ولاية الحالة التي ستدخل فيها كإخراج استنادًا إلى تسلسل المعلومات الواردة الواردة .
- تصبح حالة الإخراج الحالة النشطة الجديدة. بمعنى آخر ، هناك انتقال بين الدول .
لفهم ذلك بشكل أفضل ، ضع في اعتبارك طابع لعبة النظام الأساسي الموجودة على أرض الواقع. الشخصية في الحالة 
الدائمة . ستكون هذه حالته 
النشطة حتى يضغط اللاعب على الزر حتى تقفز الشخصية.
تعرّف الحالة 
الدائمة الضغط على زر 
كمدخل هام ، 
وكمخرج ، تنتقل إلى حالة 
القفز .
افترض أن هناك عددًا معينًا من حالات الحركة هذه وأن الشخصية لا يمكن أن تكون إلا في إحدى الولايات في وقت واحد. هذا مثال على FSM.
آلات الدولة الهرمية
النظر في منهاج باستخدام FSM ، حيث تشترك عدة ولايات في منطق الفيزياء المشترك. على سبيل المثال ، يمكنك التنقل والقفز في 
وضعي Crouching and 
Standing . في هذه الحالة ، تؤدي عدة متغيرات واردة إلى نفس السلوك وإخراج المعلومات لحالتين مختلفتين.
في مثل هذه الحالة ، سيكون من المنطقي تفويض السلوك العام إلى دولة أخرى. لحسن الحظ ، يمكن تحقيق ذلك باستخدام آلات الحالة 
الهرمية .
في FSM الهرمي ، هناك 
بدائل تفويض المعلومات الواردة 
الخام إلى 
بدائلهم . وهذا بدوره يتيح لك تقليل حجم وتعقيد FSM بأمان ، مع الحفاظ على منطقها.
قالب الحالة
في كتابهم " 
أنماط التصميم: عناصر من البرامج الموجهة للكائنات القابلة لإعادة الاستخدام" ، حدد إريك جاما وريتشارد هيلم ورالف جونسون وجون فليسيديس ( 
عصابة الأربعة ) 
مهمة قالب الحالة على النحو التالي:
يجب أن يسمح للكائن بتغيير سلوكه عندما تتغير حالته الداخلية. في هذه الحالة ، يبدو أن الكائن قد غير فئته ".
لفهم هذا بشكل أفضل ، خذ بعين الاعتبار المثال التالي:
- يتم إرفاق البرنامج النصي الذي يتلقى المعلومات الواردة عن منطق الحركة بكيان في اللعبة.
- يخزن هذا الفصل متغير الحالة الحالي الذي يشير ببساطة إلى مثيل لفئة الحالة .
- يتم تفويض المعلومات الواردة إلى هذه الحالة الحالية ، والتي تعالجها وتخلق سلوكًا محددًا في حد ذاته. كما أنه يتعامل مع التحولات الدولة المطلوبة.
لذلك ، نظرًا لحقيقة أنه في أوقات مختلفة يشير متغير 
الحالة الحالي إلى حالات مختلفة ، يبدو أن فئة البرنامج النصي نفسها تتصرف بشكل مختلف. هذا هو جوهر قالب "الحالة".
في مشروعنا ، سوف تتصرف فئة 
الأحرف المذكورة أعلاه بشكل مختلف اعتمادًا على حالات مختلفة. لكننا نحتاجه أن يتصرف بنفسه!
في الحالة العامة ، هناك ثلاث نقاط أساسية لكل فئة ولاية تسمح بسلوك الدولة ككل:
- الإدخال : هذه هي اللحظة التي يدخل فيها الكيان إلى حالة ويقوم بتنفيذ الإجراءات التي يجب القيام بها مرة واحدة فقط عند دخول الدولة.
- خروج : على غرار المدخلات - يتم تنفيذ جميع عمليات إعادة الضبط هنا ، والتي يجب تنفيذها فقط قبل أن تتغير الحالة.
- حلقة التحديث : فيما يلي منطق التحديث الأساسي الذي يعمل في كل إطار. يمكن تقسيمها إلى عدة أجزاء ، على سبيل المثال ، دورة لتحديث الفيزياء ودورة لمعالجة إدخال اللاعب.
تحديد آلة الدولة والدولة
انتقل إلى 
RW / البرامج النصية وفتح 
StateMachine.cs .
آلة الحالة ، كما قد تتخيل ، توفر تجريدًا لآلة الحالة. لاحظ أن 
CurrentState بشكل صحيح داخل هذه الفئة. سيتم تخزين ارتباط إلى حالة الجهاز الحالة النشطة الحالية.
الآن ، لتحديد مفهوم 
الحالة ، دعنا نذهب إلى 
RW / Scripts وفتح البرنامج النصي 
State.cs في IDE.
الحالة عبارة عن فئة تجريدية سنستخدمها 
كنموذج تستمد منه جميع 
فئات حالات المشروع. جزء من الكود الموجود في مواد المشروع جاهز بالفعل.
يعرض 
DisplayOnUI فقط اسم الحالة الحالية في واجهة المستخدم على الشاشة. لا تحتاج إلى معرفة أجهزتها الداخلية ، فقط أدرك أنها تستقبل 
UIManager.Alignment نوع 
UIManager.Alignment كمعلمة إدخال ، والتي يمكن أن تكون على 
Left أو 
Right . يعتمد عرض اسم الحالة في أسفل أو يسار الشاشة على ذلك.
بالإضافة إلى ذلك ، هناك نوعان من المتغيرات المحمية ، 
character و 
stateMachine . يشير متغير 
character إلى مثيل لفئة 
Character ، ويشير 
stateMachine إلى مثيل 
لجهاز الحالة المرتبط بالحالة.
عند إنشاء مثيل حالة ، فإن المُنشئ يربط بين 
character و 
stateMachine .
يمكن أن يكون لكل حالة من حالات 
Character في المشهد مجموعة خاصة بها من الحالات وأجهزة الحالة.
الآن أضف الطرق التالية إلى 
State.cs واحفظ الملف:
 public virtual void Enter() { DisplayOnUI(UIManager.Alignment.Left); } public virtual void HandleInput() { } public virtual void LogicUpdate() { } public virtual void PhysicsUpdate() { } public virtual void Exit() { } 
تحدد هذه الطرق الافتراضية نقاط الحالة الرئيسية الموضحة أعلاه. عندما 
تقوم آلة الحالة بالانتقال بين الحالات ، فإننا ندعو 
Exit للحالة السابقة 
Enter الحالة النشطة الجديدة.
HandleInput و 
LogicUpdate و 
LogicUpdate معًا 
حلقة تحديث . 
HandleInput يعالج إدخال اللاعب. 
LogicUpdate يعالج المنطق الأساسي ، بينما 
PhyiscsUpdate يعالج 
PhyiscsUpdate المنطق والفيزياء.
الآن افتح 
StateMachine.cs مرة أخرى ، أضف الطرق التالية وحفظ الملف:
 public void Initialize(State startingState) { CurrentState = startingState; startingState.Enter(); } public void ChangeState(State newState) { CurrentState.Exit(); CurrentState = newState; newState.Enter(); } 
Initialize تكوين جهاز الحالة عن طريق تعيين 
CurrentState startingState State واستدعاء 
Enter لذلك. هذا تهيئة جهاز الحالة ، لأول مرة تعيين الحالة النشطة.
ChangeState يعالج التحولات 
الدولة . وهو يستدعي 
Exit لـ 
CurrentState القديم قبل استبدال 
newState بـ 
newState . في النهاية ، فإنه يستدعي 
Enter for 
newState .
وبالتالي ، وضعنا 
آلة الدولة والدولة .
خلق الدول الحركة
ألقِ نظرة على مخطط الحالة التالي ، والذي يوضح 
حالات الحركة المختلفة لجوهر اللاعب داخل اللعبة. في هذا القسم ، نطبق قالب "الحالة" 
للحركة الموضحة في شكل 
FSM :
انتبه إلى حالات الحركة ، وهي 
الدائمة والالتفاف والقفز ، وكذلك كيف تسبب البيانات الواردة انتقالات بين الولايات. هذا هو هرمي FSM فيه 
Grounded هو حالة فرعية لـ 
Ducking و 
Stand- الولايات الفرعية .
العودة إلى الوحدة 
وانتقل إلى 
RW / البرامج النصية / الولايات . هناك ستجد العديد من ملفات C # مع أسماء تنتهي في 
الحالة .
يعرّف كل من هذه الملفات فئة واحدة ، كل منها موروث من 
State . لذلك ، تحدد هذه الفئات الحالات التي سنستخدمها في المشروع.
الآن افتح 
Character.cs من المجلد 
RW / Scripts .
قم بالتمرير أعلى ملف 
#region Variables وأضف الكود التالي:
 public StateMachine movementSM; public StandingState standing; public DuckingState ducking; public JumpingState jumping; 
تشير هذه 
movementSM إلى آلة الحالة التي تعالج منطق الحركة لمثيل 
Character . أضفنا أيضًا روابط إلى ثلاث ولايات نطبقها لكل نوع من أنواع الحركة.
انتقل إلى 
#region MonoBehaviour Callbacks في نفس الملف. أضف طرق 
MonoBehaviour التالية ثم احفظها
 private void Start() { movementSM = new StateMachine(); standing = new StandingState(this, movementSM); ducking = new DuckingState(this, movementSM); jumping = new JumpingState(this, movementSM); movementSM.Initialize(standing); } private void Update() { movementSM.CurrentState.HandleInput(); movementSM.CurrentState.LogicUpdate(); } private void FixedUpdate() { movementSM.CurrentState.PhysicsUpdate(); } 
- في Startيقوم الكود بإنشاء مثيل لـ State Machine ويعينه إلىmovementSM، كما أنه ينشئ حالات حركة مختلفة. عند إنشاء كل حالة من حالات الحركة ، نقوم بتمرير الإشارات إلى مثيلCharacterباستخدامthis، وكذلك مثيل motionSM. في النهاية ، نحن ندعوInitializemovementSMSM وتمريرStandingكحالة أولية.
- في طريقة Update، ندعوHandleInputوLogicUpdateإلىCurrentStateلجهازLogicUpdate. وبالمثل ، فيFixedUpdateندعوFixedUpdateلـآلةmovementSM. في جوهرها ، هذا تفويض المهام إلى حالة نشطة. هذا هو معنى قالب "الحالة".
الآن نحن بحاجة إلى ضبط السلوك داخل كل حالة من حالات الحركة. تستعد لنفسك ، سيكون هناك الكثير من التعليمات البرمجية!
شركة دائمة
العودة إلى 
RW / البرامج النصية / الدول في نافذة المشروع.
افتح 
Grounded.cs ولاحظ أن هذه الفئة بها مُنشئ يطابق مُنشئ 
State . هذا منطقي لأن هذه الفئة ترث منه. سترى نفس الشيء في جميع فئات 
الدولة الأخرى.
أضف الكود التالي:
 public override void Enter() { base.Enter(); horizontalInput = verticalInput = 0.0f; } public override void Exit() { base.Exit(); character.ResetMoveParams(); } public override void HandleInput() { base.HandleInput(); verticalInput = Input.GetAxis("Vertical"); horizontalInput = Input.GetAxis("Horizontal"); } public override void PhysicsUpdate() { base.PhysicsUpdate(); character.Move(verticalInput * speed, horizontalInput * rotationSpeed); } 
إليك ما يحدث هنا:
- نعيد تعريف أحد الأساليب الافتراضية المحددة في الفئة الأصل. للحفاظ على جميع الوظائف التي قد تكون موجودة في الأصل ، ندعو الطريقة baseبنفس الاسم من كل طريقة تجاوز. هذا قالب مهم سنستمر في استخدامه.
- السطر التالي ، EnterيعينhorizontalInputوverticalInputقيمهم الافتراضية.
- داخل Exitكما ذكرنا أعلاه ، ندعو طريقةResetMoveParamsإلى إعادةResetMoveParamsعند التغيير إلى حالة أخرى.
- في أسلوب HandleInput، تقوم متغيراتhorizontalInputوverticalInputHandleInputقيم محاور الإدخال الأفقية والعمودية. بفضل هذا ، يمكن للاعب التحكم في الشخصية باستخدام المفاتيح W و A و S و D.
- في PhysicsUpdateنجري مكالمةMove، لتمرير متغيرات المدخلاتhorizontalInputverticalInputمضروبة بالسرعات المقابلة. فيspeedالمتغيرةspeedيتم تخزين سرعة الحركة ، وفيrotationSpeed، السرعة الزاوية.
افتح الآن 
Standing.cs وانتبه إلى حقيقة أنه يرث من 
Grounded . حدث ذلك لأنه ، كما قلنا أعلاه ، فإن 
Standing هو بديل لـ 
Grounded . هناك طرق مختلفة لتنفيذ هذه العلاقة ، لكن في هذا البرنامج التعليمي نستخدم الوراثة.
أضف طرق 
override التالية واحفظ البرنامج النصي:
 public override void Enter() { base.Enter(); speed = character.MovementSpeed; rotationSpeed = character.RotationSpeed; crouch = false; jump = false; } public override void HandleInput() { base.HandleInput(); crouch = Input.GetButtonDown("Fire3"); jump = Input.GetButtonDown("Jump"); } public override void LogicUpdate() { base.LogicUpdate(); if (crouch) { stateMachine.ChangeState(character.ducking); } else if (jump) { stateMachine.ChangeState(character.jumping); } } 
- في Enterنقوم بتكوين المتغيرات الموروثة منGrounded. قم بتطبيقMovementSpeedوRotationSpeedspeedوrotationSpeed. ثم يرتبطون ، على التوالي ، بالسرعة الطبيعية للحركة والسرعة الزاوية المخصصة لجوهر الشخصية.
 
 بالإضافة إلى ذلك ، يتم إعادة تعيين المتغيرات لتخزينcrouchوإدخالjumpإلى خطأ.
- داخل HandleInput، تخزن متغيراتcrouchjumpإدخال اللاعبHandleInputوالقفزات. إذا ضغط المشغل في المشهد الرئيسي على مفتاح Shift ، فسيتم ضبط القرفصاء على true. وبالمثل ، يمكن للاعب استخدام مفتاح Spacejump.
- في LogicUpdateنتحقق من متغيراتjumpلنوعbool. إذا كانcrouchصحيحًا ،crouchإلىcharacter.ducking. إذا كانتjumpصحيحة ، فستتغير الحالة إلىcharacter.jumping.
احفظ المشروع وقم بتجميعه ، ثم انقر فوق " 
تشغيل" . يمكنك التنقل في مكان الحادث باستخدام المفاتيح 
W و 
A و 
S و 
D. إذا حاولت الضغط على 
Shift أو 
Space ، فسيحدث سلوك غير متوقع ، لأن الحالات المقابلة لم يتم تنفيذها بعد.
حاول التحرك تحت كائنات الجدول. سترى أنه بسبب ارتفاع مصادم الشخصية هذا غير ممكن. لكي تقوم الشخصية بهذا ، تحتاج إلى إضافة سلوك القرفصاء.
نحن نتسلق تحت الطاولة
افتح البرنامج النصي 
Ducking.cs . لاحظ أن 
Ducking يرث أيضًا من فئة 
Grounded لنفس الأسباب التي 
Standing . أضف طرق 
override التالية واحفظ البرنامج النصي:
 public override void Enter() { base.Enter(); character.SetAnimationBool(character.crouchParam, true); speed = character.CrouchSpeed; rotationSpeed = character.CrouchRotationSpeed; character.ColliderSize = character.CrouchColliderHeight; belowCeiling = false; } public override void Exit() { base.Exit(); character.SetAnimationBool(character.crouchParam, false); character.ColliderSize = character.NormalColliderHeight; } public override void HandleInput() { base.HandleInput(); crouchHeld = Input.GetButton("Fire3"); } public override void LogicUpdate() { base.LogicUpdate(); if (!(crouchHeld || belowCeiling)) { stateMachine.ChangeState(character.standing); } } public override void PhysicsUpdate() { base.PhysicsUpdate(); belowCeiling = character.CheckCollisionOverlap(character.transform.position + Vector3.up * character.NormalColliderHeight); } 
- داخل Enterالمعلمة التي تسبب تبديل حركات القرفصاء على كراوتش ، والتي تمكن الرسوم المتحركة القرفصاء.character.CrouchRotationSpeedتخصيص خصائصcharacter.CrouchSpeedوcharacter.CrouchRotationSpeedلقيمspeedrotation، والتي تُرجع الحركة والسرعة الزاوية للحرف عند تحريك القرفصاء .
 
 character.CrouchColliderHeightالتالي. يعيّن CrouchColliderHeight حجم مصادم الحرف ، والذي يُرجع ارتفاع مصادم الارتفاع المرغوب فيه عند القرفصاء. في النهاية ،belowCeilingإعادة تعيينbelowCeilingإلى "خطأ".
- داخل Exitيتم تعيين المعلمة الرسوم المتحركة القرفصاء على خطأ. هذا تعطيل الرسوم المتحركة القرفصاء. ثم يتم تعيين ارتفاع الاصطدام العادي ، يتم إرجاعها بواسطةcharacter.NormalColliderHeight.
- بداخل HandleInputيعينcrouchHeldالمتغيرة قيمة إدخال المشغل. في المشهد الرئيسي ، يؤدي عقد Shift إلىcrouchHeldإلى حقيقة.
- داخل belowCeilingتعيين قيمة المتغيرbelowCeilingعن طريق تمرير نقطة بتنسيقVector3برأس كائن لعبة الحرف إلى أسلوبCheckCollisionOverlap. إذا كان هناك تصادم بالقرب من هذه النقطة ، فهذا يعني أن الشخصية تحت سقف ما.
- داخليًا ، يتحقق crouchHeldأوbelowCeiling. إذا لم يكن أي منها صحيحًا ، فعندئذٍ يتغير moveSM.CurrentState إلىcharacter.standing.
بناء المشروع وانقر على 
اللعب . الآن يمكنك التحرك في جميع أنحاء مكان الحادث. إذا قمت بالضغط على 
Shift ، فسوف تجلس الشخصية ويمكنك التحرك في القرفصاء.
يمكنك أيضا الصعود تحت المنصة. إذا قمت بإطلاق 
Shift أثناء وجود المنصات ، فستظل الشخصية في وضع القرفصاء حتى يغادر ملجأه.
ترتفع!
افتح 
Jumping.cs . سترى طريقة تسمى 
Jump . لا تقلق بشأن كيفية عملها ؛ يكفي أن نفهم أنه يتم استخدامه بحيث يمكن للشخصية القفز مع مراعاة الفيزياء والرسوم المتحركة.
الآن إضافة أساليب 
override المعتاد وحفظ البرنامج النصي
 public override void Enter() { base.Enter(); SoundManager.Instance.PlaySound(SoundManager.Instance.jumpSounds); grounded = false; Jump(); } public override void LogicUpdate() { base.LogicUpdate(); if (grounded) { character.TriggerAnimation(landParam); SoundManager.Instance.PlaySound(SoundManager.Instance.landing); stateMachine.ChangeState(character.standing); } } public override void PhysicsUpdate() { base.PhysicsUpdate(); grounded = character.CheckCollisionOverlap(character.transform.position); } 
- من الداخل EnterSoundManagerبتشغيل صوت القفزة. ثمgroundedإعادة تعيينgroundedإلى قيمتها الافتراضية. في النهاية ، يسمىJump.
- داخل PhysicsUpdateنقطةPhysicsUpdateبجانب أرجل الشخصية إلىCheckCollisionOverlap، مما يعني أنه عندما تكون الشخصية على الأرض ، سيتم ضبطgroundedعلى صواب.
- في LogicUpdate، إذا كانتLogicUpdateصحيحة ، فإننا ندعوTriggerAnimationلتمكين الرسوم المتحركة للهبوط ، ويتم تشغيل صوت الهبوط ،TriggerAnimationإلىcharacter.standing.
لذلك ، أكملنا التنفيذ الكامل لنزوح FSM باستخدام 
قالب "الحالة" . بناء المشروع وتشغيله. اضغط على 
الفضاء لجعل شخصية القفز.
إلى أين تذهب بعد ذلك؟
مواد المشروع لديها مشروع المشروع والمشروع النهائي.
على الرغم من فائدتها ، إلا أن آلات الحالة لها قيود. يمكن لآلات الحالة المتزامنة وآلات Pushdown التلقائية معالجة بعض هذه القيود. يمكنك أن تقرأ عنها في كتاب روبرتس نيستروم 
لعبة أنماط البرمجة .
بالإضافة إلى ذلك ، يمكن استكشاف الموضوع بشكل أعمق عن طريق فحص 
أشجار السلوك المستخدمة لإنشاء كيانات أكثر تعقيدًا داخل اللعبة.