- Home /
Optimization - Distance vs Collisions
I'm wondering if anyone can give me a tip on optimizations.
Right now, my enemy objects each contain an empty gameObject with a "wake-up" collider attached to them. If the player enters the wake-up collider than the enemy (enemy is a child of its wake-up gameObject) becomes active.
In the interest of optimization, I'm wondering if it would be less resource intensive to calculate the distance between the player and the enemy, say every 1 second, as apposed to using the OnTriggerEnter function of the collider. My thinking is that Unity will need to process/calculate all interactions that the enemy's collider may have with other colliders that are not relevant (terrain, other enemies etc) - and every frame. Whereas if I just calculate the distance every 1 second or so then.. 1.) Only the calculations for the distance from the enemy to the player will be performed 2.) Only performs the calculation every X amount of time as apposed to every frame
Does anyone know if this can lead to better optimization, or are Unity's collision detections so efficient that they'd be faster?
Thanks!
I will think the distance every second is better BUT what is the use of it? If you think of distance between your player and the enemies the distance is fine between each new NPC can easily get a reference to your player when instantiated.
If you think of enemies together then the collision seems better to me since you get a reference to the colliding object. If you check the distance between two NPCs, you would still have to either use a raycast or to iterate through all the enemies to figure out which ones they are. Hopefully you follow me.
Actually I don't instantiate enemies. They're all within the scene/level and in different states. An inactive state until the player comes within their reach or visible distance, when it then becomes active. And vice versa.
The Profiler was showing that instantiating gameObjects seems to use a decent amount of system resources whereas activating a gameObject already within the scene is much less resource intensive.
When you say "If you think of enemies together" do you mean physically located near each other, as in a group of enemies? $$anonymous$$y enemies all act independently and are generally not anywhere near each other.
With regards to using a raycast, I'm not sure I follow.
$$anonymous$$eep the collider (as trigger) and fix the layer collision matrix so enemys trigger (theChild) collides only with player. This way your enemies collides with terrain etc, but trigger checks only against player. As far as i know, distance calc is much more greedy. $$anonymous$$ore info: http://docs.unity3d.com/Documentation/Components/LayerBasedCollision.html
Interesting, chelnok. I wasn't aware that you could have collisions and triggers only occur on certain layers. So by the sounds of it, if I have a "playerTrigger" layer, the enemy wake-up collider triggers will only interact with the player's collider and other enemy's colliders. This sounds like it could be the way to go.
Answer by Karsnen_2 · Nov 14, 2012 at 07:37 PM
The thoughts I have for your type of requirement would be,
a) Have an Update function on every enemy/NPC and calculate the sqrmagnitude on the difference between this position and Player position. If the is less than some value, then activate.
b) Try to create a event or delegate system with a for loop on a empty gameobject on the scene (not on the player). Do the sqrmagnitude, difference on the player and all the NPC (foreach). Trigger the event when the sqrmagnitude is less. Now the reason for using an empty gameobject is because of the usage of a foreach loop. As Player might have a lot of functions running on Update().
c) You could do (b) by invoking a function which would have a foreach statement every 'n' seconds. This would reduce a much of load.
But, I think using OnCollisionEnter is better. I do not get the reason where you say it is resource intensive as opposed to calculating every 1 second.
Well I'm not actually saying that it is more resource intensive, I'm just asking that question :) $$anonymous$$y suspicion is that the colliders in my case are more resource intensive than calculating distance because of the calculated collisions with other unrelated colliders.
For example, any given enemy's wake-up collider could be colliding with a series of other gameObject colliders that it doesn't need to be concerned with. Things like terrain, other enemies, and other items. Nonetheless, even though it doesn't need to be concerned with those other collisions (only the player collision) it is still calculating for them all.
Right now, the average enemy is colliding with anywhere from 10-200 colliders (every frame) but since the enemy isn't even active at this point really I only need to be concerned with 1 collision - the collision with the player's collider. So my thinking is that by removing the enemy's wake-up collider and calculating the distance to the player every 1 second I'll be saving the resources that would otherwise be taken up by the 10-200 unnecessary collisions.
Does any of that make sense? :)
cassius : I totally understand your requirement. If you could flock enemies together then do it and create a central reference point to that group. Activate the entire group when you need to. In this way, enemies flocking need not worry about colliding against 100 plus other collider. Similarly they need not be awake until required.
By the way, if you happen to find any solution, do enlighten me.
Ahh yes I see what you're saying now. Unfortunately I'm not sure how well that would work in my case as the enemies are generally a decent distance apart from each other. $$anonymous$$y game is very linear. See what I mean here: http://youtu.be/TVN$$anonymous$$hXJw_N$$anonymous$$ - skip to about 25 seconds in, I die first two tries. LOL. Enemies are the turret guns.
Also, it would require additional information on the player's distance for enemies that are further down the player's path so that their animation sequences (which aren't enabled in the video linked above) don't start too early and that they also don't start firing at the player way ahead of time. :)
I'm not at home right now, but I'll give each option a try tonight and check out the Profiler to see how well each does and update you.
About the game:
It is pretty cool. I like the concept. The evasion is always vertical. Try to think of some object which could lay horizontal. The shaders are pretty impressive. How did you make them and what are all are you trying to do? The tree and the leaves are also good. So, what is your platform?
About you question :
I think you are looking to activate the turret, if and only if the player is at certain distance from the turret. Well, you could ray cast from the Player (spaceship). Activate them so they are ready. And once you cross them. i.e. you z distance is larger than theirs - disable them.
Let me know, if you need more help.
About the game: The evasion is always vertical in that video. There are now tree turrets, flying enemies and obstacles. That video was from a week or two ago. Intended platform is PS3, I hope.
About your answer: I'll need to familiarize myself with raycasting. I've never used it or even really looked at it.
Thanks again for your help, $$anonymous$$arsnen!
Answer by cassius · Nov 15, 2012 at 09:50 PM
Sorry to post this as an answer but it was too long for a comment.
Karsnen, I've had a few minutes to check these out in Profiler now.
chelnok's suggestion of using the colliders on a distinct layer that only the Player interacts with was difficult to track down in the Profiler. So I'm honestly not sure of the results.
As for the approach of checking the sqrmagnitude distance between the player and the enemies, I've managed to get the processing time down fairly low. Right now the most I've seen it take is 0.30ms, which I think it acceptable. That's the total time for calculating the distances to about 100 or so enemies and other items in the environment that I needed control over. In combination with a Yield in the coroutine which only checks one enemy per LateUpdate, I feel like this is fairly streamlined, but could stand corrected. :)
Here's my current sourcecode.
/* This script is designed for SuperSpaceTrooper.com. It will enable and disable
child objects based upon their distance to the player. It may be useful
where you're trying to save resources if there is a large number of child
objects. This script should be placed on the parent object */
private var player : GameObject; // Get the player so we can check for distance every X seconds (distanceCheckTime)
private var distanceCheckTime : int = 1.0; // Time between checking distance of player to the bush
var visibleDistance : int = 100; // Distance between player and bush before being visible
private var tempTime : float = 0; // Holds the next time to check time
private var theObject : Array = new Array(); // Cache objects so we don't need to access them directly (Profiler)
private var theLocation : Array = new Array(); // Cache locations so we don't need to access them directly (Profiler)
private var theStatus : Array = new Array(); // Cache status so we don't need to access them directly (Profiler)
private var arrLength : float = 0; // Holds the array length of theObject
private var cachedPlayerPosition : Vector3;
function Start () {
visibleDistance = visibleDistance*visibleDistance;
player = GameObject.Find("Player/Ship/Ship");
for (var child : Transform in transform) {
// Lets us specify which child to enable/disable based on distance.
child.transform.gameObject.SetActiveRecursively(false);
theObject.Push(child.gameObject);
theLocation.Push(child.position);
theStatus.Push(false);
}
arrLength = theObject.length;
}
function LateUpdate () {
if(tempTime <= Time.time) {
// Cache the player's position so we don't have to access it every loop in CheckShowOrDontShow(). Saves CPU (Profiler)
cachedPlayerPosition = player.transform.position;
CheckShowOrDontShow();
tempTime = distanceCheckTime + Time.time;
}
}
function CheckShowOrDontShow() {
// Get the distance to all the objects which enable or disable based on distance
for (i=0;i<arrLength;i++) {
if (player && theObject) {
if(theStatus[i] == false) {
if((cachedPlayerPosition - theLocation[i]).sqrMagnitude <= visibleDistance) {
/* Then we tell it to enable.
Alternatively, we could just enable and disable materials, but I
feel like completely disabling the objects may save resources. */
theObject[i].SetActiveRecursively(true);
theStatus[i] = true;
}
} else {
// We should also disable currently enabled objects if they're farther than visibleDistance
if((cachedPlayerPosition - theLocation[i]).sqrMagnitude > visibleDistance) {
theObject[i].SetActiveRecursively(false);
theStatus[i] = false;
}
}
}
yield; // Delays each enemy distance check by one LateUpdate.
}
}
I know that this is an old thread, but I wanted to follow-up in case people come across this in the future.
I have deter$$anonymous$$ed that, at least in my project, perfor$$anonymous$$g the distance check on the objects from the player's distance every X seconds is indeed faster than relying only on collision layers. While I did end up grouping some objects together, the results were pretty obvious. Whereas before I was achieving between 30-45 FPS, I'm now getting between 80-120 FPS. A significant improvement.
Answer by Max_Bol · Apr 08, 2016 at 06:46 AM
I would like to add some important information related to this :
Getting distance X times per a specific amount of time should be priories when :
• If you plan on having multiple Triggers zones overlapping each others. There are many issues with the Tiggers colliders in Unity (even Unity 5). One being that having too many calls from collisions can end up with some triggers being ignored.
• If some Trigger collider might be in contact with multiple none-trigger colliders. This is another issue with Unity. Having a trigger collider from which some none-trigger colliders intersect too much of its "zone" can add a kind of glitchy collision to the trigger itself. Whenever I put a trigger collider and it has around 2/3 or more of its "zone" covered with none-trigger colliders, anything with a Character Controller seems to bounce off from it through not completely (the trigger still works, but well... you can't move within the zone anymore).
• If you require a trigger that is really big. While "zoning" can be done with triggers, a problem with triggers is that they are still getting their checkup on every OnTriggerEnter(), OnTriggerStay(), OnTriggerExit() even if those aren't used. It also create an interne list of whatever is within it. Triggers can't be turned into stand-by unless turned off. You can't have control over the "refreshing rate" of the checks either. Whenever any collider or Character Controller enter or stay or exit a trigger collider, its functions are called at every frame even if empty. So, a big zone will mostly be called every frame since the chance of it having absolutely no colliders within might never be real.
• If your zoning/collision trigger is either a sphere or especially rectangle prisme or square prisme. As you can controls such "zone" with ease (Make use of a radius or 3 sizes) and do a check on specific target at specific point of time. Even better, you can actually build a list of asset (and update it) from which the "zone" can look at and get whatever it needs to check. In other words, recycling a maybe-already-existing list you might have created for another purpose.
• If your zone is moving. Trigger zone that moves requires a Rigidbody... and rigidbodies, while optimized, still take a lot memory more than a raycast. Triggers that aren't with a Rididbody but still move is really taking a toll on the memory because every time it move, it's trigger is getting reconstructed at the new position while, with a rigidbody, the "center" zone of the trigger is based on the rigidbody's physic system.
Trigger colliders could be useful for :
• You require a really specific and complex form for the trigger zone. Well, if you require a zone that is quite complex and precise, it's quite a better option than doing a kind of insane amount of raycast from many points with different distance and all. (For example, a laser grid-like trigger zone with a bunch of specific complex sized holes for the player to move in)
• For area with little chances existing collision. For example, the transition zone at the edge of a map could be launched from a Trigger collision. As you can make the trigger zone into the air and leave the open transition zone empty from anything. (Not necessary fully empty, but leaving a "slice" of space empty which will include the trigger zone.
• If you requires to get data from the trigger. It's surely more simple to get the data from the trigger's collision than doing it from a series of raycast since triggers (as well as collider) register any points of entry or exit from other collider (the good side of the 3 previously mentioned functions that are automatically called).
Those are the general points about whenever you should check distance or use a Trigger Collider.