الانتقالات
جزء من قسم هندسة الألعاب في Unity في رحلة Unity على Coddy. الدرس 38 من 64.
من يقرر متى تتغير الحالة؟ إن جعل المالك يقرر ذلك يعيد جميع القواعد إلى تحديث واحد، كما فعلت عبارة التبديل. والإجابة المعتادة هي أن كل حالة تعرف طريقها للخروج: ففي دالة Tick الخاصة بها، تتحقق من شرطها وتطلب من الآلة الانتقال إلى الحالة التالية. تنتظر هذه القنبلة حتى يقترب اللاعب، ثم تحرق فتيلها، وتعود إلى الانتظار إذا ابتعد اللاعب راكضًا:
public class Bomb : MonoBehaviour
{
public Transform player;
public StateMachine machine = new StateMachine();
public IState idle;
public IState fuse;
void Start()
{
idle = new IdleState(this);
fuse = new FuseState(this);
machine.ChangeState(idle);
}
void Update() { machine.Tick(); }
public bool PlayerNear()
{
return Vector3.Distance(transform.position, player.position) < 3f;
}
}
public class IdleState : IState
{
private Bomb bomb;
public IdleState(Bomb bomb) { this.bomb = bomb; }
public void Enter() { }
public void Exit() { }
public void Tick()
{
if (bomb.PlayerNear()) bomb.machine.ChangeState(bomb.fuse);
}
}
public class FuseState : IState
{
private Bomb bomb;
private float timer;
public FuseState(Bomb bomb) { this.bomb = bomb; }
public void Enter() { timer = 2f; }
public void Exit() { }
public void Tick()
{
if (!bomb.PlayerNear())
{
bomb.machine.ChangeState(bomb.idle);
return;
}
timer -= Time.deltaTime;
if (timer <= 0f)
{
Debug.Log("Boom!");
Object.Destroy(bomb.gameObject);
}
}
}تحصل كل حالة على القنبلة في مُنشئها، وتصل من خلالها إلى الآلة واختبار اقتراب اللاعب والحالات الأخرى. تنشئ القنبلة كل حالة مرة واحدة في Start، لذا لا تنشئ الحالة حالةً أخرى باستخدام new: فالحالتان اللتان تنشئ كل منهما الأخرى في مُنشئهما ستدخلان في حلقة إلى أن يفيض المكدس. لا تفعل دالة Update الخاصة بالقنبلة سوى تحديث الآلة، وتوجد القواعد في الحالات التي تخصها: فتيل FuseState الذي مدته ثانيتان هو مسؤولية FuseState وحدها.
تشغّل ChangeState دالتي Exit في FuseState وEnter في IdleState مباشرةً، لكن بقية دالة Tick في FuseState ستظل تعمل بعد استدعاء الدالة. يوقفها return، وإلا لاحترق الفتيل إطارًا إضافيًا رغم أن الحالة انتهت بالفعل. تُكتب Destroy على النحو Object.Destroy: ترثها MonoBehaviour من فئة Object في Unity، أما الفئة العادية فعليها ذكر اسم الفئة.
التحدي
متوسطاكتب انتقالات مصيدة الذباب في كل استدعاء لـ Tick في FlytrapStates.cs.
- مفتوحة: عندما يتحقق
trap.PlayerInReach()، استدعِtrap.machine.ChangeState(trap.snap);. - انقضاض: أضف
Time.deltaTimeإلىtimer؛ وعند0.5، انتقل إلىtrap.chewإذا كان اللاعب لا يزال في المتناول، وإلا فانتقل إلىtrap.open. - مضغ: أنقص
timerبدءًا من 3؛ وعند 0، انتقل إلىtrap.open.
اضغط «العب»، وامشِ نحو المصيدة، ثم تفادَها. بعد ذلك اضغط «تحقق».
جرّب بنفسك
using UnityEngine;
public class OpenState : IState
{
private Flytrap trap;
public OpenState(Flytrap trap)
{
this.trap = trap;
}
public void Enter()
{
trap.Paint(new Color(0.3f, 0.8f, 0.2f));
}
public void Tick()
{
// انطبق عندما يصبح اللاعب في المتناول
}
public void Exit()
{
}
}
public class SnapState : IState
{
private Flytrap trap;
private float timer;
public SnapState(Flytrap trap)
{
this.trap = trap;
}
public void Enter()
{
timer = 0f;
trap.Paint(new Color(1f, 0.55f, 0.1f));
}
public void Tick()
{
// بعد 0.5 ثانية: امضغ إذا كان اللاعب لا يزال في المتناول، وإلا فانفتح مجددًا
}
public void Exit()
{
}
}
public class ChewState : IState
{
private Flytrap trap;
private float timer;
public ChewState(Flytrap trap)
{
this.trap = trap;
}
public void Enter()
{
timer = 3f;
trap.Paint(new Color(0.5f, 0.05f, 0.1f));
Debug.Log("Chomp!");
}
public void Tick()
{
// بعد 3 ثوانٍ: انفتح مجددًا
}
public void Exit()
{
}
}
جميع دروس هندسة الألعاب في Unity
1المكوّنات، لا التسلسلات الهرمية
مكوّنات صغيرةنص برمجي أساسي للعدومكوّنات مجرّدةالمكوّنات تعمل معًامراجعة: برج معياريمراجعة: مدفع مزدوج4البيانات المُسلسلة
الأصناف القابلة للتسلسلالقوائم في Inspectorالتعدادات في Inspectorمراجعة: جدول الغنائممراجعة: اختيار الغنائم عشوائيًا7آلات الحالات
الحالات كأصنافحالة التنبيهآلة حالاتالانتقالاتعقول الأعداءالفارس يهاجممراجعة: حالات الباب2الواجهات في Unity
IDamageableالواجهات وTryGetComponentGetComponentsمراجعة: مفتاح التفاعلمراجعة: أقرب عنصر ضمن النطاقتدرّب بنفسك: Playground لـ Unity