Main thread bottleneck

I’m doing a 2D tower defense game. I made some simple enemies, a tower, a track, got some pictures for testing from google and initialized a scene with medium/high action to see how game is performing and if unity will be ok for my game. There will be much more things later, but main thread is already dying with 50-300ms. My game in high levels will have minimum tens of thousands of enemies on the screen and many bullets flying, each object is running its own little movement script. Is there a way to optimize main thread, maybe multithreading ? Maybe i would be better with some another game engine ? My game is light on graphics, but heavy on cpu. HEre my current project : http://www.filedropper.com/towers

And another question - is there any performance difference in having 10000 - 50000 game objects(prefabs) running its own little script (what i have right now) vs having one big script that controls all those objects ?

Yes big difference. This thread has answers from various people that seem to be appropriate to your project too

Ok, trying to put all enemies movement in one script, made some little memory/cpu improvements. Now there is 2 things using most of game cpu time - “Physics2D.Simulate” and my enemy movement script. I’m using right now “Enemies = GameObject.FindGameObjectsWithTag(“Enemy”);” to find all excisting enemy objects, but i think this is really not a fast way. I’m targeting my game to deal without lag with at least 10k-50k game objects so one could have fun in later game without slideshow, so maybe i need to use some kind of array / list / something else to contain all enemies, and add them as they spawn, and remove from list and destroy as they die. Maybe someone knows a good articles about arrays/lists ? I’m looking for the fastest one to iterate through all list, to remove from list, and to add to list.

all Find x methods are horrible for performance if you call then in Update or any repeating place, I think even the manual says that. You should have own list for enemies which you update on spawn/death. Also with that many objects I hope you have some kind of pooling system for various things.

Yes, i know about Find() methods, and i’m looking for the fastest type of list/array right now. No pooling system for now, but maybe later will have to look into it.

Well, i’m trying to use List from System.Collections.Generic to hold all enemies, but i get weird error - “Array index is out of range”. I disabled other things and left just simple movement script from start to the end. Maybe someone can help to figure out what is wrong ? Also, i get this error depending on how fast i make enemies, i.e., if every 0.1 second, then i get this error, when second enemy reaches the end, but if i make enemies every 0.2 seconds, then i dont get this error, and all enemies are destroyed when they reach the end.

var enemies : List.<GameObject>;
var enemiesToDestroy : List.<int>;

function Start () {
  enemies = new List.<GameObject>();
  enemiesToDestroy = new List.<int>();
}

function Update () {
  for(var enemy : GameObject in enemies) {
    /*This line gives out of index error*/
    bloon = enemy.GetComponent(Bloon);
    /*and if commented, then this line gives same error*/
    enemy.transform.position = Vector3.MoveTowards(enemy.transform.position, point.position, bloon.speed * Time.deltaTime);

    currentDistance = Vector3.Distance(enemy.transform.position, point.position);
    if(currentDistance < distance) {
      currentPoint++;
      if(currentPoint == lastPointNumber) {
        //End of track reached, adding enemy index to list to destroy later
        enemiesToDestroy.Add(enemies.IndexOf(enemy));
      }
    }
  }
//Destroying all enemies, that reached end of track
  for(var index : int in enemiesToDestroy) {
    Destroy(enemies[index]);
    enemies.RemoveAt(index);
  }
//Clearing list that holds positions of enemies to remove from enemies list and destroy
  enemiesToDestroy.RemoveRange(0, enemiesToDestroy.Count);
  enemiesToDestroy.Clear();
}

/*And same empty game object has another script that creates enemies*/
function Update () {
  next += Time.deltaTime;
  //Every ~0.1 seconds create a new enemy and add it to the list in previous script which is above
  //Here - if 0.1, then i get error in another script, but if 0.2 or more, then i dont get that error
  if(next >= 0.1) {
    enemy = Instantiate(prefabs[7], Vector3(-8, 3.8, 0), Quaternion.identity);
    gameObject.GetComponent(BloonMovement).enemies.Add(enemy);
    next = 0;
  }
}

When and where do you get that error? Line 31 in your listing looks a little suspect to me…

In regards to performance, when you’re working with a large data set it’s worth considering how your CPU will be operating on the data and the best way to store and access the data for that particular type of operation. In CPU time frames it takes ages to fetch something from RAM (a “cache miss”), during which time the CPU isn’t executing the next instruction. So where performance is important the idea is to lay out your data to minimise how often the CPU has to read from RAM before executing the next instruction.

Don’t go nuts with that piece of information, by the way. The vast majority of code doesn’t need to be optimised, optimisation without full understanding of a system often makes things worse (sometimes in subtle but nasty ways), and optimisation should only be done after evidential testing shows that there is a problem and that the proposed solution will help. So, before you learn to optimize first learn to profile. (And Unity’s Profiler is just one tool of many, and not necessarily the best for a given use case, so don’t get hung up over not having access to it without Pro.)

First i need a working code to do something else, and now i am getting an error. I tried many variants in lines 31/32, but it didnt help, so just left these for now. It looks like some function (maybe destroy) doesnt do its work immediately, but a little bit later, and when my code goes through, it catches empty object. Maybe someone knows which unity/GenericList functions are reliable and do its work immediately, and which functions do its job later/randomly ?

What’s the error? Errors tell you what line they’re on, and you can double-click them to get the line hilighted in your code editor. Also, while the message is sometimes cryptic, the error should tell you what’s wrong so you know what to fix.

omg omg omg… please read before posting, i wrote everything in post #7. Error “Array index is out of range”, line 12, or any first line to try to access variable from list enemies. As i said, if spawning enemies fast enough, then unity doesnt do something instantly, and does it later in the middle of my running code, and then my code gets empty variable from list, or try to access empty variable not in the list.

Ok, so it looks like i solved my problem for now, i made another gameobject variable obj, and destroyed objects when needed in for(var i = 0; i < enemies.Count; i++) loop with “obj = enemies*; enemies.RemoveAt(i); Destroy(obj);”, that way it works. So if i have Generic List of type , and if i use Destroy(mylist[index]) and then mylist.RemoveAt(index), it will fail to remove object from list, maybe because after destroy(object), object variable becomes null, and then list fails to remove that object, because of null or type check somewhere in its implementation.*

Is function OnCollisionEnter2D reliable ? I mean, is it called only once ? And will it be called in the middle of my any code ? I’m trying to make one script for all bullets movement, and now i also need to group all collisions handling into 1 function/script. Can anyone see what is wrong with my code ?
Errors that i get : “parameter name: index, argument is out of range” is at the bottom of CollisionEvent.
object of type GameObject has been destroyed but you are still trying to access it” in first line in function CollisionEvent, so looks like bloon game object is already destroyed.

/* Bullet.js, attached to bullet object */
//controls - general movement script

function OnCollisionEnter2D(col : Collision2D) {
  // AddCollision(bullet object, enemy object)
  controls.AddCollision(gameObject, col.gameObject);
}


/* Movement.js script, attached to empty object */
var collisionObjects : List.<GameObject>;

function Update() {
//All the movement code, it is working ok
//During movement, collisions can appear, and it can destroy objects, so it will mess movement code
}

// using lateupdate to deal with collisions, because i need a reliable way to do all this stuff
//after all movement code in update, so it will not mess movement code
function LateUpdate() {
  if(collisionObjects.Count % 2.0 != 0) {
    Debug.Log("Error #1"); // Because collisionObjects are like pairs - bullet, enemy, bullet, enemy,......
  }
  var i : int;
  i = collisionObjects.Count;
  if(i > 0) { // If there was any collisions
    while(i > 1) {
      obj3 = collisionObjects[i-2]; // this will be bullet
      obj4 = collisionObjects[i-1]; // this will be bloon
        Debug.Log("bullet = " + obj3); // to check if really bullet
        Debug.Log("bloon = " + obj4); // to check if really bloon
      CollisionEvent(obj3, obj4); // handling collisions
      //removing bloon and bullet from list
      collisionObjects.RemoveAt(i-1);
      collisionObjects.RemoveAt(i-2);
      i -= 2;
    }
  }
  if(collisionObjects.Count != 0) {
    Debug.Log("Error #2 - some objects left");
  }
}

function AddCollision(bullet : GameObject, bloon : GameObject) {
  collisionObjects.Add(bullet);
  collisionObjects.Add(bloon);
}

//Handling collisions
function CollisionEvent(bullet : GameObject, bloon : GameObject) {
  bloon.GetComponent(Bloon).lives = 0; //second error is here
  /* Some not important code here */
 
   //removing enemy from enemies list and destroying enemy game object
   index = enemies.IndexOf(bloon);
   enemies.RemoveAt(index); // first error is here
   Destroy(bloon);
 
  //removing bullet from bullets list and destroying bullet game object
  index = bullets.IndexOf(bullet);
  bullets.RemoveAt(index); // and first error is here
  Destroy(bullet);
}

Edit :
I see now, did some tests, and there is many collisions with same objects, i.e. 1 bullet with n bloons, or n bullets with 1 bloon, so need to change something to skip collisions, that cannot happen anymore because bloon or bullet was already destroyed.

So, almost made collisions handling in one script, but i get 2 errors, because somehow some enemies are not in the enemies list and some bullets are not the bullets list, so when trying to remove these objects from lists, i get index out of range errors. Also, in one for loop unity doesnt want to remove all items from list… If anyone wants to take a look, current project is http://www.filedropper.com/towers_1 .

Now now, it’s best to be polite to those trying to help. You did not write “everything” in post #7, starting with not having posted the full error message or even the line number from it.

Index out of bounds usually comes from looking past the end of a collection. Here’s a couple of common ways that can happen:

  • Forgetting that indices start at 0, not 1, so the last index in a collection is (length - 1).
  • Removing items from the collection while iterating over it and not adjusting the counter appropriately.

If that is your actual code (is it?) then I don’t think it works. You’re setting obj to point at the entire enemies collection, then passing that to Destroy. Perhaps that first statement needs to be…

obj = enemies[i];

You’ve asked about whether some stuff is delayed. The answer is yes. Specifically, Destroy marks things to be removed at the end of the frame. Operations on collections are immediate, though. You’re not getting out of bounds errors as a result of something being delayed by Unity. (Are you using threads, though? If so you could be causing delays yourself.)

My recommendation is to make 100% certain that you’re being consistent about putting things into and taking them out of those lists.

Three common ways to do this:

  1. In the appropriate components, have them add themselves to the lists in Awake() and remove themselves in OnDestroy(). This makes list management transparent to the rest of your code.
  2. Make a “manager” class that handles spawning and destruction for you. Have a SpawnBlah(…) and DestroyBlah(…) method for each list you need to maintain, and have those create/destroy the objects and add/remove them from the list at the same time. Then make sure this is the only way you’re spawning those things.
  3. Similar to 2, but instead of a manager it can be a pair of static methods on your Bullet and Enemy classes.

Oh, that was just typing mistake, in my code that part is correct. I’m not using any threads, unless unity is using some kind of its own multithreading. And i’m instantiating bullets and adding them to the list only in one place - in tower script, it finds nearest enemy, then creates bullet and adds it to the list, and i’m destroying the bullet only in one place, that deals with collisions, but somehow somewhere some bullets still dissapear/or dont get added tot the list…

I made some progress today, decided to move back collision code to bullet script because i made some variables global and now have fast access to it, plus i dont need collision objects list, and all errors disappeared. Also, tried to improve the game by trying to remove the effect of objects pushing each other away on collision, did that by editing bullet object/prefab - by making rigidbody kinematic, checking istrigger in collider2d, and then implementing ontriggerenter2d function. Also, that improved performance by a bit. For now, i’m thinking about 3 things, maybe someone can help :

  1. Physics2d calculations are still taking a lot of cpu in the late game (fps drops to 15 fps and lower when game is in action) - Physics2D.FixedUpdate is taking 60%-80% of total, half of which is taking Physics2D.Simulate and the other half by Physics2D.ContactReportTriggers. Maybe there is another, faster way to make simple 2d collisions ? Maybe it would be possible to implement myself a simple 2d collision detection ? Because all i need is to detect collisions of objects, nothing else.

  2. Right now my game speed is based on time by multiplying variables by Time.deltaTime. If the game starts lagging really bad, then many things will just start teleporting, so i’m thinking about making game speed based on frames and then making max fps 60 or 30. To remove time dependency and move to frame dependency, is it enough to just remove multiplication by Time.deltaTime ?

  3. Question about 2D graphics : what kind of pictures should i use for 2d game to have good quality/performance ? Does it matter much for performance ? What kind of settings should i change for picture inside unity ?

1.
With regards to the physics slowdown, how many physics objects do you have?

Also, how are you using Rigidbodies? I don’t know about the 2D physics system, but in the 3D physics system anything that moves should have a Rigidbody. It’s counter-intuitive, but basically all of the static objects are stored in a way that’s optimized based on the assumption that they won’t move. If you move them then that data has to be modified, which is expensive. If you attach a Rigidbody (and mark it as kinematic, no gravity) then it’s stored in a way optimized for it to move. So, in short, if it moves then it should have a Rigidbody component.

It is quite possible that a custom implementation for bullets could be faster than the built in one. It’s a fairly specific use case where you can safely make assumptions that the general purpose solver can not. Don’t even consider this until you’ve confirmed that the built-in solver isn’t up to your requirements, though.

2.
If Time.deltaTime isn’t stable enough for you, I’d suggest looking at FixedUpdate and fixedDeltaTime. These run at a fixed rate that is not tied to the frame rate, and which you can control as one of your project’s properties.

3.
What do you mean by “kind of pictures”? If you’re referring to the format of the image file, Unity will convert the data to a platform-specific internal format when you import it. It will not use the image in whatever format you save the file in. So, use something lossless. I use 32bit PNGs, since these use lossless compression and support an alpha channel, covering all necessary bases.

As for import settings, the best way to get started is to stick your texture on whatever you’re going to use it on and tweak the settings until it looks best. Note that since Unity uses different formats for different platforms you might need to set this per-platform.

  1. Now i have enemies with only box collider 2d attached, and bullets with circle collider 2d and rigidbody2d - its minimum amount of components to make collisions/triggers to work. Fps drops to about 10 with cpu taking about 103ms (72ms - physics), game objects in scene ~1500, total objects in scene ~7700.

  2. Time is not good in a situation, when the game fps drops to 20 and lower, then every object will be moving by a large distance, so there will be situations where bullets will just teleport over enemies without collision in one calculation. Fixed update might help, but i also would like to make that slow action effect when fps drops. I tried removing deltaTime and correcting speeds, and looks like it works.

  3. Yes, i mean picture format, and also picture resolution, what kind of impact does it make to quality/performance, especially if there is performance hit for cpu, because gpu is showing over 1000 fps right now, so i have plenty of space left for improvements for gpu, but i dont want to make it any harder for cpu.

I thought a little bit about objects pool system. Is there any examples ? What i thought is, i would make some more lists just to hold objects, and when user presses start button and level starts, then i instantiate enough prefab objects for one level and put them all in one of temporary lists (only objects in main list are active and executing scripts), then when i need to spawn some objects, i would just take enough objects from temporary list, change its variables how i need, and add it to the main list for action. Would my idea work, would it increase performance versus what i have now - instantiating objects and adding to the main list on spawn timer ?

Also, how about garbage collector ? can i control how often it turns on ? Maybe i shouldnt destroy my objects immediately after they die, maybe i could just remove them from main list, and then after level ends or just a few times during the level i could run garbage collector, or something like that ? Could it increase cpu performance ?