ObjectPool של Unity
חלק מהיחידה ארכיטקטורת משחקים ב-Unity במסלול ה-Unity של Coddy. שיעור 48 מתוך 64.
Unity כוללת מאגר, ObjectPool<T> בתוך UnityEngine.Pool. מגדירים לו מה לעשות בכל שלב באמצעות ארבע פונקציות למבדה, והוא מנהל את האובייקטים הרזרביים ואת המונים. הארובה הזאת פולטת עשן כל 0.2 שניות, וכל פליטה חוזרת למאגר אחרי שנייה:
using UnityEngine;
using UnityEngine.Pool;
public class Chimney : MonoBehaviour
{
public Puff puffPrefab;
private ObjectPool<Puff> pool;
private float timer;
void Awake()
{
pool = new ObjectPool<Puff>(
createFunc: () => Instantiate(puffPrefab),
actionOnGet: puff => puff.gameObject.SetActive(true),
actionOnRelease: puff => puff.gameObject.SetActive(false),
actionOnDestroy: puff => Destroy(puff.gameObject),
collectionCheck: true,
defaultCapacity: 10,
maxSize: 20);
}
void Update()
{
timer += Time.deltaTime;
if (timer >= 0.2f)
{
timer = 0f;
Puff puff = pool.Get();
puff.transform.position = transform.position + Vector3.up;
puff.Launch(pool);
}
}
}// Puff.cs
private IObjectPool<Puff> pool;
private float age;
public void Launch(IObjectPool<Puff> owner)
{
pool = owner;
age = 0f;
}
void Update()
{
transform.Translate(0f, Time.deltaTime, 0f);
age += Time.deltaTime;
if (age >= 1f) pool.Release(this);
}createFunc פועלת כשהקריאה ל-Get מוצאת את המאגר ריק; actionOnGet פועלת בכל קריאה ל-Get, ו-actionOnRelease בכל קריאה ל-Release. כשמחזירים פליטת עשן למאגר בזמן ש-maxSize אובייקטים רזרביים כבר ממתינים בו, המאגר לא שומר אותה: actionOnDestroy משמידה אותה. defaultCapacity הוא רק גודל ההתחלה של רשימת המאגר, כך ששום דבר לא נוצר מראש. CountAll סופר את פליטות העשן שהמאגר יצר ועדיין מחזיק בבעלותו, CountActive את אלה שנמצאות במשחק, ו-CountInactive את האובייקטים הרזרביים. Get הפעיל את פליטת העשן לפני שהארובה מציבה אותה, וזה בסדר כאן כי ל-Puff אין OnEnable שקורא את המיקום שלה. פליטת העשן שומרת את המאגר כממשק IObjectPool<Puff>, שאותו כל מאגר של Unity מממש, ו-Launch מאפס את הגיל שלה בכל שימוש.
כאשר collectionCheck מופעל, החזרת פליטת עשן שכבר נמצאת במאגר גורמת לחריגת InvalidOperationException: Trying to release an object that has already been released to the pool. הבדיקה מתבצעת רק בעורך, כי היא מחפשת בין האובייקטים הרזרביים בכל קריאה ל-Release; בגרסה שנבנתה פליטת העשן תישאר במאגר פעמיים, ושתי קריאות ל-Get יחזירו את אותו אובייקט. גורם נפוץ לכך הוא אובייקט שממשיך לפעול אחרי שהוחזר למאגר ומחזיר את עצמו שוב, וזו הסיבה ש-actionOnRelease מכבה אותו.
אתגר
בינונישגרו את הזיקוקים מתוך המאגר של Unity.
- ב־
AwakeשלLauncher, צרוObjectPool<Spark>: צרו באמצעותInstantiate(sparkPrefab), הפעילו כשמוציאים מהמאגר, כבו כשמחזירים למאגר, השמידו את האובייקט כשמשחררים אותו, וקבעוmaxSize: 5. - ב־
Update, הוציאו כל זיקוק באמצעותpool.Get()במקוםInstantiate. - ב־
Return, קראו ל־pool.Release(spark)במקוםDestroy.
לחצו על "שחק" ושגרו כמה מטחים. לאחר מכן לחצו על "בדיקה".
נסו בעצמכם
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.Pool;
public class Launcher : MonoBehaviour
{
public Spark sparkPrefab;
public int sparks = 8;
void Awake()
{
// צרו את המאגר: הגדירו יצירה, קבלה, שחרור והשמדה, עם לכל היותר 5 ניצוצות רזרביים
}
void Update()
{
if (Keyboard.current.spaceKey.wasPressedThisFrame)
{
for (int i = 0; i < sparks; i++)
{
Spark spark = Instantiate(sparkPrefab);
spark.transform.position = transform.position + Vector3.up;
spark.Launch(Direction(i), this);
}
}
}
// ניצוץ שמשך חייו הסתיים
public void Return(Spark spark)
{
Destroy(spark.gameObject);
}
// החוצה ולמעלה, בפיזור אחיד סביב המשגר
Vector3 Direction(int i)
{
return Quaternion.Euler(0f, i * 360f / sparks, 0f) * new Vector3(0f, 1f, 3f);
}
}
כל השיעורים ביחידה ארכיטקטורת משחקים ב-Unity
1רכיבים, לא היררכיות
רכיבים קטניםסקריפט בסיסי לאויברכיבים מופשטיםרכיבים שפועלים יחדחזרה: צריח מודולריחזרה: תותח כפול4נתונים שעברו סריאליזציה
מחלקות שניתנות לסריאליזציהרשימות ב־Inspectorטיפוסי Enum ב־Inspectorחזרה: טבלת שללהגרלת השלל2ממשקים ב-Unity
IDamageableממשקים ו-TryGetComponentGetComponentsסיכום: מקש אינטראקציהסיכום: הקרוב ביותר בטווחתרגלו בעצמכם: Unity אונליין