Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Question Ignore collision with any gameobject that would have a certain script attached and enabled

Discussion in 'Scripting' started by Benji23245, Nov 29, 2023.

  1. Benji23245

    Benji23245

    Joined:
    May 7, 2020
    Posts:
    208
    Hello !

    I have a player gameobject, and I want it to ignore collision with any gameobject it would encounter that would happen to have the "Ignore" script attached and enabled.

    I can't think of a way of doing this, can someone help ?

    Thanks !
     
  2. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,180
    Make the "Ignore" script itself ignore collisions of its collider(s) with the player collider(s). Beyond that, the physics system doesn't let you inject custom decisions like this directly, it'd be too slow to do so for contacts.
     
  3. APSchmidt

    APSchmidt

    Joined:
    Oct 31, 2023
    Posts:
    252
    What about removing the "Ignore" script and adding an "Ignore" tag to said game objects?
     
  4. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,180
    That doesn't stop the collision happening which is what the OP asked for.
     
  5. Benji23245

    Benji23245

    Joined:
    May 7, 2020
    Posts:
    208
    Ok I wanted to make it easy and simple but apparently that was a mistake as the case is much more complicated

    The reality is the following :

    I remade Silver the hedgehog's ability's to use telekinesis to move objects around (with a script called KinesisObj). Now those object have a collision (and can be boxes, enemies, stuff like that). Obviously I want them to have collision most of the time, but when I throw them (one by one, or all at once) I want the thrown objects to ignore collision with any other object that has the KinesisObj script attached and enabled.

    That would allow me to throw my objects without them bumping into each other, while still having them collide with any object that are not grabbed.

    So basically, if I have 4 boxes, 3 of which are grabbed, and another on the scene, free, and I fire 1 at it. The thrown box will go through any of the other grabbed box, and smash into the free one.

    How can I do that ?
     
  6. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,180
    Can you not make "Kinesis" a layer because layers are the primary and fastest way to control what can contact what?

    You have 32 layers at your disposal and while that can be limiting in some games, if you have them free then you're best to use them.
     
  7. Benji23245

    Benji23245

    Joined:
    May 7, 2020
    Posts:
    208
    No I can't because I use layers already to differenciate the enemies, breakable object etc.

    And using layer would make me ignore the collision all the time or never
     
  8. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,180
    Is this 2D physics or 3D physics? What Unity version?
     
  9. Benji23245

    Benji23245

    Joined:
    May 7, 2020
    Posts:
    208
    3D physics, Unity 2021.3.20f1
     
  10. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,180
  11. Benji23245

    Benji23245

    Joined:
    May 7, 2020
    Posts:
    208
    No I can't as I said I use layers already to differentiate enemies, player, walls, objects etc
     
  12. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    2,297
    Your only real option, besides layers, is to specifically tell the Physics system ahead of time that collider A ignores collider B. You can wrap this up in to a MonoBehaviour script if you like, but you are going to need to know collider A (usually something on the character) and collider B (usually something you're about to grab or ride or something) in order to make that change.

    The Physics system needs to know this ahead of time and does not know anything about tags or GetComponent.
     
  13. Benji23245

    Benji23245

    Joined:
    May 7, 2020
    Posts:
    208
    What about if I use overlapSphere every frame while the script is active, if thrown, and take all the colliders in the sphere in that case, test for the KinesisObj script, and set the value to the condition value ?

    Then upon disabling the script, I would put back all colliders. How does that sound ?
     
  14. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,180
    Well as I said, Layers are the primary mechanism to control whether a contact is valid for any pair of colliders.

    There's also a way to hook into contacts and modify them and ask that contacts be ignored with your own logic but it's not trivial but easy once you get the hang of it:

    https://docs.unity3d.com/2022.2/Documentation/ScriptReference/Physics.ContactModifyEvent.html
    https://docs.unity3d.com/2022.2/Documentation/ScriptReference/ModifiableContactPair.html
    https://docs.unity3d.com/2022.2/Doc...ence/ModifiableContactPair.IgnoreContact.html
    https://docs.unity3d.com/2022.2/Documentation/ScriptReference/Collider-hasModifiableContacts.html
     
  15. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,180
    So you'd have the things you're throwing in a list and activate their "hasModifiableContacts". You then get the Physics.ContactModifyEvent and you can check if both the colliders in the contact-pair are the things you're throwing. If so, ignore the contact. When you're ready, deactivate their "hasModifiableContacts" (or destroy them).
     
  16. Benji23245

    Benji23245

    Joined:
    May 7, 2020
    Posts:
    208
    Well I'm not sure about those contacts things, I'm not very familiar with them (reminds me that I sent you a PM, maybe you didn't see it ? Or if you don't like PM, please let me know ^^)

    I ended up making it work with this :


    Code (CSharp):
    1. List<Collider> colsToIgnore = new();
    2.  
    3.   private void OnStay() { // called every FixedUpdate
    4.  
    5.     if (isThrown) {
    6.       Vector3 origin = transform.position;
    7.       float radius = 10f;
    8.  
    9.       // Take all colliders in radius that have the KinesisObj script and enabled
    10.       List<Collider> allColsToIgnore = Physics.OverlapSphere(origin, radius).ToList().FindAll(col => {
    11.         KinesisObj kinesisObj = col.GetComponent<KinesisObj>();
    12.         if (kinesisObj != null) return kinesisObj.enabled;
    13.         return false;
    14.       });
    15.  
    16.       // Add non-duplicatas to main list
    17.       IEnumerable<Collider> cols = allColsToIgnore.Except(colsToIgnore);
    18.       colsToIgnore.AddRange(cols);
    19.  
    20.       // Ignore colliders
    21.       foreach (Collider col in colsToIgnore) {
    22.         if (col != null) Physics.IgnoreCollision(GetComponent<Collider>(), col); // Call in OnExit with false to remove all ignoreCollision when disabling the script
    23.       }
    24.     }
    25. }
     
  17. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    775
    Perhaps you can set your kinesis objects to be triggers and when after being thrown they detect a collision with a solid object you can disable their trigger status.
     
  18. Benji23245

    Benji23245

    Joined:
    May 7, 2020
    Posts:
    208
    Hey thanks for the reply !
    I thought about it but I was worried it could end up causing “go through walls” issues. Wouldn’t it ?
     
  19. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    775
    That's a valid concern because as you've probably noticed the collision detection mode on triggers is always discrete and so very fast moving triggers can sometimes pass straight through other objects without triggering a collision. But I think at throwing speed it should work fine.

    But there's an issue you may have with a very thin trigger still being able to pass through other thin objects despite the collision being detected. You can solve this by making the object step back a little so then the physics engine can have another go at correcting the object's position but this time as a regular rigidbody and not as a trigger.

    So something like this:

    Code (CSharp):
    1.    void OnTriggerEnter(Collider c)
    2.    {
    3.       if (c.gameObject.tag!="Kinesis")
    4.       {
    5.          GetComponent<Collider>().isTrigger=false;
    6.          rb.MovePosition(rb.position-rb.velocity*Time.deltaTime);   // take a step back
    7.       }
    8.    }
    But if you don't have any thin object collisions like a book falling flat onto a table then you shouldn't have to bother with the stepping back.
     
  20. Benji23245

    Benji23245

    Joined:
    May 7, 2020
    Posts:
    208
    Well thrown objects move at 80 m/s it’s way too fast I think
    I'll try it out though, it would be much more efficient !
     
    Last edited: Nov 30, 2023
  21. Benji23245

    Benji23245

    Joined:
    May 7, 2020
    Posts:
    208
    @zulo3d
    You know how games have those invisible walls to prevent you from going out of the stage ? Or even regular walls actually, they are just mesh colliders right ? So they are 2D right ? Wouldn't I most likely face the issue with those ?

    So apparently, the boxes aren't all properly thrown as if some still bumped into other ones. That's weird as I do set them to trigger though

    EDIT: Ok so apparently OnTriggerEnter fires even if the two entities are triggers ? I thought not
    EDIT2: Welp, that's all done, it works well with your solution but you can see for a frame the object going through the other one (even though I thought OnTriggerEnter happened before, apparently not). And since they are moving so fast it quite noticeable.

    BUT, since the use case for this solution is rather to avoid throwing too many overlapSpheres per FixedUpdate, I figured I could use it only when throwing all the entities, when there are more that, say 10 entities, and use my solution when throwing less than that.

    What do you think ? Is 10 overlapSphere per FixedUpdate still too many ?
     
    Last edited: Nov 30, 2023
  22. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    775
    Wait a minute. I just went up and had another read of your requirement. I think we're making this too complicated. :)

    Why not just turn your thrown object into a regular rigidbody the moment it's thrown?. The other grabbed kinesis objects will be triggers and so won't be effected by the thrown object. There'll be no need for the OnTriggerEnter..
     
    Last edited: Nov 30, 2023
  23. Benji23245

    Benji23245

    Joined:
    May 7, 2020
    Posts:
    208
    What do you mean a regular rigidbody ? They are all rigidbodies
     
  24. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    775
    Sorry, I mean a regular rigidbody that isn't a trigger.
     
  25. Benji23245

    Benji23245

    Joined:
    May 7, 2020
    Posts:
    208
    But it’s the collider’s a trigger or not and they are already not triggers, I don’t understand ?
     
  26. Benji23245

    Benji23245

    Joined:
    May 7, 2020
    Posts:
    208
    Grabbed Kinesis items must not be triggers
     
  27. zulo3d

    zulo3d

    Joined:
    Feb 18, 2023
    Posts:
    775
    Then I don't know what to suggest because my earlier idea also involved triggers.

    Get back to the drawing board, Benji! :)
     
  28. Benji23245

    Benji23245

    Joined:
    May 7, 2020
    Posts:
    208
    Like I said, a mix of both our solutions works. I just hope 10 overlapSphere per fixedupdate isn’t too much
     
  29. Benji23245

    Benji23245

    Joined:
    May 7, 2020
    Posts:
    208
    Hey @MelvMay , in another thread I asked something about this (=> https://forum.unity.com/threads/ign...objects-with-that-layer.1529626/#post-9569521).
    Using Physics.ContactModifyEvent, I can have access to the ModifiableContactPairs, but they don't seem to contain anything that I can use to retrieve the info I need (which is "both have KinesisObj script attached and enabled"). They do have colliderInstanceId/otherColliderInstanceId, but I don't know how to use them to actually have access to the actual colliders (which would allow me to call GetComponent and get the info I need). @halley said something above about the fact that we can't use tags or GetComponent, which would make all of this impossible.

    So can I even do it ? If so, how ?

    I really want to start using these contacts modifications system because it could help me fix quite a few bugs (for example, call IgnoreContacts() if the collider my player is touching has a certain script, which contains certain values, or another example: redirect velocity when touching an edge (for cliff behavior), so that the character is pushed to its fall, stuff like that).

    Also, for the case of this thread, it'd be much better performance-wise.

    Thanks for the help !
     
  30. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,180
    So you know, I'm not a 3D physics dev so I do not know the ins/outs of this.

    You can convert any instanceID to a UnityEngine.Object using Resources.InstanceIDToObject I believe although I've never used it.

    https://docs.unity3d.com/ScriptReference/Resources.html
     
    Last edited: Jan 10, 2024
  31. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,180
  32. Benji23245

    Benji23245

    Joined:
    May 7, 2020
    Posts:
    208
    Thanks for answering !

    So I would end up with an Object ? https://docs.unity3d.com/ScriptReference/Object.html

    But I don't see methods here to have access to the components attached to the gameObject though ?

    Can I convert "Object" to "GameObject" maybe or something ?
     
  33. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,180
    Yes, of course.

    Cast the Object to the type you want.
     
  34. Benji23245

    Benji23245

    Joined:
    May 7, 2020
    Posts:
    208
    Okay, and how can I make the kinesisObj stop being able to move my player when colliding with him ?
    I'd like to make it as if the player were kinematic (except he's not).

    Should I use setTargetVelocity with Vector3.zero ? Not sure how to only do it on the Player though ?
     
    Last edited: Jan 10, 2024
  35. Benji23245

    Benji23245

    Joined:
    May 7, 2020
    Posts:
    208
    I did the following :


    Code (CSharp):
    1. private void OnContactModify(PhysicsScene scene, NativeArray<ModifiableContactPair> pairs) {
    2.     foreach (var pair in pairs) {
    3.  
    4.       for (int i = 0; i < pair.contactCount; i++) {
    5.         KinesisObj obj = Resources.InstanceIDToObject(pair.bodyInstanceID) as KinesisObj;
    6.         KinesisObj otherObj = Resources.InstanceIDToObject(pair.otherBodyInstanceID) as KinesisObj;
    7.  
    8.         bool cond1 = obj != null && otherObj != null;
    9.  
    10.         if (cond1) {
    11.           bool cond2 = obj.enabled && otherObj.enabled;
    12.           bool cond3 = obj.isThrown || otherObj.isThrown;
    13.  
    14.           if (cond1 && cond2 && cond3) {
    15.             pair.IgnoreContact(i);
    16.           }
    17.         }
    18.       }
    19.     }
    20.   }
    21.  
    22.   private void EnableContactModif() => Physics.ContactModifyEvent += OnContactModify;
    23.   private void DisableContactModif() => Physics.ContactModifyEvent -= OnContactModify;
    And I get the error : "UnityException: InstanceIDToObject can only be called from the main thread.
    Constructors and field initializers will be executed from the loading thread when loading a scene.
    Don't use this function in the constructor or field initializers, instead move initialization code to the Awake or Start function."
     
  36. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,180
    As per the docs: https://docs.unity3d.com/2023.2/Documentation/ScriptReference/Physics.ContactModifyEvent.html
    Again, I am NOT a 3D physics dev. I've never used this API therefore I am not an authority on best practices with it.