Zoeken…


Object zwembad

Soms moet je bij het maken van een game steeds weer een heleboel objecten van hetzelfde type maken en vernietigen. Je kunt dit eenvoudig doen door een prefab te maken en dit te instantiëren / vernietigen wanneer je dit nodig hebt. Dit is echter inefficiënt en kan je spel vertragen.

Een manier om dit probleem te omzeilen is het poolen van objecten. In feite betekent dit dat u een pool (met of zonder een beperking van de hoeveelheid) objecten die u gaat hergebruiken wanneer u kunt om onnodig instantiëren of vernietigen hebt.

Hieronder ziet u een voorbeeld van een eenvoudige objectpool

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

Laten we eerst de variabelen bekijken

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

private List<GameObject> pool = new List<GameObject>();
  • GameObject prefab : dit is het prefab dat de GameObject prefab zal gebruiken om nieuwe objecten in de pool te instantiëren.
  • int amount : dit is het maximale aantal items dat in de pool kan zijn. Als u een ander item wilt instantiëren en de pool zijn limiet al heeft bereikt, wordt een ander item uit de pool gebruikt.
  • bool populateOnStart : je kunt ervoor kiezen om de pool bij het opstarten te vullen of niet. Als je dit doet, wordt de pool gevuld met instanties van de prefab, zodat je de eerste keer dat je Instantiate belt, een al bestaand object krijgt
  • bool growOverAmount : Door dit op true in te stellen, kan de pool groeien wanneer binnen een bepaald tijdsbestek meer dan het bedrag wordt gevraagd. Je kunt niet altijd nauwkeurig het aantal items voorspellen dat je in je pool plaatst, dus dit zal meer aan je pool toevoegen als dat nodig is.
  • List<GameObject> pool : dit is de pool, de plaats waar al uw geïnstantieerde / vernietigde objecten worden opgeslagen.

Laten we nu eens kijken naar de Start functie

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

In de startfunctie controleren we of we de lijst bij het opstarten moeten invullen en doen we of het prefab is ingesteld en het aantal groter is dan 0 (anders zouden we voor onbepaalde tijd creëren).

Dit is gewoon een eenvoudige manier om nieuwe objecten te instantiëren en in het zwembad te plaatsen. Een ding om op te letten is dat we alle instanties op inactief zetten. Op deze manier zijn ze nog niet zichtbaar in het spel.

Vervolgens is er de functie Instantiate , waar de meeste magie gebeurt

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

De Instantiate functie lijkt op Unity's eigen Instantiate functie, behalve dat het prefab hierboven al als lid van de klas is opgegeven.

De eerste stap van de functie Instantiate is controleren of er momenteel een inactief object in de pool staat. Dit betekent dat we dat object kunnen hergebruiken en terug kunnen geven aan de aanvrager. Als er een inactief object in de pool is, stellen we de positie en de rotatie in, stellen we dat deze actief is (anders kan het per ongeluk worden hergebruikt als u het vergeet te activeren) en terugsturen naar de aanvrager.

De tweede stap gebeurt alleen als er geen inactieve items in de pool zijn en de pool over de oorspronkelijke hoeveelheid mag groeien. Wat er gebeurt is eenvoudig: er wordt een ander exemplaar van het prefab gemaakt en aan de pool toegevoegd. Door de groei van het zwembad toe te staan, kunt u de juiste hoeveelheid objecten in het zwembad hebben.

De derde "stap" gebeurt alleen als er geen inactieve items in de pool zijn en de pool niet mag groeien. Wanneer dit gebeurt, ontvangt de aanvrager een null GameObject, wat betekent dat er niets beschikbaar was en correct moet worden behandeld om NullReferenceExceptions te voorkomen.

Belangrijk!

Om ervoor te zorgen dat je items terug in het zwembad komen, moet je de spelobjecten niet vernietigen. Het enige dat u hoeft te doen, is ze inactief te maken en dat maakt ze beschikbaar voor hergebruik door het zwembad.

Eenvoudig object pool

Hieronder ziet u een voorbeeld van een objectpool waarmee een bepaald objecttype kan worden gehuurd en geretourneerd. Om de objectpool te maken, zijn een Func voor de create-functie en een actie om het object te vernietigen vereist om de gebruiker flexibiliteit te bieden. Bij het aanvragen van een object wanneer de pool leeg is, wordt een nieuw object gemaakt en bij het aanvragen wanneer de pool objecten heeft, worden objecten uit de pool verwijderd en teruggestuurd.

Object zwembad

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

Voorbeeld gebruik

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

Nog een eenvoudige objectpool

Een ander voorbeeld: een wapen dat kogels afschiet.

Het wapen fungeert als een objectpool voor de opsommingstekens die het maakt.

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
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow