Search Unity

[SOLVED] OnTriggerEnter() is called from each script. How can we "filter this to a single callback"?

Discussion in 'Physics' started by FrogSkull, Apr 28, 2017.

  1. FrogSkull

    FrogSkull

    Joined:
    Jun 15, 2016
    Posts:
    14
    Hey guys.
    What would be a good practice when coding OnTriggerEnter() between two instances from the same Prefab?
    They share same tag and same layer.

    OnTriggerEnter() will be called from both sides as they hit. This is a good thing if our purpose is to apply
    damage against both objects, ok.

    But how to add score or spawn a gameobject between them only once since OnTriggerEnter() is called twice in this case? How to filter this doubled callback to a single function call or scope?

    I am not sure if I am being clear due to language limitations, thanks for any effort anyway.
     
    Last edited: Apr 29, 2017
  2. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    What's the setup that has 2 objects that get hit at the same time? Can you remove one of them?
     
  3. FrogSkull

    FrogSkull

    Joined:
    Jun 15, 2016
    Posts:
    14
    Whenever two objects hit or overlap their colliders, OnTrigger/OnCollisionEnter is called from each component or script inside those objects when they have those collision callbacks coded.
    Now consider the following component/script.
    Code (CSharp):
    1.  
    2. public class MouseDragObject: MonoBehaviour {
    3.      OnTriggerEnter(Collider other)
    4.      {
    5.         if(other.gameObject.tag == gameObject.tag)
    6.         {
    7.               Score.add(15);
    8.               ObjectManager.spawn(boom);
    9.               //this code runs 2 times, one from this script and one from other.getComponent<MouseDrag>()
    10.         }
    11.        if(other.gameObject.tag == "some_other_tag")
    12.        {
    13.                  //this code runs  only 1 time if true
    14.        }
    15.    }
    16. }
    Now, if We have two instances from the same prefab with this component/script inside each instance and if we drag an object against the other, Score.add(x) will be called from both gameobjects, result will be a total of 30, or x*2.
    Now, considering they share same layer, and have same tag, how can we prevent Score.add() and spawn() to run twice since both objects will run the code inside tag check, each instance from their component.
     
    Last edited: May 2, 2017
    arkilis likes this.
  4. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Okay, let's rewind a little here. I do understand how a script works. I also understand mostly what you're asking about.
    I was wondering why they'd be hitting each other.. which, as I was typing maybe I figured out. You can drag either of these, but if you drag them into each other they boom?
    You might try to set a 'Selected' object (which, btw you can also do that + dragging with the EventSystem in Unity).
    With your selected (ie: currently being dragged), have only the dragged (or hit, whichever you choose) execute the code.
    Code (csharp):
    1.  
    2. if(gameObject != CurrentlyDragged) { /* code here */ };
    3. // or
    4. if(gameObject == CurrentlyDragged) { /* code */ };
    5.  
    Just an idea. :)
     
    FrogSkull likes this.
  5. FrogSkull

    FrogSkull

    Joined:
    Jun 15, 2016
    Posts:
    14
    Methos5k, your idea is clean, good and solves part of my situation. Thanks a lot.

    How could we handle this if the objects were wandering around and then suddenly collide?
    They can be controlled by mouseDrag but they also move around and eventually collide.

    More precisely they are blobs and make bigger blobs when hitting each other while sharing the same tag.

    I'm thinking of scheduling a list of collisions to be iterated in the next frame and comparing instanceIDs but that does not seem very accurate to me (or fast). What do you think? Maybe another approach?
     
  6. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Well, let's see.. If you had a variable on each object's script "ProcessCollision", maybe when you have a collision, check the tag.. it's the same.. (2 blobs running into each other)
    do this:
    Code (csharp):
    1.  
    2. if(ProcessCollision) {
    3.    other.GetComponent<Script>().ProcessCollision = false;
    4.    // do your score, etc..
    5.    }
    6. ProcessCollision = true;
    7.  
    I believe that'll let whichever object first gets hit to do the code you want, and disable the functionality on the next one.
    When they get the callback, their variable will be false, making them miss the processing, before resetting their variable to true (if they're not destroyed) so they can work next time (in case they get the callback first next time).
    Since I didn't know they could run into each other in your first post, I think this would be better than my previous code; I wouldn't use my first code if I had this situation.
     
    FrogSkull likes this.
  7. FrogSkull

    FrogSkull

    Joined:
    Jun 15, 2016
    Posts:
    14
    Thanks again, it is working.
    In fact, both of your posts are going to help me in different cases. I appreciate that.
    :)
     
  8. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Cool, sounds great. You're welcome. Best wishes with your game :)
     
    FrogSkull likes this.
  9. CausticLasagne

    CausticLasagne

    Joined:
    Oct 2, 2015
    Posts:
    26
    Thank you @methos5k your code was helpful.
     
  10. brunosallesdev

    brunosallesdev

    Joined:
    Dec 19, 2021
    Posts:
    5
    I have a player prefab, and a enemy (clone). Both have same RB/collider properties.

    when they collide, the player’s ontriggerenter is called first.

    When the player colides with this enemy, but the enemy as prefab, enemie’s ontriggerenter is called first.

    Player prefab + enemy clone = player prefab.

    player prefab + enemy prefab = enemy prefab.

    what that? Thanks for help