Menu
CoddyTech

ObjectPool de Unity

Fait partie de la section Architecture de jeu dans Unity du Journey Unity de Coddy. Leçon 48 sur 64.

Unity fournit un pool, ObjectPool<T> dans UnityEngine.Pool. Tu lui indiques quoi faire à chaque étape à l’aide de quatre fonctions lambda, et il conserve les objets disponibles ainsi que les compteurs. Cette cheminée produit une bouffée de fumée toutes les 0.2 secondes, et chaque bouffée est remise dans le pool au bout d’une seconde :

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 s’exécute lorsque Get trouve le pool vide ; actionOnGet s’exécute à chaque Get et actionOnRelease à chaque Release. Lorsqu’une bouffée est remise dans le pool alors que maxSize objets disponibles s’y trouvent déjà, le pool ne la conserve pas : actionOnDestroy la détruit. defaultCapacity correspond uniquement à la capacité initiale de la liste du pool ; aucun objet n’est donc créé à l’avance. CountAll compte les bouffées créées par le pool et qu’il possède encore, CountActive celles qui se trouvent dans le jeu et CountInactive celles qui sont disponibles. Get a activé la bouffée avant que la cheminée ne la place, ce qui convient ici, car Puff n’a pas de méthode OnEnable qui lit sa position. La bouffée conserve le pool sous la forme d’un IObjectPool<Puff>, l’interface implémentée par tous les pools Unity, et Launch réinitialise son âge à chaque utilisation.

Lorsque collectionCheck est activé, remettre dans le pool une bouffée qui s’y trouve déjà déclenche une InvalidOperationException : Trying to release an object that has already been released to the pool. La vérification ne s’exécute que dans l’éditeur, car elle parcourt les objets disponibles à chaque Release ; dans une version compilée, la bouffée se trouverait deux fois dans le pool et deux appels à Get renverraient le même objet. Une cause fréquente est un objet qui continue de s’exécuter après sa remise dans le pool et qui s’y remet une deuxième fois. C’est pourquoi actionOnRelease le désactive.

challenge icon

Défi

Moyen

Fais éclater les feux d’artifice à partir du pool de Unity.

  1. Dans Awake de Launcher, crée un ObjectPool<Spark> : crée avec Instantiate(sparkPrefab), active les étincelles lorsqu’elles sont prises, désactive-les lorsqu’elles sont relâchées, détruis l’objet lorsqu’il est libéré, et définis maxSize: 5.
  2. Dans Update, prends chaque étincelle avec pool.Get() au lieu de Instantiate.
  3. Dans Return, appelle pool.Release(spark) au lieu de Destroy.

Appuie sur « Jouer » et lance quelques salves. Puis appuie sur « Vérifier ».

Essayez vous-même

using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.Pool;

public class Launcher : MonoBehaviour
{
    public Spark sparkPrefab;
    public int sparks = 8;

    void Awake()
    {
        // Créer le pool : créer, récupérer, libérer et détruire, avec au plus 5 étincelles en réserve
    }

    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);
            }
        }
    }

    // Une étincelle arrivée en fin de vie
    public void Return(Spark spark)
    {
        Destroy(spark.gameObject);
    }

    // Vers l’extérieur et vers le haut, réparties uniformément autour du lanceur
    Vector3 Direction(int i)
    {
        return Quaternion.Euler(0f, i * 360f / sparks, 0f) * new Vector3(0f, 1f, 3f);
    }
}

Toutes les leçons de Architecture de jeu dans Unity

Entraînez-vous par vous-même : Playground Unity