- Home /
Enemy with character controller has inconsistent hit detection
I've created an enemy and attached a character controller to it. In the past, it had a simple capsule collider set as a trigger, to be used to detect player bullets. All was fine until I started using character controllers instead so that enemies don't pass through obstacles. Now that I've changed it, the bullets will register seemingly only half of the time, and even then the bullets are not being destroyed as they should. The other half of the time they will just pass through without causing any damage.
This is the code on the enemy detailing the collision detection for player bullets:
function OnTriggerEnter (other : Collider)
{
if (other.gameObject.tag == "Bullet")
{
var bullet1 : BulletScript = other.GetComponent(BulletScript);
health -= bullet1.damage;
}
}
And this is the bullet's code detailing enemy collision:
var damage : int;
function OnTriggerEnter (other : Collider)
{
if (other.gameObject.tag == "Environment")
{
impact.PlayClipAtPoint(clank, transform.position);
Destroy(gameObject);
}
else if (other.gameObject.tag == "Monster")
{
impact.PlayClipAtPoint(hit, transform.position);
Destroy(gameObject);
}
}
The bullets have a box collider set as a trigger and a rigid body.
I probably should not be using OnTriggerEnter on the enemy, but nothing else has worked. Not OnCollisionEnter, not OnControllerColliderHit (the game thinks the bullets are Untagged when I do this, even though they're not). Changing the bullet's function to OnControllerColliderHit has the same effect as OnTriggerEnter. I've tried adding an extra capsule collider on the enemy, but that only causes hits to register twice. So unless I've been using these functions incorrectly, I don't know how to fix this.
Answer by aldonaletto · May 18, 2013 at 10:40 PM
Usually bullets are rigidbodies and use OnCollisionEnter to detect when something is hit (bullet script):
var damage : int;
function OnCollisionEnter (col : Collision)
{
if (col.gameObject.tag == "Environment")
{
impact.PlayClipAtPoint(clank, transform.position);
Destroy(gameObject);
}
else if (col.gameObject.tag == "Monster")
{
impact.PlayClipAtPoint(hit, transform.position);
Destroy(gameObject);
// try to get the enemy script EnemyHealth:
var eScript: EnemyHealth = col.gameObject.GetComponent(EnemyHealth);
// if found, decrement the enemy health
if (eScript) eScript.health -= damage;
}
}
The enemy script don't even need to know that it was hit - the bullet script itself can do the job like above.
Maybe your problem is being caused by the discrete nature of collision detection: when a projectile moves reasonably fast, it may be before the target in one frame and after it in the next, and no collisions are detected. A simple solution is to elongate the bullet collider a lot, so that it extends way ahead the bullet:
Since the physics default rate is 50 cycles per second, a collider with about 0.5 meter long can travel at up 0.5 * 50 = 25 meters per second without miss any hit - smaller colliders at this speed will depend on the target width in order to not miss the collision.
Excellent answer aldonaletto! I was recently struggling with collision between enemies (who were using the Character Controller but not always moving) and projectiles trying to kill them. Was simple to use OnTriggerEnter when one of them was a trigger. I noticed that whichever object had the "Is Trigger" checked would not do any kind of collision with other static rigidbodies.
Using OnCollisionEnter as you've described above looks to have solved all the issues ;)
Yes, static triggers can detect rigidbodies or CharacterControllers that hit them, but a moving trigger only works if it has a rigidbody (usually a kinematic one).