Search Unity

Even with object pooling, it's still very slow to spawn objects (mainly particle systems)

Discussion in 'Scripting' started by DoubleIsLoveDoubleIsLife, Dec 10, 2016.

  1. DoubleIsLoveDoubleIsLife

    DoubleIsLoveDoubleIsLife

    Joined:
    Nov 29, 2014
    Posts:
    32
    I've been trying to make a very efficient way to spawn many objects, but some things have been making this a nightmare. Originally, I used the typical SetActive pooling technique, then switched over to adding a script called PooledObject to the gameObjects I wanted pooled, and using this script to selectively enable and disable monobehaviours (Only behaviors with Update/FixedUpdate). The enable/disable technique is noticeably faster from my test (Only looked back and forth at the frame rate, no hard data). However, enabling a ParticleSystem is sooo slow and generates garbage(!?), and I just cant find a way to make it faster. Also horrendously slow, is SetParent.


    You might be thinking, well there are a lot of calls so its not so bad. But keep in mind that for each decal the bullet creates, it has already processed multiple raycasts, found the depth of the surface, contacted and read from components on the surface it has shot, and done some basic math. While creating a decal is calling Play() on a couple particle-systems, enabling a script, and parenting the decal to the thing it hit.



    So what do I do? Is there some way I can avoid calling the unholy Play() on the particle systems? I'm thinking I won't parent the decals but instead have a script that attaches them to the object they hit.
     
  2. Dameon_

    Dameon_

    Joined:
    Apr 11, 2014
    Posts:
    542
    Looks like your most expensive call is the parenting call, which makes sense. So the first thing to ask yourself is...does the decal need to actually be parented? Eliminate that, and you've cut your CreateDecal()'s time in half. The other bottleneck appears to be getting an object out of your pool, so maybe you need to reconsider how your pool is storing and returning objects.

    As far as the cost of activating an object, you're dealing with particle systems. You don't need to activate/deactivate them, because you can control their emission, and the emission can be turned off. Perhaps you should use your particle system independently of the object so that you don't have to activate/deactivate it along with the decal.

    Frankly, it's really hard to troubleshoot your performance using only the profiler. If you want any real help, we need scripts too.
     
  3. DoubleIsLoveDoubleIsLife

    DoubleIsLoveDoubleIsLife

    Joined:
    Nov 29, 2014
    Posts:
    32
    I've made it so that decals only get parented to dynamic objects, which has helped a bit. With 12 CreateDecal() calls, 0.03ms is spent getting the object from the pooling list and 0.52ms is spent activating it. I simply call Play() on the particle systems and that seems to be pretty slow. Here is my (presumably) O(1) pooling script.

    Code (CSharp):
    1.         [System.Serializable]
    2.         public class InstancePool
    3.         {
    4.  
    5.             public GameObject baseInstance;
    6.             public int size;
    7.             public List<GameObject> instances = new List<GameObject>();
    8.             GameObject toGrab;
    9.             GameObject temp;
    10.  
    11.             public GameObject GetObject(Vector3 pos, Quaternion rot)
    12.             {
    13.                 if (Flow.GM.poolingEnabled)
    14.                 {
    15.                     if (instances.Count > 0)
    16.                     {
    17.                         toGrab = instances[instances.Count - 1];
    18.                         instances.RemoveAt(instances.Count - 1);
    19.                     }
    20.                     else
    21.                     {
    22.                         Expand();
    23.  
    24.                         toGrab = instances[instances.Count - 1];
    25.                         instances.RemoveAt(instances.Count - 1);
    26.                     }
    27.  
    28.                     toGrab.GetComponent<PooledObject>().Activate();
    29.  
    30.                     toGrab.transform.position = pos;
    31.                     toGrab.transform.rotation = rot;
    32.  
    33.                     toGrab.GetComponent<DestroyAfterSec>().currentlyInactive = false;
    34.  
    35.                     return toGrab;
    36.                 }
    37.                 else
    38.                 {
    39.                     toGrab = Instantiate(baseInstance, pos, rot);
    40.  
    41.                     return toGrab;
    42.                 }
    43.             }
    44.  
    45.             public GameObject Expand()
    46.             {
    47.                 temp = Instantiate(baseInstance) as GameObject;
    48.                 temp.GetComponent<PooledObject>().Deactivate();
    49.              
    50.                 instances.Add(temp);
    51.                 return temp;
    52.             }
    53.  
    54.             public void InitalizePool()
    55.             {
    56.                 for (int i = 0; i < size; i++)
    57.                 {
    58.                     temp = Instantiate(baseInstance) as GameObject;
    59.                     temp.transform.position = new Vector3(0, -20000, 0);
    60.                     temp.GetComponent<PooledObject>().Deactivate();
    61.  
    62.                     instances.Add(temp);
    63.                 }
    64.             }
    65.  
    66.             public void Return(GameObject go)
    67.             {
    68.                 if (Flow.GM.poolingEnabled)
    69.                 {
    70.                     instances.Add(go);
    71.                     go.transform.position = new Vector3(0, -20000, 0);
    72.                     go.GetComponent<PooledObject>().Deactivate();
    73.                 }
    74.                 else
    75.                 {
    76.                     Destroy(go);
    77.                 }
    78.             }
    79.  
    80.         }
    81.     }
    PooledObject script

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class PooledObject : MonoBehaviour {
    6.  
    7.     public ParticleSystem[] particleSystems;
    8.     public MonoBehaviour[] monoBehaviours;
    9.     public AudioSource audioSource;
    10.  
    11.     public void Activate()
    12.     {
    13.         if (audioSource != null)
    14.         {
    15.             audioSource.enabled = true;
    16.         }
    17.  
    18.         if (particleSystems.Length > 0)
    19.         {
    20.             for (int i = 0; i < particleSystems.Length; i++)
    21.             {
    22.                 //particleSystems[i].time = 0;
    23.                 particleSystems[i].Play();
    24.             }
    25.         }
    26.  
    27.         if (monoBehaviours.Length > 0)
    28.         {
    29.             for (int i = 0; i < monoBehaviours.Length; i++)
    30.             {
    31.                 monoBehaviours[i].enabled = true;
    32.             }
    33.         }
    34.  
    35.     }
    36.  
    37.     public void Deactivate()
    38.     {
    39.         if (audioSource != null)
    40.         {
    41.             audioSource.enabled = false;
    42.         }
    43.  
    44.         if (particleSystems.Length > 0)
    45.         {
    46.             for (int i = 0; i < particleSystems.Length; i++)
    47.             {
    48.                 particleSystems[i].Stop();
    49.             }
    50.         }
    51.  
    52.         if (monoBehaviours.Length > 0)
    53.         {
    54.             for (int i = 0; i < monoBehaviours.Length; i++)
    55.             {
    56.                 monoBehaviours[i].enabled = false;
    57.             }
    58.         }
    59.  
    60.     }
    61.  
    62. }
     
  4. DoubleIsLoveDoubleIsLife

    DoubleIsLoveDoubleIsLife

    Joined:
    Nov 29, 2014
    Posts:
    32
    I found out ParticleSystem.Play() has an overload to skip playing child particle systems (ParticleSystem.Play(false)), and this seems to be faster. Instead of setting all the particle systems in the decal to one parent and playing the main parent particle system, I just call Play() on each individual particle system. Now 12 CreateDecal().PooledObject.Activate() calls takes just 0.28ms compared to 0.52ms.