Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice
  3. Dismiss Notice

Experimental contacts modification API

Discussion in 'Physics Previews' started by yant, Jul 3, 2020.

  1. XANTOMEN

    XANTOMEN

    Joined:
    Dec 18, 2012
    Posts:
    41
    [EDIT: Solved my problems, so this can be ignored now. I'm still curious about how the order of object initialization works and why it changes when changing Layers, but it would not benefit my current usecase anyway. Posting some code on my own for reference to other people]

    I stumbled upon this thread a few days ago by accident and I'm so impressed with this feature, I love it! Props to @yant and also the other people in this thread making cool stuff with it. Thank you very much for your efforts!

    Unfortunately, I'm entering some brain pickles in my way to try and do what I want with it, and I wanted to gather your (@yant or anyone that knows) thoughts on it.

    [EDIT: Ended up not needing to alter the point, as I had misunderstood what I needed, so these point became irrelevant for my usecase]
    1. I have read this bug report and the response to it, both in the bug report and in the thread. Namely:

    Could someone clarify if there's a way to force such "object initialisation" to be in a predictable order? I don't mind about if the object I want to check for comes as "collider" or "otherCollider", I can deal with that easy. However, the fact that the world pos of the point (getting it via GetPoint(i) is different in both cases and I can't predict if I'm going to get the value I want or a different one is making things quite messy, and I don't like not knowing where the order is coming from. For example, if I have two objects in place, and I change one of them to a layer (by clicking in the Editor) where it does not collide with the other one, then bring it back to the original layer, the order of those colliders (and therefore where the point in GetPoint(i) ends up) is now different. I can repeat this double switch many times and it will keep changing which one is the "otherCollider" and which one is the first.

    Gifs of the result before and after doing the double layer switch:

    Desired case


    "Wrong" case


    The debug ray I'm doing with this code:

    Code (CSharp):
    1. var arrow = pair.GetNormal(i) * pair.GetSeparation(i) * Mathf.PingPong(time * speedMultiplier, 1f);
    2.  
    3. Debug.DrawRay(pair.GetPoint(i), arrow, color);
    Luckily, my one neuron made me realize that via hashing them both in different hashmaps I can easily tell when I'm getting the small sphere as a first collider or "otherCollider" and when in the undesired order, change its point, normal and separation accordingly to where my desired contact point would have been by changing the "wrong" point value to "wrong" point + "wrong" normal * "wrong" separation, but I'm not sure if other vars are harder to invert and I'm missing something, and in any case, if I could, I would rather do whatever necessary to make the order predictable from the get go. (or at the very least, understand under which circumstances the order might change, so I can potentially think of something)

    Notes: Both spheres I used for the gifs have kinematic RigidBody with a child, and that child has a Convex Mesh Collider of a Sphere. (I have my reasons to be testing with this instead of Sphere Colliders). Only the big Sphere has "hasModifiableContacts" set to true.

    [EDIT: Reference code of the solution I implemented at the end of the post]
    2. In the following image,



    I need to tell if each contact point between the two Spheres (they are Spheres in this example but I need a solution that works for any convex shape) is also inside the Monkey hull or outside, the three of them having a convex collider. My best idea so far was to loop through the contact points of both Spheres and pass them through the ClosestPoint() function of the Monkey collider (hashed previously), which it being convex, would return the same point I fed the function, giving me my answer.

    However, this function can only be used in the main thread, so that's a no go.

    My next best idea is to hash all the transformed (world pos) vertices of the Monkey collider beforehand (I do have a VHACD convex version of the mesh that lines up with the collider perfectly, so I'm taking the vertices from there, altho I would not mind if someone explained how to get the vertices of Unity's native mesh collider convex hull), and during the modification event, wrap my head around this clever technique and implement it to tell if each of my contact points are inside the Monkey convex hull or not.

    Am I being crazy here, and there's an easier way to do this check inside the modification event, so I can ignore the contact points I need to and let the others pass?

    [EDIT] I did try the approach from a comment in the Stack Overflow post I linked (the one of adding all vectors together, then using that summed vector as a reference and compare by angle). It looked really cool when debugging it with gizmo lines, but it did not really work, as having more vertices in one side of the mesh would bias the summed vector in that direction, and some vectors could end up "behind" the imaginary plane, giving wrong positives for "is inside volume".

    Instead, I ended up using the following code. Look up the initial code comment and the links in it to find the inspiration for the approach if unclear:

    Code (CSharp):
    1.  
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. //Tried this approach before but it was slightly off when vectors were repeating more on one side of the volume
    6. //https://stackoverflow.com/questions/4901959/find-if-a-point-is-inside-a-convex-hull-for-a-set-of-points-without-computing-th/4903615#4903615
    7. //Ended up implementing more or less this one: https://stackoverflow.com/a/16906278 , leveraging Plane.GetSide() function, but could as well have used
    8. //an angle > 90 comparison (if vertex to targetPoint vector angle with normal is > 90 then targetPoint is on the left of the face plane)
    9.  
    10.  
    11. public class PointInsideConvexCollidersTest : MonoBehaviour
    12. {
    13.     public Vector3 testPoint;
    14.  
    15.     public MeshCollider[] meshColliders;
    16.  
    17.     public float sizeTargetSphereGizmo = 0.2f;
    18.  
    19.     public Dictionary<MeshCollider, Plane[]> transformedFacePlanesByMeshCollider = new Dictionary<MeshCollider, Plane[]>();
    20.  
    21.     bool isInside;
    22.  
    23.     public void OnDrawGizmos()
    24.     {
    25.         if (isInside)
    26.         {
    27.             Gizmos.color = Color.cyan;
    28.         }
    29.         else
    30.         {
    31.             Gizmos.color = Color.red;
    32.         }
    33.  
    34.         Gizmos.DrawWireSphere(testPoint, sizeTargetSphereGizmo);
    35.     }
    36.  
    37.     private void OnEnable()
    38.     {
    39.         meshColliders = GetComponentsInChildren<MeshCollider>();
    40.         UpdateTransformedFacePlanes();
    41.     }
    42.  
    43.     private void FixedUpdate()
    44.     {
    45.         if (transform.hasChanged)
    46.         {
    47.             UpdateTransformedFacePlanes();
    48.             transform.hasChanged = false;
    49.         }
    50.  
    51.         isInside = IsPointInsideConvexMeshColliders(meshColliders, testPoint);
    52.     }
    53.  
    54.     void UpdateTransformedFacePlanes()
    55.     {
    56.         transformedFacePlanesByMeshCollider.Clear();
    57.  
    58.         foreach (var meshCollider in meshColliders)
    59.         {
    60.             List<Plane> facePlanes = new List<Plane>();
    61.  
    62.             if (meshCollider.sharedMesh.normals.Length == 0)
    63.                 meshCollider.sharedMesh.RecalculateNormals();
    64.  
    65.             var triangleIndices = meshCollider.sharedMesh.triangles;
    66.             var vertices = meshCollider.sharedMesh.vertices;
    67.             var normals = meshCollider.sharedMesh.normals;
    68.  
    69.             //Step through for loop 3 by 3
    70.             for (var i = 0; i < triangleIndices.Length; i+=3)
    71.             {
    72.                 var p1 = vertices[triangleIndices[i]];
    73.                 var p2 = vertices[triangleIndices[i + 1]];
    74.                 var p3 = vertices[triangleIndices[i + 2]];
    75.  
    76.                 var pCenter = (p1 + p2 + p3) / 3f;
    77.  
    78.                 var n1 = normals[triangleIndices[i]];
    79.                 var n2 = normals[triangleIndices[i + 1]];
    80.                 var n3 = normals[triangleIndices[i + 2]];
    81.  
    82.                 var normal = (n1 + n2 + n3) / 3f;
    83.  
    84.                 //Debug.DrawRay(trP1, trN1.normalized, Color.red);
    85.                 //Debug.DrawRay(trP2, trN2.normalized, Color.yellow);
    86.                 //Debug.DrawRay(trP3, trN3.normalized, Color.cyan);
    87.  
    88.                 var transformedVertex = meshCollider.transform.TransformPoint(pCenter);
    89.                 var transformedNormal = meshCollider.transform.TransformVector(normal);
    90.  
    91.                 //Debug.DrawRay(transformedVertex, transformedNormal.normalized, Color.yellow);
    92.  
    93.                 Plane facePlane = new Plane(transformedNormal, transformedVertex);
    94.  
    95.                 facePlanes.Add(facePlane);
    96.             }
    97.  
    98.             transformedFacePlanesByMeshCollider[meshCollider] = facePlanes.ToArray();
    99.         }
    100.     }
    101.  
    102.     public bool IsPointInsideConvexMeshColliders(MeshCollider[] meshColliders, Vector3 targetPoint)
    103.     {
    104.         foreach (var meshCollider in meshColliders)
    105.         {
    106.             if (IsPointInsideConvexMeshCollider(meshCollider, targetPoint))
    107.             {
    108.                 return true;
    109.             }
    110.         }
    111.  
    112.         return false;
    113.     }
    114.  
    115.     public bool IsPointInsideConvexMeshCollider(MeshCollider meshCollider, Vector3 targetPoint)
    116.     {
    117.         var facePlanes = transformedFacePlanesByMeshCollider[meshCollider];
    118.  
    119.         foreach (var facePlane in facePlanes)
    120.         {
    121.             //Keep in mind, in this case, GetSide() will evaluate true for the range (0,infinite]. So, 0 is treated as Inside.
    122.             //Inverted the normal of the planes if it was meant to treat 0 as Outside.
    123.             if (facePlane.GetSide(targetPoint))
    124.                 return false;
    125.         }
    126.  
    127.         return true;
    128.     }
    129. }
    130.  
    Thanks a lot and keep doing great work!

    [EDIT: This is a sneak peek into what I'm doing with it. I will be releasing a cutting asset of any shape versus any shape through Fragment Shaders that includes accurate collisions and shadows in the near future. The balls could have been any shape as well and it would work, as the code is agnostic to any shape of Cutters, CutTargets or physics objects]

     
    Last edited: Oct 6, 2022
    AntonioModer, Ruchir, JuozasK and 2 others like this.
  2. iDerp69

    iDerp69

    Joined:
    Oct 27, 2018
    Posts:
    59
    I don't understand this at all. I use the contact mods API in the context of a fully predicted/rollback Photon Fusion sim. I was getting a misprediction around ignoring all contacts in a pair, where sometimes the contact pair wouldn't be ignored. The apparent "solution" was to adjust my function:
    Code (CSharp):
    1. void IgnoreContactPair(ModifiableContactPair pair)
    2. {
    3.     for (int i = 0; i < pair.contactCount + 2; ++i)
    4.         pair.IgnoreContact(i);
    5. }
    Yes... +2.
    Makes no sense to me, and yet completely solves the issue I was having. What could the reason be that there would be unaccounted contacts in the pair?
     
    Last edited: Nov 3, 2022
  3. Prodigga

    Prodigga

    Joined:
    Apr 13, 2011
    Posts:
    1,127
    Hmm, in our game we dampen the collision forces, but sometimes - rarely but sometimes - our collider hits a wall and bounces away with great force. I wonder if this is the culprit..?
     
  4. codebiscuits

    codebiscuits

    Joined:
    Jun 16, 2020
    Posts:
    113
    Is there somewhere I can see this bug? Sorry if it's obvious, I can't seem to find it by Googling.

    SetMaxImpulse seems to be working for me for Rigidbody, however, the bug still seems to apply for me to ArticulationBody.

    Or, @yant Should SetMaxImpulse even work for ArticulationBody? Maybe that's not the intent, or maybe I'm using it wrong.

    Edit: Using Unity 2021.3.5f1 LTS. I can provide a tiny project that demonstrates this if that's helpful.

    Code (CSharp):
    1. using UnityEngine;
    2. using Unity.Collections;
    3.  
    4. public class ContactModificationTest : MonoBehaviour {
    5.  
    6.     //Set to 0, both Rigidbody and ArticulationBody will immediately fall through a test plane;
    7.     //Set to 1e-07, Rigidbody will fall through test plane, AritculationBody travels along plane as usual
    8.     //Set to 0.5, Rigidbody will "skate through" bumps<3; ArticulationBody bangs over them as usual
    9.     public float maxImpulse = .5f;
    10.  
    11.     void OnEnable() {
    12.         var colliders = GetComponentsInChildren<Collider>();
    13.         foreach ( var c in colliders )
    14.             c.hasModifiableContacts = true;
    15.         Physics.ContactModifyEvent += ModificationEvent;
    16.     }
    17.  
    18.     private void ModificationEvent( PhysicsScene scene, NativeArray<ModifiableContactPair> pairs ) {
    19.         foreach ( var pair in pairs ) {
    20.             for ( int i = 0; i < pair.contactCount; ++i )
    21.                 pair.SetMaxImpulse( i, maxImpulse );
    22.         }
    23.     }
    24.  
    25. }
    26.  
    What I'm actually trying to achieve is a squashy tyre effect like this: https://twitter.com/ArturBerk/status/1586397732088209410
    (I think that video is using Havok, but anyway, I'm hoping I can manage with Unity/PhysX:)
     
    Last edited: Nov 15, 2022
  5. JuozasK

    JuozasK

    Unity Technologies

    Joined:
    Mar 23, 2017
    Posts:
    84
    That was a case number in a system where we tracked incoming bugs. You would normally be able to find a case's public page here https://issuetracker.unity3d.com/ by searching for that number.

    In this specific case (no pun intended), it was never converted to a bug, so the issue tracker page was not generated, so that's why it won't show up there.

    Interesting to see that it differs, I would be under the impression that it should work for articulations too. Will investigate. Thanks for the repro script :)

    If you could report a bug and post the case number that you get from our lovely support staff, we could take a look at the project and see if it's actually a bug.
     
    Last edited: Nov 16, 2022
    codebiscuits likes this.
  6. codebiscuits

    codebiscuits

    Joined:
    Jun 16, 2020
    Posts:
    113
    Hi,
    I got a mail saying CASE IN-23249, but it told me not to post a link (although, I can actually post a link if that's helpful, I only attached a proof-of-concept project).

    I attached the project here too if that's helpful (minus the library etc folder, so only 59k).

    Behavior is the same in 2021.3.13f1 (latest LTS), and also in 2022.1.23f1.

    Apologies in advance if the bug is, in the end, me.

    Edit: it wasn't, in the end, me:)
    https://issuetracker.unity3d.com/is...t-evaluated-when-using-it-on-articulationbody
     

    Attached Files:

    Last edited: Mar 28, 2023
    yant and JuozasK like this.
  7. JuozasK

    JuozasK

    Unity Technologies

    Joined:
    Mar 23, 2017
    Posts:
    84
    The case number is fine, we'll be able to see it in our system :) And thank you so much for taking the time to create a project and report this :)
     
  8. JuozasK

    JuozasK

    Unity Technologies

    Joined:
    Mar 23, 2017
    Posts:
    84
    @codebiscuits
    I was able to confirm that behaviour. Unfortunately, all we do from our side is pass the values into the SDK which then gets propagated inside PhysX itself. What that means is that it becomes an exponentially more difficult problem to solve. I did an initial pass of digging/experimentation, but the issue lied somewhere deeper in the solver.
     
  9. codebiscuits

    codebiscuits

    Joined:
    Jun 16, 2020
    Posts:
    113
    Hi @JuozasK,
    Thanks for the reply!
    So I think this *is* a bug of some kind, perhaps with PhysX? Like, you'd expect the ArticulationBody stuff in my example to behave the same as the Rigidbody?
     
    Last edited: Dec 1, 2022
  10. JuozasK

    JuozasK

    Unity Technologies

    Joined:
    Mar 23, 2017
    Posts:
    84
    I would expect the two to react similarly for sure. At the end of the day it's a contact being resolved between two bodies, whether a Rigid vs Static or an Articulation vs static.
     
  11. keni4

    keni4

    Joined:
    Nov 30, 2014
    Posts:
    31
    Hi!
    Is it intended behaviour when collider.hasModifiableContacts set to false, collider stops recieveing any collision events (e.g. OnCollisionEnter, ...Stay, ...Exit)? Even if collider.hasModifiableContacts has been never set to true. For my pov this is not correct. (I'm using on/off modifiable contacts on kinematic collider)
     
    Last edited: Jan 22, 2023
  12. dreamade

    dreamade

    Joined:
    Feb 26, 2014
    Posts:
    5
    I'm trying to apply inverMassScale on collision for two rigidbodies which both has modifiable contacts set to true. What makes one object other? As far as I can see, the object with lower bodyInstanceID is always the other. Am I right?

    Also so much thanks for this feature!
     
  13. Luwyliscious

    Luwyliscious

    Joined:
    Mar 21, 2021
    Posts:
    3
    @dreamade I believe there is no guarantee in the order of the colliders in the ModifiableContactPair. My approach is to store my collider's instanceId to define which of invertMasseScale or otherInvertMassScale I should modify. Wether or not there is a logic to the order, this approach makes sure that the behavior is reliable.

    @yant I believe that setting mass properties in the CCD version of the modification callback does not work.

    Using the simplest implementation :
    Code (CSharp):
    1. public class ContactModification : MonoBehaviour
    2. {
    3.     private void OnEnable()
    4.     {
    5.         var collider = GetComponent<Collider>();
    6.         collider.hasModifiableContacts = true;
    7.  
    8.         Physics.ContactModifyEvent += ModificationCallback;
    9.         Physics.ContactModifyEventCCD += ModificationCallback;
    10.     }
    11.  
    12.     private void ModificationCallback(PhysicsScene scene, NativeArray<ModifiableContactPair> modifiableContactPairs)
    13.     {
    14.         for (int i = 0; i < modifiableContactPairs.Length; i++)
    15.         {
    16.             var pair = modifiableContactPairs[i];
    17.  
    18.             var massProperties = pair.massProperties;
    19.             massProperties.inverseMassScale = 0;
    20.             pair.massProperties = massProperties;
    21.         }
    22.     }
    23. }
    I can get a collision to "dominate" the other one by setting it's inverseMassScale to 0 (equivalent to infinite mass).

    When colliding at low speed (discreet collision detection), it works as expected :


    But if I increase the speed, the CCD kicks in and the mass override seems to be ignored :


    I have also tried uniform mass scaling to no avail. The documentation doesn't seem to indicate that this wouldn't be supported, nor that there are different flags for the CCD contact modification. :/

    Any clues? Would you like me to submit my findings on the physX GitHub page : https://github.com/NVIDIAGameWorks/PhysX/issues ?
     
    iDerp69 and Edy like this.
  14. tonytopper

    tonytopper

    Joined:
    Jun 25, 2018
    Posts:
    236
    Any plans to port this to Physics2D?
     
    NotaNaN likes this.
  15. codebiscuits

    codebiscuits

    Joined:
    Jun 16, 2020
    Posts:
    113
    Hi,

    I thought I’d try “cheating” and adding a Rigidbody on the end of my articulations to catch the collisions, hoping this would make the contact modification start working.
    Hierarchy showing FixedJoint connecting Rigidbody to ArticulationBody.png
    However, that didn’t work (behavior is identical to pure articulation hierarchy); but also, the SphereCollider is attaching to the parent ArticulationBody, rather than the Rigidbody on it’s own GameObject (so, the SphereCollider’s attachedRigidbody is null, and it’s showing attachedArticulationbody as being “FL”). Edit: I filed a bug (below)

    (So also, IIRC, OnCollisionEnter was being called at FL, rather than RBFL - not reconfirmed that in my repro project yet, however).

    Edit: updates on my original reported bug <3
    https://issuetracker.unity3d.com/is...t-evaluated-when-using-it-on-articulationbody
    Updates on this bug:
    https://issuetracker.unity3d.com/is...rigidbody-is-a-child-of-the-articulation-body


    I'd love to have a fix for this, or a suggested workaround: we're trying to implement a vehicle's wheels by "skating" a frictionless (we do our own friction/ force maths) meshcollider over terrain, and want to be able to kindof ignore the occasional penetration from "noise" in the terrain. As-is, it's mostly working very well, we just occasionally catch a "bad" polygon and the wheel pings up on its suspension spring unexpectedly. The whole vehicle's using ArticulationBody for joints and springs.
     
    Last edited: Mar 28, 2023
  16. JuozasK

    JuozasK

    Unity Technologies

    Joined:
    Mar 23, 2017
    Posts:
    84
    Having a mixed hierarchy of ArticulationBody and Rigidbody will break things for sure.
    If you want to have a rigidbody at the end of the AB, I would suggest taking it out of the AB hierarchy and keeping the Rigidbody + Fixed Joint setup.

    Though Not sure if it would solve your issue still.
     
    codebiscuits likes this.
  17. Ruchir

    Ruchir

    Joined:
    May 26, 2015
    Posts:
    934
    Is this API available in the latest alpha?
    It really would help a lot with my project
     
  18. Qleenie

    Qleenie

    Joined:
    Jan 27, 2019
    Posts:
    916
    This is already in LTS (2021), so, yes, also in alpha.
     
    JuozasK and Ruchir like this.
  19. Ruchir

    Ruchir

    Joined:
    May 26, 2015
    Posts:
    934
    A quick question is there a way to modify the forces such that there is no torque applied on a body (Player Character in my case)?
    One solution I thought of was to change the contact point to the center of mass of the character, but that would apply abnormal forces on the second body if I'm correct.
     
  20. JuozasK

    JuozasK

    Unity Technologies

    Joined:
    Mar 23, 2017
    Posts:
    84
    I would have also suggested applying the force on the CoM. You should be able to modify both of the forces applied, no? What exactly would be wrong with the approach you mentioned?
     
  21. Ruchir

    Ruchir

    Joined:
    May 26, 2015
    Posts:
    934
    To modify the position of force, we need to change the contact point, right?
    So if I do that, I'll get the results I wanted for the first body, but it could add some unwanted torque on the second object (if it's a non-kinematic rigdbody) if I'm correct.
    I can't modify the contact point for just one body after all.
     
  22. drycactus

    drycactus

    Joined:
    Sep 8, 2015
    Posts:
    20
    Hi,
    We've been integrating this fantastic new feature in 2022.2.2f1 , however I am experiencing an intermittent native crash and I've been going insane trying to narrow it down over the past few days.
    In our use-case we keep two sets of bodies, one is Rigidbody so we can 'freeze' it, the other is ArticulationBody so we can simulate (since ABs do not support a full kinematic tree).
    When switching from RB to AB, we disable all RBs and enabled all ABs and mark colliders as having modifiable contacts.
    A callback is already registered to the modification event.
    Sometimes, most likely in a build (rare in the Editor), PhysX will crash in the Simulate Step, I've been "lucky" to get the crash once in Editor and I have this line:

    Code (CSharp):
    1. Assertion failed on expression: '!onSceneContactModify.IsNull()'
    2. Crash!!!
    I know this is a long-shot question since I can't reproduce it in a simple project, but the intermittency and nature of the crash being more likely on Standalone build make me suspicious of the multi-threaded nature of the callback (as mentioned in the PhysX docs, the callback needs to be multi-thread safe and re-entrant).

    Anyone on the Unity side have any idea what could cause the above assert and related hard crash?

    Thanks,
    Patrick
     
  23. yant

    yant

    Unity Technologies

    Joined:
    Jul 24, 2013
    Posts:
    597
    Right, we saw a report like that, though that one was under quite rare conditions. Do you happen to have a somewhat usable repro for the crash? Thanks.
     
  24. drycactus

    drycactus

    Joined:
    Sep 8, 2015
    Posts:
    20
    I have it crashing consistently, only in build (not editor) with a specific setup in our game, but it's such a complicated codebase that it's not useful for a repro project.
    I notice that there is no need to have the event subscribed to (Physics.ContactModifyEvent) - all that is needed is to mark a collider as 'hasModifiableContacts' and it crashes.
    We do use LoadSceneAsync, but without any of the Physics scene stuff, so I wonder if that has any impact.
    I will try my best to get an individual repro case in a test project but I'm not too hopeful I'll be able to isolate this.
    It's a total show-stopper for us unfortunately, the rest works great :(
     
  25. Augustinas-Simkus

    Augustinas-Simkus

    Unity Technologies

    Joined:
    Mar 9, 2020
    Posts:
    84
    Hey! We had an old case tracking this crash. I've reopened it and the Issue Tracker can be found here: https://issuetracker.unity3d.com/product/unity/issues/guid/UUM-7389.
    We have a repro project and I just figured out the root cause of the crash. I'm making a Pull Request with the fix as we speak. It should be backported to all the active streams. Sorry for the inconvenience.
     
    a436t4ataf and codebiscuits like this.
  26. drycactus

    drycactus

    Joined:
    Sep 8, 2015
    Posts:
    20
    You made my day! I will keep an eye on this thread and change logs for 2022.2!
    Thanks!
     
    Augustinas-Simkus likes this.
  27. drycactus

    drycactus

    Joined:
    Sep 8, 2015
    Posts:
    20
    Hey, I've been keeping an eye on 2022 waiting for the fix to drop, in the issue tracker it's marked as fixed in 2022.3.0f1 but in the patch notes for that version there is no mention of it. Is it missing in the release notes or did it not actually make it in 2022.3.0f1?
     
  28. albert-jurkoit

    albert-jurkoit

    Unity Technologies

    Joined:
    Mar 7, 2017
    Posts:
    8
    Hey @drycactus,

    I've just checked, the fix for this crash landed in 2022.2.22f1 and was verified by QA in 2022.3 stream as well. Thank you for bringing up this crash to us, we appreciate that.
     
    yant likes this.
  29. bbjones

    bbjones

    Joined:
    Feb 1, 2016
    Posts:
    85
    I'm not sure if ContactModifyEvent will help so here's my problem.

    My scenario: Falling tree where limbs can break off but only when they collide with certain layers/tags such as terrain and buildings, but not others.

    My tree setup:
    Rigidbody on the parent object with OnCollisionEnter handling script. Many child colliders on different child objects.

    On the parent object in OnCollisionEnter I check tags through the ContactPoint:

    Code (CSharp):
    1.  
    2. private void OnCollisionEnter(Collision collision)
    3.         {
    4.             foreach (ContactPoint contactPoint in collision.contacts)
    5.             {
    6.                 if (contactPoint.thisCollider.gameObject.tag == this.limbObjectTag)
    7.                 {
    8.                     Debug.Log("TreeTop Collision match on limb tag: " + collision.gameObject.name + " collides_with " + contactPoint.thisCollider.gameObject.name);
    9.  
    10.                     // ignore certain layers, like resources
    11.                     if (collision.gameObject.IsInLayerMask(this.limbCollisionIgnoreLayers))
    12.                     {
    13.                         continue;
    14.                     }
    15.  
    16.                     ... proceed with collision and destroy the tree limb that caused the collision
    17.  
    When I proceed with collision I destroy the tree branch that collided.
    The gameplay effect is a tree that falls and some branches break off when they hit the ground while the others remain intact.

    My current problem is using the code above, the collision still happens causing the tree to bounce before the tree limb is destroyed. I was searching for ways to abort specific collisions and came across the ContactModifyEvent but it uses ContactPairs (not Points) and I don't see how to get the related gameobject tag/layers for me to evaluate if I should proceed with collision or not.

    I would just use IsTrigger on my child branch colliders but I don't want to setup rigidbodies/joints on each child object and I need to know which branch collided so only that branch can be destroyed.

    Is ContactModifyEvent going to help or should I be looking somewhere else?
     
    Last edited: Jun 4, 2023
  30. drycactus

    drycactus

    Joined:
    Sep 8, 2015
    Posts:
    20
    You should be able to do what you described with contact modification, keep in mind that since the contact modification happens on a native array you don't have access to the actual unity side obejcts (ie: GameObject), you do however know the instance IDs of colliders/gameobjects/etc. and you would keep track of those and compare them to the instance id of each object in a modifiable contact pair to then decide what to do (ignore the collision, for example).
    We're doing this successfully in our game, and while it does add a bit of complexity (keeping track of all instance IDs) it does allow for much more fine control of collision response.
     
    yant likes this.
  31. bbjones

    bbjones

    Joined:
    Feb 1, 2016
    Posts:
    85
    That sounds like it would work, but how are you tracking the instace IDs? Can I track valid collision IDs in OnCollisionEnter then process ContactModifyEvent after that point? I didn't see any clear documentation on the sequence for when things happen in relation to Unity events.
     
  32. drycactus

    drycactus

    Joined:
    Sep 8, 2015
    Posts:
    20
    When instancing the object (ex: a Prefab) you iterate through your colliders and store their instance IDs somewhere by using GetInstanceID().
    That's where the extra complexity is, you need to manually keep tracks of which IDs will collide with which other IDs.
    Keep in mind instance IDs are only valid at run-time and change between runs, so you can't serialize or store them in-between sessions.
    In our case we keep dictionaries and have logic to know which collider and body instance IDs are supposed to collide and how.
    If you're filtering out a collision you will (obviously) not get the OnCollisionEnter event for it (since the collision was filtered about before solving) so you might need to be careful with mixing contact modification and OnCollision events.
     
  33. bbjones

    bbjones

    Joined:
    Feb 1, 2016
    Posts:
    85
    Well that's a bummer :p

    In my case there could be 100's or 1000's or more of colliders that I don't want to collide with. That's why I have things setup to use layers and tags to identify which collisions should be valid or not.

    I'll look into it some more though, thanks for the info.
     
  34. Edy

    Edy

    Joined:
    Jun 3, 2010
    Posts:
    2,556
    That doesn't have todo with Native Array, but with the contact modification event called from outside the main execution thread. You may still have access to references to GameObjects and other Unity objects, but accessing their properties or methods from within the contact modification event will raise an exception.

    Here:
    https://forum.unity.com/threads/a-c...ution-order-of-unity-event-functions.1381647/

    The OnCollisionXXX events happen right after the internal physics update block (not explicitly stated in the above diagram, but easily deducted by looking at the Unity's diagram).
     
    bbjones and yant like this.
  35. yant

    yant

    Unity Technologies

    Joined:
    Jul 24, 2013
    Posts:
    597
    Pretty much as Edy said above, the extra complexity comes from the fact that the collision modification events originate directly from PhysX during the simulation phase, where it calls them on any thread right before passing the data to solver. "Any thread" might actually turn out to be the main thread too, on accident. But generally speaking, it's safe to assume invokes happen from off the main thread, and thus most of Unity API is inaccessible. Constraints are pretty much the same as you would have if you used DOTS. The event itself is somewhat restrictive too -- we assume that you won't affect the scene in any way from the events (ie no new GOs, no deletions, no property changes), because it happens during that sensitive moment in time, and physics doesn't expect changes. The only changes allowed are using the modification APIs -- you're free to set impulses, adjust points, etc. For other actions, such as GO deletions or insertions -- only read & analyse but don't act on the data instantly. Delay until after simulation.

    On the other hand, the classical OnCollision & OnTrigger events are actually invoked after the simulation has already happened, on the main thread -- so you do have access to the full GO API (however deletions are not welcome there either, for other reasons).

    Finally, let me promote this new way of reading contacts that allows threading (2022.2+). Again, anything that deals with jobs or threading will unavoidably require more attention and a bit of extra housekeeping, but the benefits are that even without threading, if you simply use the new collision event to iterate contacts on the main thread -- it's already up to 4x faster than collecting the same data via OnContact.

    Hope that's a helpful perspective.

    Anthony
     
    NotaNaN and Edy like this.
  36. bbjones

    bbjones

    Joined:
    Feb 1, 2016
    Posts:
    85
    wd
    Ok the part that didn't sink in was that you can ignore the collision before the resolver, which is obviously before OnCollisionEnter fires.

    So does this make sense?
    • My tree has a rigidbody, child branches have colliders (not IsTrigger)
    • I record the instance/colliderIDs for each branch
    • I can then add a larger IsTrigger collider to the tree (or each branch) and in OnTriggerEnter I capture instanceIDs of valid collisions like terrain or buildings
    • In ContactModifyEvent I can then detect when I want to ignore collision, which would be ignore a branch colliding with the terrain or building etc, also at that point I record which branch instance/colliderID was involved
    • At the end of that frame (fixed update perhaps) I process any recorded branch instance/colliderIDs to be processed (break off from the tree and destroy the gameobject)
     
  37. Andefob

    Andefob

    Joined:
    Sep 17, 2018
    Posts:
    100
    I am using 2021.3.23 (LTS) (but can upgrade to later 2021.3 versions) and just bumped into this same issue. Didn't see any mention about this in the manual either.

    I want to use modifiable contacts to change the angular inertia of collisions when they happen between certain objects. I still have lots of logic in OnCollisionEnter/Stay that I would like to get called even when using the modifiable contacts. But I guess that is not possible and I need to build a system of my own to take care of that if I also want to modify contacts? Perhaps doable but complex if threading needs to be considered.

    So what happens if a modifiable contact collider collides with a non-modifiable one? Modify event gets called but OnCollision still gets called but for the other collider object only?
     
  38. Andefob

    Andefob

    Joined:
    Sep 17, 2018
    Posts:
    100
    As mentioned in the previous message, I started to think about how achieve something like this myself.

    But have I misunderstood that the event might be called from different threads at the same time? This and many other examples here seem to assume it is ok to add elements to thread-unsafe data structures like Lists which to me looks like asking for problems.

    Or is it actually guaranteed the event can't be called simultaneously from two threads? It would be very good to clarify this in the documentation.
     
  39. Andefob

    Andefob

    Joined:
    Sep 17, 2018
    Posts:
    100
    @yant or anyone do you have any comments related to my previous message? I assume the code in the event handler must be thread-safe and e.g. the code in the previous message's example is not ok since it is using a non-thread safe List to collect the contacts?

    At least according to PhysX documentation, onContactModify needs to be made thread-safe. Or is there some Unity side synchronization so that this is not needed for Unity's ContactModifyEffect?

    I also noticed this fix in the latest LTS, I guess it is related?
    "UUM-7389: Physics: Fixed a race condition that caused crashes in Contact Modification callbacks."
     
  40. Andefob

    Andefob

    Joined:
    Sep 17, 2018
    Posts:
    100
    Seems like no one else is reading this thread anymore. But in case someone is interested, I am now collecting contacts by reading them into an array (index incremented by Interlocked.Increment for thread safety) and then processing them after the physics update.

    But a new problem now is is that I don't know how to get info about how big the collisions were (impulse or something similar). That is needed both for the collision audio and possibly dealing damage in the future. One hack would be to check how much an object's speed and angular speed changed during Physics.tick but still you wouldn't know which of the potentially many collisions had the biggest impact.

    Any ideas? (Using 2021.3)
     
  41. TheZombieKiller

    TheZombieKiller

    Joined:
    Feb 8, 2013
    Posts:
    267
    Are modifiable contacts expected to work with the
    CharacterController
    component? I'm seeing the appropriate
    Physics.ContactModifyEvent
    and
    Physics.ContactModifyEventCCD
    events for all other colliders, but not
    CharacterController
    (i.e., when calling
    Move
    ). All colliders have
    hasModifiableContacts
    enabled, including the
    CharacterController
    . If that's not expected behavior, I can open a bug report.

    If that is expected behavior, what would be the optimal way to modify contacts for a
    CharacterController
    ? I'm currently using contact modification events to implement a more advanced collision filtering system than what is provided through layers and
    Physics.IgnoreCollision
    , but this throws a bit of a wrench into the works.

    (Moving away from
    CharacterController
    is not an option for this scenario)
     
  42. codebiscuits

    codebiscuits

    Joined:
    Jun 16, 2020
    Posts:
    113
    Last edited: Oct 19, 2023
  43. DragonCoder

    DragonCoder

    Joined:
    Jul 3, 2015
    Posts:
    1,753
    Am really liking the "soft contact" effect of the official ECS examples here:
    https://github.com/Unity-Technologi...les/READMEimages/modify_contact_jacobians.gif

    The gif does not quite do it justice. It's not just bouncyness but the object slips slightly inside of the collider before coming out again. I need it velocity dependent as well, so I cannot just use a smaller collider thank the mesh and bounce off that or any similar "cheat".

    Its core is this code:
    Code (CSharp):
    1.  
    2.                 // Angular jacobian modifications
    3.                 for (int i = 0; i < contact.NumContacts; i++)
    4.                 {
    5.                     ContactJacAngAndVelToReachCp angular = header.GetAngularJacobian(i);
    6.  
    7.                     angular.Jac.EffectiveMass *= 0.1f;
    8.                     if (angular.VelToReachCp > 0.0f)
    9.                     {
    10.                         angular.VelToReachCp *= 0.5f;
    11.                     }
    12.  
    13.                     header.SetAngularJacobian(i, angular);
    14.                 }
    Can this be applied through the contacts API of this thread, without using ECS?
    Or how could one do this with regular GOs?
    Performance is not highest priority as it would only be for a few objects.
     
  44. codebiscuits

    codebiscuits

    Joined:
    Jun 16, 2020
    Posts:
    113
    Hi,
    I'm using ModifiableContactPair.SetMaxImpulse to allow my colliders to slip slightly inside each other before coming out again.
    The effect I'm going for is a squishy tyre, I want it to be able to cope with minor bumps without pinging into space.
    So I look at ModifiableContactPair.GetSeparation, and if it's only a small negative value, I cap the max impulse, & I raise the cap in a lerpy way the more negative the separation becomes, until the cap effectively doesn't exist.
     
    DragonCoder likes this.
  45. DragonCoder

    DragonCoder

    Joined:
    Jul 3, 2015
    Posts:
    1,753
    Thank you, that sounds really viable and my usecase is very similar!
    Unfortunately I just realized that both contacts-manipulation APIs are 3D only and I use 2D.
    So far there doesn't seem to be any possibility like this for 2D, is there?
     
  46. codebiscuits

    codebiscuits

    Joined:
    Jun 16, 2020
    Posts:
    113
    Hi, I'm not sure, sorry, I've never worked with 2D.
    However:
    https://twitter.com/melvmay/status/1504139014932410375?lang=en-GB
    suggests you can do contact modification in 2D? Somehow? Or maybe it just works?
    If you can't find info, if you post a general question in the physics forum I bet MelvMay will reply, I'm not sure if he reads this thread, but he's v active on physics & knows the 2D stuff inside out.
     
    DragonCoder likes this.
  47. boolean01

    boolean01

    Joined:
    Nov 16, 2013
    Posts:
    92
    Is there a way to specify a separate PhysicsScene to the "Physics.ContactModifyEvent" callback?

    I'm trying to use this in combination with "Physics.Simulate()", where I'm spinning up a new scene and then calling "scene.GetPhysicsScene()" to run some simulations with cloned objects. But the static call to "Physics.ContactModifyEvent" only seems to grab the first physics scene that was setup.

    I think this comment from 2020 by yant is touching on a similar topic.

    Is there any way to do this?
     
  48. yant

    yant

    Unity Technologies

    Joined:
    Jul 24, 2013
    Posts:
    597
    Are you sure about this? Would make a legitimate bug report, as the first argument to the contact modification event is supposed to be the correct physics scene for the particular object.
     
  49. boolean01

    boolean01

    Joined:
    Nov 16, 2013
    Posts:
    92
    Thanks for stopping by this old thread yant :)

    I setup a test scene to show this issue in action. I took the contact modification demo from this post in this thread to demonstrate the fix for the collider-edge-bouncing that happens when the sphere rolls over the seams between two colliders (which works great). I then took that demo and at runtime, clone the scene and run "Physics.Simulate" against it. When I use that to trace a line of where the ball will go (a prediction line), I get the following:

    contactjump.PNG

    I've attached the test project to this post. The pink preview path will show up on scene start, then you can press spacebar to push the ball along (to verify the ball does not actually bounce off the collider seams).

    Two other notes: I tried registering the contact mods both before and after creating the new simulation scene. No difference. I also tried, just on a whim, updating the version to 2023.3.0a18 - no luck there either.

    --

    Now, it's not outside the realm of possibility that I'm doing something very stupid or missing an obvious detail with my setup. I've been using this contact fix for quite a while, but I'm still a bit shaky when it comes to understanding the real inner workings of the Unity physics system. Either way hopefully I'll learn a bit more about it :)

    Thanks!
     

    Attached Files:

    Last edited: Dec 21, 2023
  50. Z0mbie1111

    Z0mbie1111

    Joined:
    Sep 13, 2021
    Posts:
    7
    There seems to be an issue with the massProperties.inverseMassScale and massProperties.inverseInertiaScale,
    they are always equal to 1.0f no matter the mass of the rigidbodies. I have tested it in 2022.3.16f1 and 2023.1.17f1 with the same result in both versions.

    I've attached a test project to this post that shows the issue (Notice how it always prints 1.0 in the console even tho the mass of the rigidbodies varies a lot)
     

    Attached Files: