Collider does not always detect OnTriggerEnter
Dear Unity Community,
I stumbled across a problem while working with the book "Unity in Action". At the end of chapter 3 you'd end up with the basics for a simple fps game. Since I really liked this basic functionality I tried to expand it. I basically have a player (camera attached to it) which can walk and look around in a simple and small level which only exists out of a number of cubes forming walls and the floor etc. These cubes all have box collider on them. Now the player is also able to shoot at moving enemies which are also able to shoot. This was done by Raycast/RaycastHit. All this worked perfectly so I wanted to add something that reassembles bullet holes, simply by instantiating a black sphere object on the wall where the fireball (this is the object the enemy and the player shoot) hits it. Basically this also works BUT sometime the fireball object just goes through the wall. In case there is another wall behind it, the fireball object gets destroyed by this second wall and the desired sphere is created on the second wall instead of on the first wall.
EDIT: I changed the speed in the fireball from 20 to 10 and then back to 20 and it seems like at a speed of 10, the success rate is around 19/20 so there is an improvement but I still think there is a logical flaw somewhere!
Here is the code of the fireball which is supposed to check whether it hits the player (then health is deducted, that works fine) or hits the enemy (then the enemy falls over, also works fine) or hits the wall in which case the sphere should be created (that works in 7/10 cases approximately, success rate is very random which makes it hard to understand)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Fireball : MonoBehaviour {
public float speed = 10.0f;
public int damage = 1;
[SerializeField] private GameObject wallHit;
private GameObject _wallHit;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
transform.Translate(0,0, speed * Time.deltaTime);
}
void OnTriggerEnter(Collider other){
RaycastHit hit;
PlayerCharacter player = other.GetComponent<PlayerCharacter>();
ReactiveTarget target = other.GetComponent<ReactiveTarget>();
WallBehavior wall = other.GetComponent<WallBehavior>();
if(player != null){
player.Hurt(damage);
}
if(target != null){
target.ReactToHit();
}
if(wall != null){
if(Physics.Raycast(transform.position, transform.forward, out hit)){
wall.WallWasHit(hit);
}
}
Destroy(this.gameObject);
}
}
As you can see one thing I tried was to give each wall a WallBehavior script, which looks like this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class WallBehavior : MonoBehaviour {
[SerializeField] private GameObject wallHit;
private GameObject _wallHit;
static int count = 0;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
public void WallWasHit(RaycastHit hit){
count++;
Debug.Log("Wall was hit: " + count);
_wallHit = Instantiate(wallHit) as GameObject;
_wallHit.transform.position = hit.point;
}
}
But none of my attempts to understand why this happens so infrequently was successful so far and I hope someone can help me with this because I feel like might be crucial for my learning before I continue with the book! Thanks in advance. if further information are needed, I am happy to provide them. See the following picture for a better visualization of the issue.
Answer by marvinb89 · Apr 12, 2018 at 09:03 AM
Found the solution on stackoverflow. So I changed the movement of the fireball which happens now in the start function, using rb.velocity. But what really solved the whole issue in the end was to change the value Fixed Timestep (Edit > Project Settings > Time) to a lower value. It was initially on 0.02 and I changed that to 0.001 and now it works perfectly!
Answer by meat5000 · Apr 03, 2018 at 01:43 PM
void Update () {
transform.Translate(0,0, speed * Time.deltaTime);
}
Expecting Collisions to work properly whilst moving with Translate is a FAIL situation.
For a similar approach without using Forces you can use MovePosition method of the Rigidbody.
Thanks for your time and feedback. I implemented your solution into the FireBall-Class like in the code block below. The result is actually exactly the same, some go through and some work just fine. Did I do something wrong in the way I implemented your solution?
public Rigidbody rb;
void Start () {
rb = GetComponent<Rigidbody>();
}
void FixedUpdate(){
rb.$$anonymous$$ovePosition(transform.position + transform.forward * Time.deltaTime * speed);
}
I forgot to mention that the docs specify to Enable Rigidbody Interpolation for this to work. If you find that collisions are working up to a certain speed but not beyond, 'step up' the level of collision detection. e.g up to 'Dynamic', or 'Continuous Dynamic'. Its all explained in the docs (not the scripting api), but here's a forum page on discussions around it
I did this as well, same result. So now I replaced the Update code with your FixedUpdate version, then I tried addForce or Rigidbody Velocity, changed the collision detection, changed the Interpolation but it just won't work, there are always some "fireballs" going through the wall.
Which colliders are the fireballs using? $$anonymous$$ovePosition will specifically help detect a collision to fire off the OnCollisionEnter routine. This should work even if the fireball travels right through. Perhaps you could use this to create a manual deflection/absorption of the fireball. Just re-read your code. If you colliders are marked as triggers the fireballs will ALWAYS travel right through :P
Well even if the collider is a trigger doesn't change much right? Because the gameobject, here the fireball gets destroyed anyway. The crucial part is not whether the fireball goes through or not but if the sphere (which should reassemble a bullet hole) is created at the right spot. But to come back to your question, it is a Sphere Collider, the wall has a Box Collider. Right now in my project I need to make either the fireball a trigger, otherwise the fireball won't be destroyed but bounces back from the wall or the enemy. If it is a trigger, it does everything perfectly, it hits the enemy, the enemy falls down, the fireball gets destroyed. It hits the wall, the wall has a visible sphere on that spot and the fireball gets destroyed. BUT as soon as I increase the speed of the fireball movement, the sphere is not created on the wall anymore, leaving no mark behind...That is the main issue!
So with a low speed it works just fine but this fireball is supposed to be sort of bullet-like so of course the speed should be quite high right? And that's where the problem starts to become really obvious. See the following video I took real quick to illustrate: