Search Unity

OnTriggerEnter being called twice

Discussion in 'Scripting' started by Robdon, Feb 7, 2016.

  1. Robdon

    Robdon

    Joined:
    Jan 10, 2014
    Posts:
    141
    I have the following code on a cube object that is flagged as a trigger, and the first person controller in the project
    with a panel as a floor.

    Walking into the cube and triggering it, it basically duplicates the trigger object, removes it, moves the player back to the 0,0,0 pos, and then moves the duplicate into the same position as the original.

    The problem being, this 'OnTriggerEnter' event is called twice. Once for the original collision, and then again for the duplicated object.

    Code (CSharp):
    1.     void OnTriggerEnter(Collider other) {
    2.  
    3.         // Note, 'this' object (a cube with trigger) is at position -5.0,0.5,0.0
    4.  
    5.         UnityEngine.Debug.Log("OnTriggerEnter:" + other.name + " from object " + gameObject.name);
    6.  
    7.         // Duplicate this object out of the way at 100,100,100
    8.  
    9.         GameObject newGO = Instantiate(gameObject,new Vector3(100,100,100),Quaternion.identity) as GameObject;
    10.         newGO.name = "ExitPoint2";
    11.  
    12.         // Destory this object
    13.  
    14.         Destroy(gameObject);
    15.  
    16.         // Move the character back to 0,0,0
    17.  
    18.         GameObject.Find("FPSController").transform.position = new Vector3(0,1,0);
    19.  
    20.         // Move duplicate object into the same position this object was in
    21.  
    22.         newGO.transform.position = new Vector3(-5.0f,0.5f,0.0f);
    23.  
    24.     }
    This is just a test scenario to show the problem.

    The 'real' case I have is a scene that is blank, and I load in the room/objects from a file, then the user walks around the room, finds the 'exit point' (which is what this trigger is) and when they walk over it, it clears the room and then creates a new room based on a file. If the new room happens to create the exit point in exactly the same position, then it immediately triggers OnTriggerEnter again on that object.

    Firstly, I'm guessing this is incorrect behavior of the trigger, and should be reported as a bug?
    Secondly, can anyone think of a way around this to stop the event getting triggered twice?
    |
    Thanks, Rob.
     
  2. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    It's not a bug. It seems weird but it's actually correct according to the working of the engine.

    You have to delay the movement of the duplicated cube by a frame or a small amount of time. This can easily be achieved by the following:

    Change the return type
    Code (CSharp):
    1. IEnumerator OnTriggerEnter(Collider other) { ... }
    Then, before you move the duplicate back, have this line
    Code (CSharp):
    1. yield return null;
    That'll stop execution for this frame and continue the next frame. One thing that needs to be changed additionally is the destruction of the object, you have to delay it aswell in order to have it in the next frame and allow the rest of the method to be executed.

    So it should look something like:
    Code (CSharp):
    1.  yield return null;
    2. newGO.transform.position = new Vector3(-5.0f, 0.5f, 0.0f);
    3. Destroy(gameObject);
    That should actually do the trick.
     
    T-Zee, alexeu and Robdon like this.
  3. Robdon

    Robdon

    Joined:
    Jan 10, 2014
    Posts:
    141
    Ah yes...

    I was playing with putting it in a Coroutine, and using yield, but I didnt really understand 'why' that fixed it.

    But, your fix/explanation is cleaner. I didnt realize you could put IEnumerator on standard methods like that, nice.

    I also didnt realize that changing the position didnt actually happen 'immediately', so I get now why I need a yield.

    BTW, is there anywhere that tells you what is 'late' running in Update like this? I see it tells me in the 'Destroy' methods documentation that it is run after Update and before rendering, so I knew that, but I couldnt find anything telling me that changing position would work like that.

    Thanks for your help.
     
  4. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    Actually, it takes place immediately, as you can already log those new positions. So the position must be set instantly. It's probably more related to the physics part.

    It may be the reason that you move the object by setting its transform normally, while the physics engine also takes into account the physics related stuff. Trigger and Collision events are run in the physics cycle.
     
  5. Robdon

    Robdon

    Joined:
    Jan 10, 2014
    Posts:
    141
    Ok thanks.

    If its physics related then, would it be better to 'yield return new WaitForFixedUpdate()' then?

    Since 'normally' fixedupdate is called more often than update, so waiting for a fixedupdate cycle should be quicker?

    And technically, you really know there has been a physics update since then, rather than a return null, which could possibly have returned without a physics update happening (most likely not, but still possible maybe).
     
  6. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    I've initially tried that but it didn't work unless you do it twice. I'd just wait for the next frame.
     
    Robdon likes this.