Menu
CoddyTech

מצבים כמחלקות

חלק מהיחידה ארכיטקטורת משחקים ב-Unity במסלול ה-Unity של Coddy. שיעור 35 מתוך 64.

המתג על enum ביחידה 3 משאיר את העבודה של כל מצב בתוך Update אחד. הפתרון הזה מפסיק להתאים כשהמצבים כוללים נתונים משלהם: טיימר שמשמש רק מצב אחד הופך לשדה של MonoBehaviour, לצד השדות של כל שאר המצבים, וכל case חייב לזכור לאפס את מה שהוא משתמש בו. תבנית המצבים של C# נותנת לכל מצב מחלקה משלו, וכל מחלקה מממשת ממשק אחד. משואה זו מהבהבת או נשארת דולקת, ומקש הרווח מחליף בין שני המצבים:

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 מסדרת את הדברים כשהמצב מסתיים: מקש הרווח יכול להילחץ כשהמנורה כבויה, ו־Exit מדליקה אותה מחדש, כך ש־SteadyState לעולם לא מתחיל עם מנורה כבויה. SetState מפעילה את Exit של המצב הישן, ואז את Enter של המצב החדש. מצב שלישי, כמו פעימה איטית, הוא מחלקה נוספת, ואין צורך לשנות את Update של Beacon.

challenge icon

אתגר

בינוני

כתוב את מצב הסריקה של הצריח.

  1. ב־IState.cs, הכרז על public interface IState עם void Enter();, void Tick(); ו־void Exit();.
  2. ב־TurretStates.cs, כתוב ScanState : IState, בהינתן הראש. בעת הכניסה: זווית 0, צבע הראש Color.green. בכל עדכון: סובב את הזווית המקומית של הראש בציר y בין -45 ל־45, בקצב של 90 מעלות לשנייה.
  3. Turret: היכנס למצב אחד כזה ב־Start, ועדכן אותו ב־Update.

לחץ על "שחק": הראש הירוק מסתובב. לאחר מכן לחץ על "בדיקה".

נסו בעצמכם

using UnityEngine;

// ScanState

כל השיעורים ביחידה ארכיטקטורת משחקים ב-Unity

תרגלו בעצמכם: Unity אונליין