- Home /
OnCollisionEnter vs OnTriggerEnter for performance
I am looking at a bullet script and trying to find the most efficient way of detecting bullet damage. I see two options:
Have an OnTriggerEnter script attached to all damageable items (enemies, crates, barrels, etc.) checking if the entered object is tagged as a bullet, apply damage and destroy bullet.
Have a single OnCollisionEnter check in the bullet script itself.
I know OnTriggerEnter is more efficient than OnCollisionEnter, but is anyone willing to take a guess on which of the above approaches is the more optimized route?
Wouldn't a raycast be more efficient than a collision detection, given the speed of a bullet ?
OnTriggerEnter should be more optimized..... I am guessing too.
Berenger, the bullets in my game are slightly slower moving than a real bullet.
I agree with @flamy: OnTrigger events pass only a reference to the other collider, while OnCollision events pass a lot of info - this allocates memory and takes a reasonable time to do.
Answer by aldonaletto · Nov 11, 2013 at 11:37 PM
That's weird: are the damageable items triggers? Or is the bullet a trigger? OnCollision and OnTrigger events usually are mutually exclusive: if the bullet is a trigger, OnCollision events will never occur; if it's a rigidbody with a regular collider, OnCollision events may occur when it hits a collider, not a trigger.
Anyway, fast bullets don't generate collisions/trigger events reliably because they may be before a collider in one physics cycle and after it in the next. A good and efficient solution is to calculate the bullet trajectory as a sequence of line segments: calculate the bullet position each frame and do a Physics.Linecast between it and the previous position in order to know whether anything was hit. This also makes it easy to apply gravity to the bullet, since you're calculating the trajectory. You don't need a real bullet, just a coroutine that does the simulation like this:
var bulletSpeed: float = 100;
var force: float = 20;
var damage: float = 10;
var gravity: float = 9.8;
var range: float = 1000;
function Shoot(pos: Vector3, dir: Vector3): IEnumerator {
var vel: Vector3 = bulletSpeed * dir.normalized; // calculate velocity vector
var dist: float = 0; // initialize distance travelled
while (dist < range){
yield; // let Unity free till next frame
vel.y -= gravity * Time.deltaTime; // apply gravity
var newPos: Vector3 = pos + vel * Time.deltaTime; // calculate current position
var hit: RaycastHit;
if (Physics.Linecast(pos, newPos, hit)){ // if something hit...
if (hit.rigidbody){ // apply impact force if it's a rigidbody
hit.rigidbody.AddForceAtPosition(force * dir, hit.point);
}
// call ApplyDamage(damage) in the hit object:
hit.collider.SendMessageUpwards("ApplyDamage", damage, SendMessageOptions.DontRequireReceiver);
return; // shot ended because bullet hit something
}
dist += Vector3.Distance(pos, newPos); // update distance
pos = newPos; // update position
}
// shot ended because it's out of range
}
Supposing that you shoot from the weapon position and in the weapon forward direction, this function may be called like this (weapon script):
StartCoroutine(Shoot(transform.position, transform.forward));
or simply:
Shoot(transform.position, transform.forward);
If you really want a bullet model, simply update the model position in the last line of Shoot, and remember to destroy or deactivate the model when the bullet hit something or when it goes out of range.
Hey thanks for the script. That looks more robust than my system — which FYI has a trigger on any items/enemies that can be hit by a bullet — although I am concerned about the performance implications in your script for doing so many Physics.Linecasts for a mobile game. If the player is firing a machine gun, there could be 20+ linecast checks happening per Update. Would your script still be okay under those circumstances do you think?
That's a good question. Apparently, a virtual bullet is much less expensive than a rigidbody bullet: it spends only a Linecast and simple math per frame, while a rigidbody internally requires a lot more per physics cycle: solving differential equations numerically, checking collisions etc. On the other hand, the physics engine is highly optimized and executes in machine code, what's a lot faster than our scripts. Anyway, I would bet on virtual bullets: checking collisions seems a lot more complicated than doing a linecast, and a rigidbody has a heavy load on its shoulders (game object, transform, renderer) while the virtual bullet has none. The only way to know is to test both alternatives.
Good points. Although I believe a rigidbody is not doing much if it's set to kinematic, right? I'm not actually using any rigidbodys in my OnTriggerEnter approach, as those checks are happening on damageable items and looking for a bullet. Although there is definitely a lot of merit for further exploring your version above too. :)
You're right: a kinematic rigidbody is way lighter, since basically the only thing it does is testing collisions - and in a more simplified way, because there's no reaction. $$anonymous$$aybe a kinematic bullet moved by code and trigger targets is the fastest choice.
Your answer
Follow this Question
Related Questions
OnTriggerEnter conflict 1 Answer
Have bullets be destroyed when they collide with ANY collider 0 Answers
Colliding two GameObjects 1 Answer
onCollisionEnter vs onTriggerEnter 1 Answer
My game after being built has some really weird glitches? 0 Answers