1
\$\begingroup\$

My game requires a 2D GameObject to be spawned every second until they fill the whole screen and the game ends (around 60 objects total). On Android I see some performance spikes whenever a GameObject is spawned, so I'd like to use object pooling to help alleviate this. However, some of my objects have random properties like a second sprite, different tag, particle effect, and so on, so I don't think it's possible to save them for object pooling.

Is there any way for me to pool objects that have variable properties?

\$\endgroup\$
  • 1
    \$\begingroup\$ Use a plugin man, there are tons of them in asset store. No need to re-discover America. \$\endgroup\$ – S. Tarık Çetin Feb 22 '16 at 17:33
  • 1
    \$\begingroup\$ @SamedTarıkÇETİN I might have to "rediscover America" when I don't have the money to spend on asset store products. \$\endgroup\$ – Milen Pivchev Feb 22 '16 at 17:45
  • \$\begingroup\$ Check this out: assetstore.unity3d.com/en/#!/search/page=1/sortby=relevance/… \$\endgroup\$ – S. Tarık Çetin Feb 22 '16 at 17:50
  • 2
    \$\begingroup\$ Just pointing everybody to the asset store kind of defeats the purpose of this site. Learning should be involved at least somewhere! \$\endgroup\$ – zcabjro Feb 22 '16 at 21:39
2
\$\begingroup\$

This is how I pool objects in Unity.There are other techniques, and I am sure you can probably find an asset in the unity store, however, this is my preferred method up to now.

First, we need a Pool class, that will allow us to fetch/return/manage our pooled objects:

using System.Collections.Generic;

public class Pool<T> where T : new()
{
    public struct Node
    {
        internal int Index;
        public T Item;
    }

    private Node[] pool;
    private bool[] active;
    private Queue<int> available;

    public int AvailableCount
    {
        get { return available.Count; }
    }

    public int ActiveCount
    {
        get { return pool.Length - available.Count; }
    }

    public int Count
    {
        get { return pool.Length; }
    }

    public Node[] Nodes
    {
        get { return pool; }
    }

    public Pool(int capacity)
    {
        if (capacity <= 0)
        {
            return;
        }

        pool = new Node[capacity];
        active = new bool[capacity];
        available = new Queue<int>(capacity);

        for (int i = 0; i < capacity; i++)
        {
            pool[i] = new Node();
            pool[i].Index = i;
            pool[i].Item = new T();

            active[i] = false;
            available.Enqueue(i);
        }
    }

    public Node Get()
    {
        int index = available.Dequeue();
        active[index] = true;
        return pool[index];
    }

    public void Return(Node item)
    {
        if ((item.Index < 0) || (item.Index > pool.Length))
            return;

        if (!active[item.Index])
            return;

        active[item.Index] = false;
        available.Enqueue(item.Index);
    }

    public bool IsActive(int index)
    {
        if (active[index])
            return true;
        else
            return false;
    }
}

I like to store the GameObject inside of a none-MonoBehaviour class. The reason is that it allows you to adjust a GameObject without having to have an active GameObject present.

This class will manage an individual pooled GameObject.You are going to need one of these for each Prefab that you want to pool.

using UnityEngine;

public class PoolableGameObject
{
    public GameObject GameObject;

    private SpriteRenderer Background;
    private SpriteRenderer Foreground;    

    public PoolableGameObject()
    {
    }

    public void Initialize()
    {
        GameObject = GameObject.Instantiate(Resources.Load("PrefabName")) as GameObject;

        Background = GameObject.transform.Find("Background").GetComponent<SpriteRenderer>();
        Foreground = GameObject.transform.Find("Foreground").GetComponent<SpriteRenderer>();
    }

    public void Set(Color foregroundColor, Color backgroundColor, float width, float height, Vector2 position)
    {
        GameObject.transform.position = position;
        GameObject.transform.localScale = new Vector2(width, height);

        Background.color = backgroundColor;
        Foreground.color = foregroundColor;

        GameObject.SetActive(true);
    }

    public void Return()
    {
        GameObject.SetActive(false);
    }
}

A usage example, you would first need to initialize your Pool.This pre-instantiates your GameObjects.You will want to do this during the loading phase of your game.

Pool<PoolableGameObject> PooledObjects;

private void InitPooledObjects()
{
    // Initialize a Pool with specified number of objects
    PooledObjects = new Pool<PoolableGameObject>(5000);

    // Pre-initialize each object (common properties)
    foreach (Pool<PoolableGameObject>.Node node in PooledObjects.Nodes)
    {
        node.Item.Initialize();
    }
}

Then, to use the GameObjects in your scene, you simply grab one from the pool, and set it:

// If a object is availabe, get one
if (PooledObjects.AvailableCount > 0)
{
    Pool<PoolableGameObject>.Node node = PooledObjects.Get();

    // Post-init special properties
    node.Item.Set((foregroundColor, backgroundColor, width, height, position);        
}

To get rid of a Pooled object, you return it to the Pool for later use:

// Return to pool
node.Item.Return();
PooledObjects.Return(node.Item);
| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.