I have about 350 agents for an rts game (ideally I’d like to have as many as possible), and my problem is that I only know two ways to “position” my agents on the terrain, and both ways are pretty expensive (processor-wise).
My first way (and currently the fastest/best) is that I turn off gravity and collisions between my agents and the terrain collider in the physics settings. I then physics.raycast from about 20 meters above the agent against the ground layer every Update (the main update function) and tell the agent what height it needs to be at at all times. While this is faster than my second way, it still adds a good chunk of ms to my main thread.
The second way (which is doable, but noticeably laggier) is that I turn on gravity and collisions between the ground and my agents, and allow the agents to “rest” on the terrain collider. I can then turn off my raycasting code of course. But the collisions between my agent’s colliders, and the terrain collider cause a lot of lag when larger numbers are involved (about 150+ agents when moving).
Colliders are a big problem. All of my agents have one simple box collider on them. And when all of them are colliding with the ground, it starts to drop the framerate and up the main thread ms significantly.
All in all, is there a faster/better way to position and move lots of agents on a terrain?
If I were you I wouldn’t use rigidbodies for your units unless you actually want them to interact with forces such as explosions and crashes, you will end up using a lot more CPU time like you were saying. An update queue that only allows so many updates per frame and a light path-finding solution would be the way to go, in my opinion.
How would I go about limiting the updates per frame? I’m assuming you mean only physics.raycast for positioning every 5 update cycles instead of every update cycle correct?
Note: I currently have a pretty light pathfinding solution. When I turn off all of my colliders, I have tested about 500+ agents moving with negligible framerate drop or lag. It seems to be mostly collider physics doing the lagging.
You could use a coroutine instead of the Update function, and then you can choose your own update speed (as well as getting a slight performance boost; Update is “expensiver” to call).
function Start() {
DoMyUpdate();
}
function DoMyUpdate() {
while ( 1 ) {
yield WaitForSeconds( 0.5 );
// stuff;
}
}
Yeah, even when you move a collider with something like transform.Translate you’ll still get a Physics.Update hit if you watch your profiler. You could use a (System.Collections.Generic) Queue to have your agents “request” to be updated, then only process the first few in the beginning of that queue.
Thanks for the code Loius, I get all of it except for what goes in the “while ( 1 )” part. What does that “one” represent or what specifically would go there?
While I have some knowledge of code, I am still learning, thanks
And to Kyle: That sounds awesome. But I think what you are suggesting is way above my current coding ability right now. I know what a queue is, however I honestly have no idea how to even start coding one in Unity. I will however keep that in mind and look into it if I can.
Creating a queue goes something like this…Keep an array of your agents and an index. On each frame peel off the first five in the array that have not been used yet and update those and the index number to keep track of where you have updated to in the array. When the array is exhausted then just loop the index around to reiterate through the array again…and again…and again… This way if you have 30fps then in one second you can update 150 of your agents. If five gives good framerates then try six agent updates per frame, then seven per frame etc… etc, until you get a frame lag too much for your uses.
I use raycasts to keep my ai following terrain height.
//setting up the raycast var
var hit : RaycastHit;
//if the raycast (from the enemy (-up) downwards) hits something below then it will
//put the value in the hit var
if (Physics.Raycast (this.transform.position, -Vector3.up, hit)) {
//this is the distance from the hit point and the enemy
distanceToGround = hit.transform.position;
//DEBUG STUFF
//print("Distance: "+distanceToGround);
//print("Hitting "+hit.transform);
//the commented line below draws a line in the editor so that I can check it is working
//Debug.DrawLine (this.transform.position, hit.point, Color.red, 1);
//this moved the enemies position from where it was(floating) to the floor
this.transform.position = hit.point;
}
That loop will repeat until “x” evaluates as false - since 1 is not false, the loop will repeat until the game is stopped.
Sometimes you need to break out of a while in a rare situation; for instance, I have some while checks that go like this:
while ( 1 ) {
// stuff
if ( !gameObject.active ) break;
// stuff
}