Search Unity

  1. Unity 2019.2 is now released.
    Dismiss Notice

Detect collision with an object from outside the class

Discussion in 'Scripting' started by MordazaH, Aug 8, 2016.

  1. MordazaH

    MordazaH

    Joined:
    Oct 15, 2015
    Posts:
    45
    Hi all,
    Well I tried using trigger functions for the moment the collider enter or exits but I cant get it to do what I need, I mean I tried setting a boolean or increasing/decreasing a counter or so but it seems it cant do what I need, I want to check from other class if the object is colliding with other type of object (based on the tag) at any moment, so I tried looking in the docs and I cant find other function that could handle this the way I need, any ideas or suggestions are greatly welcome =).
     
  2. jeffreyschoch

    jeffreyschoch

    Joined:
    Jan 21, 2015
    Posts:
    2,542
    One way would be to give that object a function that checks if its collider is overlapping a certain layer. You can edit to use whatever physics check you want to use (overlap box, sphere, check tag, w/e you need):

    Code (CSharp):
    1. public bool IsTouching(LayerMask layers) {
    2.     Collider col = GetComponent<Collider>();
    3.     return Physics.CheckBox(col.bounds.center, col.bounds.extents, transform.rotation, layers);
    4. }
    If the colliders are 2D you can use OverlapPoint or isTouching.
     
  3. MordazaH

    MordazaH

    Joined:
    Oct 15, 2015
    Posts:
    45
    Thanks @jeffreyschoch for the idea, it looks like it could work quite well in the future but not in my current case or maybe I understood it wrong, to tell the truth I didnt learn much about the layers until now and after some reading and testing it seems to me that the overlapping is being check against a box bigger than it should or maybe not rotated the way it is, I know you added the collider extents and the transform rotation too so it should work quite well theoritically but maybe my current scenario is making it not work, I dont know maybe scaling the prefab or something makes the checkbox act strange, I read the docs and it says nothing about that so then it might be something in my setup I guess because the current overlapping seems to be validated when the objects are not really overlapping. I added a conversion from the local coordinate system to the global one as the object I want to add this functionality is nested into others but still its not working, as said maybe my setup is responsible of this not working, thanks for the idea I will keep in mind it for the future =), isnt there another possible way to check the collision from other class?
     
  4. LTK

    LTK

    Joined:
    Jul 16, 2015
    Posts:
    22
    You can use UniRx(Reactive Extention for Unity, it's free on assets store) to observable collision, trigger,...
     
    MordazaH likes this.
  5. jimroberts

    jimroberts

    Joined:
    Sep 4, 2014
    Posts:
    560
    This screams bad design... Can you please elaborate on why you would need to do this?
     
    jeffreyschoch and MordazaH like this.
  6. MordazaH

    MordazaH

    Joined:
    Oct 15, 2015
    Posts:
    45
    Thanks @LTX I will have a look at this tomorrow (today its late for me).

    Thanks for replying @jimroberts , well I will explain more or less what I have the best way possible to see the trouble or maybe the way to design this better. I have a ship which is basically an empty game object that contains the model for the ship and other visual elements, there is also a few boxes that are not rendered and I need them there for a special type of weapon (they are the triggers). Then the main code is in the main game object which is like a container, and from there I call what is needed when the player fire or so on, as said one special weapon needs to check the invisible boxes to see if they are colliding with enemy objects so I have a small piece of code attached to the each invisible box so that from main code I can check them all and see if collision is detected so that I can fire the weapon one way or another, more or less the setup is like that, then as you see I want to check the collision the inviisble boxes find from the main game object class itself, in fact with just onTrigger functions it was working great but when I added the bit of code that destroys enemies it started not working because the destroyed enemy apparently is not recognizable when onTriggerExit is called, otherway I could have continued using these functions it was all perfect until that point. If you can suggest a variation in this method or other function to achieve this please let me know and thanks beforehand =).
     
  7. Timelog

    Timelog

    Joined:
    Nov 22, 2014
    Posts:
    528
    Why in the world would you do all those things from a main game object/script... That is terrible design... No not even terrible... it's.... it's..... impossible really.

    Doing it that way will cause you sooooooo many problems down the line that all I can suggest is going back to the drawing board and rethink how different components and objects are to interact with each other. Also I advise you to follow a couple of tutorials from the learning section especially the Space Shooter and Survival Shooter to get some basics of how to make a complete working game.
     
    MordazaH likes this.
  8. MordazaH

    MordazaH

    Joined:
    Oct 15, 2015
    Posts:
    45
    Thanks @Timelog but its not main game object just I was refering to the gameobject that contains all the elements for the particular ship so in essence its the prefab of the ship, and I followed these tutorials you mentioned, thanks for the advice, I did the same way they do there having a game object thats a container and then adding elements within it. So in this situation, is there an effective way to check if an object is colliding with other by calling a function? the onTrigger functions are called automatically and so I cant assure the colision is happening now or I cant think a way of doing so, as said I tried setting boolean and counter for this end and its not working due the way the function works, I wish there was a function I could call just to see if the collider is hiting another object with certain tag now, thats all I need.

    Edit: In essence what I need is a function that could look like this:
    Code (csharp):
    1.  
    2. public bool isHitting(Collider other)
    3. {
    4.         //mistery action to check collision againt the other collider
    5.         if (other.CompareTag("enemy"))
    6.         {      
    7.             return true;
    8.         }
    9.         return false;
    10. }
    11.  
     
    Last edited: Aug 9, 2016
  9. Timelog

    Timelog

    Joined:
    Nov 22, 2014
    Posts:
    528
    OnTrigger functions are not really called automatically, they have a clear moment when they get called, namely:
    OnTriggerEnter: gets called when an object that the trigger can interact with enters the trigger
    OnTriggerStay: gets called as long as an object that the trigger can interact with stays in the trigger
    OnTriggerExit: gets called when an object the trigger can interact with exits the trigger
    (Same goes for the OnCollision counterparts)

    So you want to know if the object hits the trigger right at that moment? Use OnTriggerEnter. You want to make sure it's a specific object? use CompareTag as you use above.

    There is not other need for weird functions to do what everyone understands you want... The above example you give is literary nothing more then a useless wrapper over CompareTag that already returns a Boolean whether you've hit an object with that specific tag.
     
    MordazaH likes this.
  10. jeffreyschoch

    jeffreyschoch

    Joined:
    Jan 21, 2015
    Posts:
    2,542
    If you're passing the collider to check, then you can check the tag of that object before passing it in the first place.

    So really you just need to check the collision.

    I've actually lost the concept of what you're trying to do, but surely one of these contains what you're looking for:
    Code (CSharp):
    1. public bool IsHitting(Collider other) {
    2.     Collider col = GetComponent<Collider>(); // this object's collider
    3.     Collider[] hits = Physics.OverlapBox(col.bounds.center, col.bounds.extents, transform.rotation);
    4.     foreach(Collider hit in hits) {
    5.         if(hit == other) {
    6.             return true;
    7.         }
    8.     }
    9.     return false;
    10. }
    11.  
    12. public bool IsHitting(string tag) {
    13.     Collider col = GetComponent<Collider>(); // this object's collider
    14.     Collider[] hits = Physics.OverlapBox(col.bounds.center, col.bounds.extents, transform.rotation);
    15.     foreach(Collider hit in hits) {
    16.         if(hit.CompareTag(tag)) {
    17.             return true;
    18.         }
    19.     }
    20.     return false;
    21. }
    22.  
    23. public bool IsHitting(Collider other, string tag) {
    24.     Collider col = GetComponent<Collider>(); // this object's collider
    25.     Collider[] hits = Physics.OverlapBox(col.bounds.center, col.bounds.extents, transform.rotation);
    26.     foreach(Collider hit in hits) {
    27.         if(hit == other && hit.CompareTag(tag)) {
    28.             return true;
    29.         }
    30.     }
    31.     return false;
    32. }
    You could have the object itself keep track of what it is colliding with by using OnEnter/Exit methods, and adding and removing from a list of currently colliding objects.

    Then other objects can call a function to ask if a certain object is in that list.
     
    MordazaH likes this.
  11. MordazaH

    MordazaH

    Joined:
    Oct 15, 2015
    Posts:
    45
    Thanks @Timelog I think I understand when the onTrigger functions are called in relation to the collision, but is there an specific order for them to be called? like they are called before or after the Update/lateUpdate? just in case, because if these are not called in specific order in relation to the Update or LateUpdate then I cant think of a way to set a variable that determines wether its hitting other object or not, I mean to make it possible from other classes to see if the collision is currently happening.

    Thanks @jeffreyschoch Im sorry I explained it wrongly in last post, well your solution seems like it could work, however I still find the same issue I found with CheckBox, I mean, since the object having this function inside of its class is nested in other object then the coordinates of its position are not global and I think col.bounds.center could be returning the local ones, well I tried converting it as I have done before with transform.TransformPoint and still the collision seems off the place, my assumption is all this only happens because the object is nested within other, right? then how could I correct the position other way? I cant think of other way to achieve that and in docs or other threads apparently thats the way. And if coordinates cant be corrected like that or something is in conflict with current nesting, I think your idea of having a list should work nicely, well the list may get really big as screen will be filled with elements having the same tag, but still sounds like a working solution =),

    Edit: I forgot to mention my previous post was slightly wrong as I dont have the collider beforehand just know the tag but the could be many collidable objects having that, of course I addapted the suggested solution to check the tag, just wanted to clarify what Im needing here, sorry for wrongly saying it in previous
     
  12. Timelog

    Timelog

    Joined:
    Nov 22, 2014
    Posts:
    528
    For information about the execution order you should read this article: https://docs.unity3d.com/Manual/ExecutionOrder.html

    If you want other classes to know whether a collision between two objects is currently happening, let the OnTrigger or OnCollision functions update some variable in another class like this:
    Code (CSharp):
    1. // Warning: Pseudocode
    2.  
    3. public class Collidable : MonoBehaviour
    4. {    // If the tracker is a MonoBehaviour somewhere in the scene use the following attribute
    5.      // and drag the object in via the inspector, otherwise see Awake()
    6.     [SerializeField]
    7.     private CollisionTracker _tracker = null;
    8.    
    9.     private void Awake()
    10.     {
    11.         // if the tracker is a standard c# class
    12.         _tracker = new CollisionTracker();
    13.  
    14.         // If the tracker is on the same GameObject
    15.         _tracker = GetComponent<CollisionTracker>();
    16.     }
    17.  
    18.     private void OnTriggerEnter(collider other)
    19.     {
    20.         _tracker.HandleCollisions(this.collider, other);
    21.     }
    22. }
    23.  
    24. public class CollisionTracker
    25. {
    26.     private HandleCollisions(collider hitCollider, collider other)
    27.     {
    28.         // So with the colliders what you wish
    29.     }
    30. }
     
    MordazaH likes this.
  13. jeffreyschoch

    jeffreyschoch

    Joined:
    Jan 21, 2015
    Posts:
    2,542
    You can build the appropriate sized bounding box by using Bounds.Encapsulate(bounds);

    So create a new Bounds, and use Bounds.Encapsulate on all collider bounds that you want to be part of the area, then use that bounds for the check.

    Or you can calculate it yourself by taking the average of the center points, and finding the min/max corners of your objects.
     
    MordazaH likes this.
  14. MordazaH

    MordazaH

    Joined:
    Oct 15, 2015
    Posts:
    45
    Thanks @Timelog I think counting on the execution order as I thought would make it more complicated, thanks for the link to the docs, I have a brief idea about this but not about all the orders there. Your code I think is a better solution, having a list of elements like @jeffreyschoch suggested and tracking there the elements colliding or not should bring me an accurate count of the current elements hitting the collider.

    Thanks @jeffreyschoch I didnt know I could build the bounding box like that, it will be handy feature in the future for me I think. Well for the current situation I think I will handle like you and @Timelog suggested, having the onTrigger functions keep a list of colliding elements and adding/removing them from there, as I said before I tried using counters or booleans but this isnt accurate enough because I cant track objects being destroyed and with many objects on screen it results in an unaccurate method but listing the gameobjects in an array will do well I think, as I can add/remove the right elements and have the accurate counter that I need =).
     
    Last edited: Aug 10, 2016