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:

  1. 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.
  2. 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?

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.