- Home /
Optimize hundreds of objects
How can i optimize hundreds of objects firing at each other? I've already got box colliders because they are the least memory usage by a long shot. everything is fine until i add this part:
void OnTriggerStay(Collider other)
{
if (other.tag == "Enemy" )
{
if (inTrigger && Time.time > lastFireTime)
{
lastFireTime = Time.time + delayTime;
Fire(other.transform);
}
}
else
Physics.IgnoreCollision(collider, other.collider);
}
void Fire(Transform currentTarget)
{
GameObject shot = Instantiate(bullet, transform.position, Quaternion.identity) as GameObject;
shot.GetComponent<BulletPhysics>().target = currentTarget;
Destroy((shot.gameObject), 2);
}
basically i think the slowdown is coming from the way i'm doing my delay is there a better way? The profiler states 80k calls is being done in stay in a matter of seconds
your right it took so long for this to get approved i posted it on forums sorry about that
Not a problem. The cross-reference is just a link for people to see what has already been suggested across both posts. If you let people know, then it might save someone giving the same answer you have somewhere else. If you get a solution, be sure to : mark as answered on UA; post how the problem was resolved in the Forums.
Answer by AndresBarrera · Feb 04, 2015 at 03:48 PM
Two things to keep in mind:
Make sure that all your bullets are using only 1 Draw Call. Read about dynamic batching on Unity.
Use Object Pooling. Search a bit about how to implement it, but the basic idea is to keep a Queue of the object you will be "creating" a lot (bullets). Instead of intantiate every single one (instantiate is slow), deactivate objects that you are no longer using and re-use them again when you need a new instance. For example:
using System.Collections.Generic; //Pool private Queue<BulletPhysics> bulletPool = new Queue<BulletPhysics>(); public BulletPhysics GetBulletFromPool() { BulletPhysics bullet = null; if (this.bulletPool .Count == 0) { bullet = Instantiate(this.bulletPrefab) as BulletPhysics; } else { bullet = this.bulletPool .Dequeue(); } bullet.gameObject.SetActive(true); bullet.Init(); return bullet; } public void ReturnBulletToPool(BulletPhysics bullet) { bullet.ClearData(); this.bulletPool.Enqueue(bullet); }
is queue better than list? for performance reasons with hundreds of objects this would matter a lot to me.
Answer by andrew-lukasik · Feb 04, 2015 at 04:05 PM
Hi. OnTriggerStay() can potentially be called rather frequently. Consider switching to OnTriggerEnter() and OnTriggerExit(). But more importantly - if you seek performance then abandon Instantiate() immediately and use "prefab pooling". Runtime Instantiate() takes a lot of time and very probably it alone impacts performance in your script the most.
You can use mine prefab pooling script from below (you don't have to undestand it really and it's very simple to use - after creating PoolingManager.cs just replace every "Instantiate" with "PoolingManager.Instantiate"). But keep in mind that it will work ONLY if you disable instantiated gameobject and not destroy them (just replace your "Destroy(gameObject)" with "gameObject.SetActive(false)").
using UnityEngine;
using System.Collections;
#if UNITY_EDITOR
using UnityEditor;
#endif
public class PoolingManager : MonoBehaviour {
///////////////////////////////////////////////////////////////////////////////
// FIELDS & PROPERTIES ////////////////////////////////////////////////////////
private static PoolingManager _singleton = null;
private static PoolingManager singleton {
get {
if( _singleton==null ) InstantiateSingleton();
return _singleton;
}
}
public static Transform poolingContainer { get { return singleton.transform; } }
public GameObject[] pool = new GameObject[0];
private GameObject[] poolSourceGameObjects = new GameObject[0];
///////////////////////////////////////////////////////////////////////////////
// MONOBEHAVIOUR MESSAGES /////////////////////////////////////////////////////
// AWAKE //
void Awake () {
if( _singleton==null ) _singleton = this;
else if( _singleton!=this ) Destroy( this );
}
// ON DESTROY //
private void OnDestroy () {
#if UNITY_EDITOR
foreach( GameObject go in poolSourceGameObjects ) go.SetActive( true );
#endif
}
///////////////////////////////////////////////////////////////////////////////
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////
// GET POOL INDEX //
private int GetPoolIndex ( GameObject go ) {
int result = -1;
for( int i = 0 ; i<pool.Length ; i++ ) {
if( !pool[i] ) {
pool.Remove( ref pool , i );
i--;
}
else if( !pool[i].activeInHierarchy && pool[i].name==go.name ) {
result = i;
break;
}
}
if( result==-1 ) {
AddToPool( go );
result = pool.Length-1;
}
return result;
}
// ADD TO POOL
private void AddToPool ( GameObject paramGameObject ) {
if( paramGameObject.activeSelf ) paramGameObject.SetActive( false );
GameObject instance = Instantiate( paramGameObject ) as GameObject;
#if UNITY_EDITOR
if( instance.activeSelf ) Debug.Log( "disable prefab for proper pooling: "+instance.name );
//instance.SetActive(false);
#endif
instance.name = paramGameObject.name;
instance.transform.parent = transform;
pool.Add( ref pool , instance );
paramGameObject.SetActive( true );
}
// INITIATE SINGLETON //
private static void InstantiateSingleton () {
new GameObject( "#PoolingManager" , typeof(PoolingManager) );
}
///////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// PUBLIC STATIC FUNCTIONS ////////////////////////////////////////////////////
// INSTANTIATE POOLED //
public static void InstantiatePooled ( GameObject paramPrefab , Vector3 paramPosition , Quaternion paramRotation ) {
int poolIndex = singleton.GetPoolIndex( paramPrefab );
_singleton.pool[poolIndex].transform.position = paramPosition;
_singleton.pool[poolIndex].transform.rotation = paramRotation;
_singleton.pool[poolIndex].SetActive( true );
#if UNITY_EDITOR
MaintainPoolSourceGameObjects( paramPrefab );
#endif
}
public static void InstantiatePooled ( GameObject paramPrefab , Vector3 paramPosition , Quaternion paramRotation , out GameObject instance ) {
int poolIndex = singleton.GetPoolIndex( paramPrefab );
_singleton.pool[poolIndex].transform.position = paramPosition;
_singleton.pool[poolIndex].transform.rotation = paramRotation;
_singleton.pool[poolIndex].SetActive( true );
instance = _singleton.pool[poolIndex];
#if UNITY_EDITOR
MaintainPoolSourceGameObjects( paramPrefab );
#endif
}
#if UNITY_EDITOR
// MAINTAIN POOL SOURCE GAMEOBJECTS //
private static void MaintainPoolSourceGameObjects ( GameObject paramSourceGo ) {
foreach( GameObject entry in _singleton.poolSourceGameObjects ) {
if( entry==paramSourceGo ) return;
}
_singleton.poolSourceGameObjects.Add( ref _singleton.poolSourceGameObjects , paramSourceGo );
}
#endif
}
If you would like to use then you Fire function would like this:
void Fire(Transform currentTarget)
{
GameObject shot;
Pooling$$anonymous$$anager.InstantiatePooled(bullet, transform.position, Quaternion.identity , out shot);
shot.GetComponent<BulletPhysics>().target = currentTarget;
}
and ins$$anonymous$$d of "Destroy((shot.gameObject), 2);" attach this script to every bullet prefab:
using UnityEngine;
using System.Collections;
public class DisableGameObjectAfterDelay : $$anonymous$$onoBehaviour {
[SerializeField] private float delay = 2f;
private void OnEnable () {
Invoke("Execute",delay);
}
private void Execute () {
gameObject.SetActive(false);
}
}
it will properly disable it after given delay
You have extremely well written code i tend to put as little in singleton as possible though hmm....Would have no use for OnEnable to...need them to only fire when in a detection range I've created. Could easily modify this to my needs though. thanks for the info