Recherche…


Pool d'objets

Parfois, lorsque vous créez un jeu, vous devez créer et détruire de nombreux objets du même type, encore et encore. Vous pouvez simplement faire cela en créant un préfabriqué et en l'instanciant / détruisant chaque fois que vous en avez besoin, mais cela est inefficace et peut ralentir votre jeu.

Un moyen de contourner ce problème est le regroupement d'objets. En gros, cela signifie que vous avez un pool (avec ou sans limite de quantité) d’objets que vous allez réutiliser chaque fois que vous le pouvez pour éviter une instanciation ou une destruction inutiles.

Voici un exemple de pool d'objets simple

public class ObjectPool : MonoBehaviour 
{
    public GameObject prefab;
    public int amount = 0;
    public bool populateOnStart = true;
    public bool growOverAmount = true;

    private List<GameObject> pool = new List<GameObject>();

    void Start() 
    {
        if (populateOnStart && prefab != null && amount > 0) 
        {
            for (int i = 0; i < amount; i++) 
            {
                var instance = Instantiate(Prefab);
                instance.SetActive(false);
                pool.Add(instance);
            }
        }
    }

    public GameObject Instantiate (Vector3 position, Quaternion rotation) 
    {
        foreach (var item in pool) 
        {
            if (!item.activeInHierarchy) 
            {
                item.transform.position = position;
                item.transform.rotation = rotation;
                item.SetActive( true );
                return item;
            }
        }

        if (growOverAmount) 
        {
            var instance = (GameObject)Instantiate(prefab, position, rotation);
            pool.Add(instance);
            return instance;
        }

        return null;
    }
}

Voyons d'abord les variables

public GameObject prefab;
public int amount = 0;
public bool populateOnStart = true;
public bool growOverAmount = true;

private List<GameObject> pool = new List<GameObject>();
  • GameObject prefab : il s'agit du préfabriqué que le pool d'objets utilisera pour instancier de nouveaux objets dans le pool.
  • int amount : C'est le nombre maximum d'éléments pouvant se trouver dans le pool. Si vous souhaitez instancier un autre élément et que le pool a déjà atteint sa limite, un autre élément du pool sera utilisé.
  • bool populateOnStart : vous pouvez choisir de remplir le pool au démarrage ou non. Cela remplira le pool d'instances du préfabriqué de sorte que la première fois que vous appelez Instantiate vous obtiendrez un objet déjà existant.
  • bool growOverAmount : bool growOverAmount cette valeur sur true pour permettre au pool de croître chaque fois que le montant demandé est supérieur à un certain montant. Vous n'êtes pas toujours en mesure de prédire avec précision la quantité d'articles à placer dans votre piscine, ce qui ajoutera plus à votre piscine en cas de besoin.
  • List<GameObject> pool : c'est le pool, l'endroit où tous vos objets instanciés / détruits sont stockés.

Maintenant, vérifions la fonction Start

void Start() 
{
    if (populateOnStart && prefab != null && amount > 0) 
    {
        for (int i = 0; i < amount; i++) 
        {
            var instance = Instantiate(Prefab);
            instance.SetActive(false);
            pool.Add(instance);
        }
    }
}

Dans la fonction de démarrage, nous vérifions si nous devons remplir la liste au début et le faire si le prefab a été défini et que le montant est supérieur à 0 (sinon, nous créerions indéfiniment).

Ceci est juste une simple boucle pour instancier de nouveaux objets et les placer dans le pool. Une chose à laquelle il faut faire attention est que toutes les instances sont inactives. De cette façon, ils ne sont pas encore visibles dans le jeu.

Ensuite, il y a la fonction Instantiate , qui est la plus grande partie de la magie

public GameObject Instantiate (Vector3 position, Quaternion rotation) 
{
    foreach (var item in pool) 
    {
        if (!item.activeInHierarchy) 
        {
            item.transform.position = position;
            item.transform.rotation = rotation;
            item.SetActive(true);
            return item;
        }
    }

    if (growOverAmount) 
    {
        var instance = (GameObject)Instantiate(prefab, position, rotation);
        pool.Add(instance);
        return instance;
    }

    return null;
}

La Instantiate fonction ressemble propre de l' unité Instantiate fonction, à l' exception de la maison préfabriquée a déjà été fournie ci - dessus en tant que membre de la classe.

La première étape de la fonction Instantiate consiste à vérifier si un objet inactif se trouve actuellement dans le pool. Cela signifie que nous pouvons réutiliser cet objet et le rendre au demandeur. S'il y a un objet inactif dans le pool, nous définissons la position et la rotation, configurez-le pour qu'il soit actif (sinon il pourrait être réutilisé par accident si vous oubliez de l'activer) et renvoyez-le au demandeur.

La deuxième étape ne se produit que s'il n'y a pas d'éléments inactifs dans le pool et que le pool est autorisé à dépasser le montant initial. Ce qui se passe est simple: une autre instance du préfabriqué est créée et ajoutée au pool. Permettre la croissance de la piscine vous aide à avoir la bonne quantité d'objets dans la piscine.

La troisième "étape" ne se produit que s'il n'y a pas d'éléments inactifs dans le pool et que le pool n'est pas autorisé à se développer. Lorsque cela se produit, le demandeur recevra un objet GameObject nul, ce qui signifie que rien n'était disponible et devrait être géré correctement pour empêcher les NullReferenceExceptions .

Important!

Pour vous assurer que vos objets reviennent dans le pool, vous ne devez pas détruire les objets du jeu. La seule chose à faire est de les désactiver et de les rendre utilisables dans le pool.

Pool d'objets simple

Vous trouverez ci-dessous un exemple de pool d'objets permettant de louer et de renvoyer un type d'objet donné. Pour créer le pool d'objets, une fonction Func pour la fonction de création et une action pour détruire l'objet sont requises pour donner à l'utilisateur une certaine flexibilité. Lors de la demande d'un objet lorsque le pool est vide, un nouvel objet sera créé et, sur demande, lorsque le pool contient des objets, les objets seront supprimés du pool et renvoyés.

Pool d'objets

public class ResourcePool<T> where T : class
{
    private readonly List<T> objectPool = new List<T>();
    private readonly Action<T> cleanUpAction;
    private readonly Func<T> createAction;

    public ResourcePool(Action<T> cleanUpAction, Func<T> createAction)
    {
        this.cleanUpAction = cleanUpAction;
        this.createAction = createAction;
    }

    public void Return(T resource)
    {
        this.objectPool.Add(resource);
    }

    private void PurgeSingleResource()
    {
        var resource = this.Rent();
        this.cleanUpAction(resource);
    }

    public void TrimResourcesBy(int count)
    {
        count = Math.Min(count, this.objectPool.Count);
        for (int i = 0; i < count; i++)
        {
            this.PurgeSingleResource();
        }
    }

    public T Rent()
    {
        int count = this.objectPool.Count;
        if (count == 0)
        {
            Debug.Log("Creating new object.");
            return this.createAction();
        }
        else
        {
            Debug.Log("Retrieving existing object.");
            T resource = this.objectPool[count-1];
            this.objectPool.RemoveAt(count-1);
            return resource;
        }
    }
}

Utilisation de l'échantillon

public class Test : MonoBehaviour
{
    private ResourcePool<GameObject> objectPool;

    [SerializeField]
    private GameObject enemyPrefab;

    void Start()
    {
        this.objectPool = new ResourcePool<GameObject>(Destroy,() => Instantiate(this.enemyPrefab) );
    }

    void Update()
    {
        // To get existing object or create new from pool
        var newEnemy = this.objectPool.Rent();
        // To return object to pool
        this.objectPool.Return(newEnemy);
        // In this example the message 'Creating new object' should only be seen on the frame call
        // after that the same object in the pool will be returned.
    }
}

Un autre pool d'objets simples

Un autre exemple: une arme qui tire des balles.

L'arme agit comme un pool d'objets pour les puces qu'elle crée.

public class Weapon : MonoBehaviour {
    
    // The Bullet prefab that the Weapon will create
    public Bullet bulletPrefab;

    // This List is our object pool, which starts out empty
    private List<Bullet> availableBullets = new List<Bullet>();

    // The Transform that will act as the Bullet starting position
    public Transform bulletInstantiationPoint;

    // To spawn a new Bullet, this method either grabs an available Bullet from the pool,
    // otherwise Instantiates a new Bullet
    public Bullet CreateBullet () {
        Bullet newBullet = null;

        // If a Bullet is available in the pool, take the first one and make it active
        if (availableBullets.Count > 0) {
            newBullet = availableBullets[availableBullets.Count - 1];

            // Remove the Bullet from the pool
            availableBullets.RemoveAt(availableBullets.Count - 1);

            // Set the Bullet's position and make its GameObject active
            newBullet.transform.position = bulletInstantiationPoint.position;
            newBullet.gameObject.SetActive(true);
        }
        // If no Bullets are available in the pool, Instantiate a new Bullet
        else {
            newBullet newObject = Instantiate(bulletPrefab, bulletInstantiationPoint.position, Quaternion.identity);

            // Set the Bullet's Weapon so we know which pool to return to later on
            newBullet.weapon = this;
        }
        
        return newBullet;
    }

}

public class Bullet : MonoBehaviour {
    
    public Weapon weapon;

    // When Bullet collides with something, rather than Destroying it, we return it to the pool
    public void ReturnToPool () {
        // Add Bullet to the pool
        weapon.availableBullets.Add(this);

        // Disable the Bullet's GameObject so it's hidden from view
        gameObject.SetActive(false);
    }

}


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow