Buscar..


Conjunto de objetos

A veces, cuando creas un juego necesitas crear y destruir muchos objetos del mismo tipo una y otra vez. Simplemente puede hacer esto haciendo una prefabulación y creando una instancia / destruyéndolo cuando lo necesite, sin embargo, hacerlo es ineficiente y puede ralentizar su juego.

Una forma de solucionar este problema es la agrupación de objetos. Básicamente, lo que esto significa es que tiene un conjunto (con o sin límite a la cantidad) de objetos que va a reutilizar siempre que pueda para evitar la creación de instancias innecesarias o la destrucción.

A continuación se muestra un ejemplo de un grupo de objetos 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;
    }
}

Repasemos primero las 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 : esta es la prefab que el grupo de objetos usará para instanciar nuevos objetos en el grupo.
  • int amount : Esta es la cantidad máxima de elementos que pueden estar en la agrupación. Si desea crear una instancia de otro elemento y la agrupación ya ha alcanzado su límite, se utilizará otro elemento de la agrupación.
  • bool populateOnStart : puede elegir rellenar el grupo al inicio o no. Al hacerlo, se llenará el grupo con instancias de la prefab para que la primera vez que llame a Instantiate , obtenga un objeto ya existente.
  • bool growOverAmount : establecer esto en true permite que la agrupación crezca siempre que se solicite más de la cantidad en un determinado período de tiempo. No siempre es capaz de predecir con precisión la cantidad de elementos para colocar en su grupo, por lo que esto agregará más a su grupo cuando sea necesario.
  • List<GameObject> pool : este es el grupo, el lugar donde se almacenan todos los objetos creados / destruidos.

Ahora vamos a ver la función de 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);
        }
    }
}

En la función de inicio, verificamos si debemos rellenar la lista en el inicio y hacerlo si se ha establecido la prefab y la cantidad es mayor que 0 (de lo contrario, estaríamos creando de forma indefinida).

Esto es solo un simple para crear un bucle de objetos nuevos y ponerlos en el grupo. Una cosa a la que hay que prestar atención es que configuramos todos los casos como inactivos. De esta manera aún no están visibles en el juego.

A continuación, está la función de Instantiate , que es donde ocurre la mayor parte de la magia.

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 función de Instantiate parece a la función de Instantiate propia de Unity, excepto que la prefabulación ya se ha proporcionado anteriormente como miembro de la clase.

El primer paso de la función de Instantiate es verificar si hay un objeto inactivo en la agrupación en este momento. Esto significa que podemos reutilizar ese objeto y devolvérselo al solicitante. Si hay un objeto inactivo en el grupo, establecemos la posición y la rotación, lo configuramos para que esté activo (de lo contrario, podría reutilizarse por accidente si olvida activarlo) y se lo devolverá al solicitante.

El segundo paso solo ocurre si no hay elementos inactivos en el grupo y se permite que el grupo crezca sobre la cantidad inicial. Lo que sucede es simple: se crea otra instancia del prefab y se agrega a la agrupación. Permitir el crecimiento de la piscina le ayuda a tener la cantidad correcta de objetos en la piscina.

El tercer "paso" solo ocurre si no hay elementos inactivos en el grupo y el grupo no puede crecer. Cuando esto suceda, el solicitante recibirá un GameObject nulo, lo que significa que no había nada disponible y debería manejarse adecuadamente para evitar NullReferenceExceptions .

¡Importante!

Para asegurarse de que sus artículos ponen de nuevo en la piscina que no deben destruir los objetos del juego. Lo único que debe hacer es establecerlos como inactivos y eso hará que estén disponibles para su reutilización a través de la piscina.

Conjunto de objetos simples

A continuación se muestra un ejemplo de un grupo de objetos que permite alquilar y devolver un tipo de objeto determinado. Para crear el grupo de objetos, se requiere un Func para la función de creación y una Acción para destruir el objeto para dar flexibilidad al usuario. Al solicitar un objeto cuando el grupo está vacío, se creará un nuevo objeto y al solicitar cuando el grupo tiene objetos, los objetos se eliminan del grupo y se devuelven.

Conjunto de objetos

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

Uso de la muestra

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.
    }
}

Otro grupo de objetos simples

Otro ejemplo: un arma que dispara balas.

El arma actúa como un grupo de objetos para las balas que crea.

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
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow