Menu
CoddyTech

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 מכבה אותו.

challenge icon

אתגר

בינוני

שגרו את הזיקוקים מתוך המאגר של Unity.

  1. ב־Awake של Launcher, צרו ObjectPool<Spark>: צרו באמצעות Instantiate(sparkPrefab), הפעילו כשמוציאים מהמאגר, כבו כשמחזירים למאגר, השמידו את האובייקט כשמשחררים אותו, וקבעו maxSize: 5.
  2. ב־Update, הוציאו כל זיקוק באמצעות pool.Get() במקום Instantiate.
  3. ב־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

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