Ricerca…


Pool di oggetti

A volte quando realizzi un gioco devi creare e distruggere molti oggetti dello stesso tipo più e più volte. Puoi farlo semplicemente creando un prefabbricato e istanziato / distruggi questo quando ne hai bisogno, tuttavia, farlo è inefficiente e può rallentare il tuo gioco.

Un modo per aggirare questo problema è il pool di oggetti. Fondamentalmente ciò significa che hai una piscina (con o senza un limite per la quantità) di oggetti che dovrai riutilizzare ogni volta che puoi per evitare un'inutile istanziazione o distruzione.

Di seguito è riportato un esempio di un pool di oggetti semplice

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

Passiamo prima alle variabili

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

private List<GameObject> pool = new List<GameObject>();
  • GameObject prefab : questo è il prefabbricato che il pool di oggetti utilizzerà per istanziare nuovi oggetti nel pool.
  • int amount : questa è la quantità massima di articoli che possono essere nel pool. Se si desidera creare un'istanza di un altro elemento e il pool ha già raggiunto il limite, verrà utilizzato un altro elemento del pool.
  • bool populateOnStart : puoi scegliere di popolare il pool all'avvio oppure no. Così facendo riempirai il pool con istanze del prefabbricato in modo che la prima volta che chiami Instantiate otterrai un oggetto già esistente
  • bool growOverAmount : l'impostazione su true consente al pool di crescere ogni volta che l'importo è richiesto in un determinato intervallo di tempo. Non si è sempre in grado di prevedere con precisione la quantità di articoli da inserire nella propria piscina, in modo tale da aggiungere di più alla propria piscina quando necessario.
  • List<GameObject> pool : questo è il pool, il luogo in cui sono memorizzati tutti gli oggetti istanziati / distrutti.

Ora diamo un'occhiata alla funzione 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);
        }
    }
}

Nella funzione di avvio controlliamo se dovremmo compilare la lista all'avvio e farlo se il prefab è stato impostato e la quantità è maggiore di 0 (altrimenti la creazione sarebbe indefinita).

Questo è solo un semplice ciclo per istanziare nuovi oggetti e metterli in piscina. Una cosa a cui prestare attenzione è che impostiamo tutte le istanze su inattive. In questo modo non sono ancora visibili nel gioco.

Successivamente, c'è la funzione di Instantiate , che è dove la maggior parte della magia accade

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 funzione Instantiate la stessa funzione di Instantiate Unity, ad eccezione del fatto che il prefabbricato è già stato fornito sopra come membro della classe.

Il primo passo della funzione Instantiate è controllare se c'è un oggetto inattivo nel pool in questo momento. Ciò significa che possiamo riutilizzare quell'oggetto e restituirlo al richiedente. Se è presente un oggetto inattivo nel pool, impostiamo la posizione e la rotazione, impostandolo come attivo (altrimenti potrebbe essere riutilizzato per errore se si dimentica di attivarlo) e restituirlo al richiedente.

Il secondo passaggio avviene solo se non ci sono elementi inattivi nel pool e il pool può crescere oltre l'importo iniziale. Quello che succede è semplice: un'altra istanza del prefabbricato viene creata e aggiunta al pool. Permettere la crescita della piscina ti aiuta ad avere la giusta quantità di oggetti nella piscina.

Il terzo "passo" si verifica solo se non ci sono elementi inattivi nel pool e il pool non è autorizzato a crescere. Quando ciò accade, il richiedente riceverà un GameObject null, il che significa che non è disponibile nulla e che dovrebbe essere gestito correttamente per prevenire NullReferenceExceptions .

Importante!

Per assicurarsi che le voci tornare in piscina non si deve distruggere gli oggetti di gioco. L'unica cosa che devi fare è impostarli su inattivo e questo li renderà disponibili per il riutilizzo attraverso il pool.

Pool di oggetti semplice

Di seguito è riportato un esempio di un pool di oggetti che consente di affittare e restituire un determinato tipo di oggetto. Per creare il pool di oggetti è necessario utilizzare Func per la funzione di creazione e un'azione per distruggere l'oggetto per garantire all'utente flessibilità. Alla richiesta di un oggetto quando il pool è vuoto verrà creato un nuovo oggetto e su richiesta quando il pool ha oggetti, gli oggetti verranno rimossi dal pool e restituiti.

Pool di oggetti

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

Esempio di utilizzo

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 altro pool di oggetti semplice

Un altro esempio: un'arma che spara proiettili.

L'arma funge da pool di oggetti per i proiettili che 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
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow