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

تدرّب بنفسك: Playground لـ Unity