Menu
CoddyTech
flag Ar iconالعربيةdown icon

الحالات كأصناف

جزء من قسم هندسة الألعاب في Unity في رحلة Unity على Coddy. الدرس 28 من 50.

يضع التبديل باستخدام enum في القسم 3 عمل كل حالة داخل Update واحد. لكن هذا الأسلوب لا يتوسع جيدًا عندما تكون للحالات بياناتها الخاصة: فالمؤقّت الذي تستخدمه حالة واحدة فقط ينتهي به الأمر حقلًا في MonoBehaviour، إلى جانب حقول كل الحالات الأخرى، ويجب أن تتذكّر كل حالة إعادة ضبط ما تستخدمه. يمنح نمط الحالة في C# كل حالة صنفًا خاصًا بها، وتطبّق كل الأصناف واجهة واحدة. يومض هذا المنارة أو يبقى مضاءً، ويبدّل Space بين الحالتين:

public interface IState
{
    void Enter();
    void Tick();
    void Exit();
}

public class BlinkState : IState
{
    private Renderer lamp;
    private float timer;

    public BlinkState(Renderer lamp)
    {
        this.lamp = lamp;
    }

    public void Enter() { timer = 0f; }

    public void Tick()
    {
        timer += Time.deltaTime;
        if (timer >= 0.5f)
        {
            timer = 0f;
            lamp.enabled = !lamp.enabled;
        }
    }

    public void Exit() { lamp.enabled = true; }
}

public class SteadyState : IState
{
    public void Enter() { }
    public void Tick() { }
    public void Exit() { }
}

public class Beacon : MonoBehaviour
{
    public Renderer lamp;
    private IState blink;
    private IState steady;
    private IState current;

    void Start()
    {
        blink = new BlinkState(lamp);
        steady = new SteadyState();
        SetState(steady);
    }

    void Update()
    {
        if (Keyboard.current.spaceKey.wasPressedThisFrame) SetState(current == steady ? blink : steady);
        current.Tick();
    }

    void SetState(IState next)
    {
        if (current != null) current.Exit();
        current = next;
        current.Enter();
    }
}

الحالات أصناف C# عادية، وليست MonoBehaviours. تنشئها باستخدام new وتمرّر إليها ما تحتاج إليه عبر المُنشئ، وفي هذه الحالة Renderer الخاص بالمصباح، لأنها ليست مكوّنات ولا يمكنها استدعاء GetComponent أو عرض حقول في Inspector. كما أن Unity لا يستدعي أساليبها: فـ Beacon هو MonoBehaviour، ولذلك يستدعي Update الخاص به current.Tick() في كل إطار.

ينتمي timer إلى BlinkState وحدها، ويضبطه Enter على 0 في كل مرة تبدأ فيها المنارة بالوميض. ينظّف Exit الأمور عند انتهاء الحالة: قد يُضغط Space بينما يكون المصباح مطفأً، ويعيده Exit إلى الإضاءة، لذا لا تبدأ SteadyState بمصباح مطفأ. ينفّذ SetState أسلوب Exit للحالة القديمة، ثم أسلوب Enter للحالة الجديدة. وتتطلب إضافة حالة ثالثة، مثل نبض بطيء، صنفًا إضافيًا واحدًا، ولا يتغير Update في Beacon.

challenge icon

التحدي

متوسط

يحرس برجٌ حارسٌ الفناء. حرّك اللاعب باستخدام الأسهم (جاهز). امنح البرج حالتين، واكتبهما على شكل صنفين:

  • في IState.cs، عرّف public interface IState مع void Enter() وvoid Tick() وvoid Exit().
  • في TurretStates.cs، اكتب صنفين عاديين يطبّقان هذه الواجهة. يستقبل ScanState رأس البرج في الباني. يحرّك الرأس من جانب إلى آخر: تتغير زاوية y المحلية للرأس بمعدل 90 درجة في الثانية بين -45 و45، بدءًا من 0 والاتجاه إلى الأعلى في كل مرة يبدأ فيها المسح. كما يلوّن Enter الرأس باستخدام Color.green.
  • يستقبل AlertState الرأس واللاعب. يلوّن Enter الرأس باستخدام Color.red ويسجّل Intruder!؛ ويوجّه Tick الرأس لمواجهة اللاعب حول المحور y فقط، دون إمالة؛ ويسجّل Exit All clear.
  • في Turret.cs، أنشئ الحالتين في Start وابدأ المسح. في كل إطار، انتقل إلى حالة التنبيه عندما تكون المسافة بين اللاعب والبرج أقل من range (4)، وارجع إلى المسح عندما لا يكون كذلك، ثم نفّذ Tick للحالة الحالية. عند تغيير الحالة، يُنفّذ Exit للحالة القديمة، ثم Enter للحالة الجديدة.

اضغط Play، وامشِ نحو البرج ثم ابتعد عنه، ثم اضغط Check.

جرّب بنفسك

using UnityEngine;

// ScanState وAlertState
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس هندسة الألعاب في Unity