מצבים כמחלקות
חלק מהיחידה ארכיטקטורת משחקים ב-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.
אתגר
בינוניכתוב את מצב הסריקה של הצריח.
- ב־
IState.cs, הכרז עלpublic interface IStateעםvoid Enter();,void Tick();ו־void Exit();. - ב־
TurretStates.cs, כתובScanState : IState, בהינתן הראש. בעת הכניסה: זווית 0, צבע הראשColor.green. בכל עדכון: סובב את הזווית המקומית של הראש בציר y בין-45ל־45, בקצב של90מעלות לשנייה. Turret: היכנס למצב אחד כזה ב־Start, ועדכן אותו ב־Update.
לחץ על "שחק": הראש הירוק מסתובב. לאחר מכן לחץ על "בדיקה".
נסו בעצמכם
using UnityEngine;
// ScanState
כל השיעורים ביחידה ארכיטקטורת משחקים ב-Unity
1רכיבים, לא היררכיות
רכיבים קטניםסקריפט בסיסי לאויברכיבים מופשטיםרכיבים שפועלים יחדחזרה: צריח מודולריחזרה: תותח כפול4נתונים שעברו סריאליזציה
מחלקות שניתנות לסריאליזציהרשימות ב־Inspectorטיפוסי Enum ב־Inspectorחזרה: טבלת שללהגרלת השלל2ממשקים ב-Unity
IDamageableממשקים ו-TryGetComponentGetComponentsסיכום: מקש אינטראקציהסיכום: הקרוב ביותר בטווחתרגלו בעצמכם: Unity אונליין