Search Unity

[Reason Found] Good collision detection only after certain conditions? Faster = better?!

Discussion in 'Physics' started by HiddenMonk, Jun 9, 2015.

  1. HiddenMonk

    HiddenMonk

    Joined:
    Dec 19, 2014
    Posts:
    987
    [tl;dr]
    After a certain velocity speed, collision detection seems very accurate (specifically continuous dynamic, discrete seems to always be bad), however, under a certain velocity speed (slow, generic speeds), there is interpenetration (poor collision detection). Why is this? How to avoid?
    [/tl;dr]

    EDIT - From further testing, it seems that discrete pretty much always gives bad collision detection, and continuous dynamic only gives good collision detection after a certain velocity. Perhaps this is for performance reasons, though, If that is the case, I would like an option to have it work constantly.

    I would like to ask the developers at unity if there are certain conditions for collision detection to start kicking into high gear (specifically continuous dynamic collision detection), and if so, how to disable it. It would seem very possible for optimizing and performance reasons.

    What I mean is, after doing some tests it seems that at low, generic speeds a rigidbody would somewhat penetrate other objects (lower speeds for mesh colliders compared to primitive colliders(or at least for a cube)).
    If the rigidbody is going past a certain speed, it will no longer interpenetrate other objects, and will continuous dynamic on, it will handle very high speeds. This seems true whether you are using addforce or changing the velocity directly, although it seems if you have the velocity stay constant then it doesnt interpenetrate.

    EDIT - To test yourself, follow this..
    For you to test this... create a new scene.
    1 - Put your fixedTimestep to .01 and min penetration/default contact offset to .01
    2 -Create 2 cubes and put that at the origin (0,0,0)...
    One cube gets a mesh collider (mesh colliders give better results from my tests). Set this mesh collider cube 10 units in the z axis.
    The other cube, add this script on it.
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. [RequireComponent(typeof(Rigidbody))]
    5. public class TestInterpenetration : MonoBehaviour
    6. {
    7.     public bool interpenetrate = true;
    8.  
    9.     Rigidbody myRigidbody;
    10.     Vector3 startPosition;
    11.  
    12.     void Start()
    13.     {
    14.         myRigidbody = gameObject.GetComponent<Rigidbody>();
    15.         myRigidbody.useGravity = false;
    16.         myRigidbody.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
    17.         myRigidbody.constraints = RigidbodyConstraints.FreezeRotation;
    18.         startPosition = transform.position;
    19.         StartCoroutine(GoBack());
    20.     }
    21.  
    22.     void FixedUpdate()
    23.     {
    24.         if(interpenetrate) myRigidbody.AddForce(transform.forward * 50);
    25.         if(!interpenetrate) myRigidbody.AddForce(transform.forward * 100);
    26.         CheckInterpenetration();
    27.     }
    28.  
    29.     void CheckInterpenetration()
    30.     {
    31.         RaycastHit[] hitInfos;
    32.         hitInfos = Physics.RaycastAll(transform.position + ((transform.forward * .49f) + (-transform.right * 2)), transform.right, 5);
    33.         if(hitInfos.Length == 0) return;
    34.         foreach(RaycastHit hitInfo in hitInfos)
    35.         {
    36.             if(hitInfo.collider && hitInfo.collider.transform != transform)
    37.             {
    38.                 Debug.DrawRay(transform.position + ((transform.forward * .5f) + (-transform.right * 2)), transform.up * 10, Color.red, .5f);
    39.                 Debug.Log("Interpenetrated");
    40.             }
    41.         }
    42.     }
    43.  
    44.     IEnumerator GoBack()
    45.     {
    46.         while(true)
    47.         {
    48.             myRigidbody.velocity = Vector3.zero;
    49.             transform.position = startPosition;
    50.             yield return new WaitForSeconds(2);
    51.         }
    52.     }
    53. }
    3 - Press play. Notice in the inspector there is a checkbox for "Interpenetration". It is by default set to true. You should see red lines appearing when the boxes collide. If so, this means there was an interpenetration. Uncheck this box and you will notice the box moves faster, but there should be no more red lines, meaning collision is being handled properly. All this checkbox is doing is changing the force from 50 to 100.

    I am trying to find out why this is happening.

    If you try this out and you don't get the same results as I do, let me know what unity version you are using. I am using 5.0.2f1


    For example.
    If I take cube, make it a rigidbody with no gravity and rotations locked, collision detection mode is at continuous dynamic and put it at 0,0,0 (with collision detection discrete, it seems to always be bad until it just completely goes through)
    Take another cube with a box collider (no rigidbody) and put it at 0,0,10 (10 in z).
    - I use this for the rigidbody "myRigidbody.velocity += (Vector3.forward * 2.993);"
    - I press play and watch the rigidbody interpenetrate the other box and then snap back.
    - Now I change the value "2.993" to "2.994" or higher and it will no longer interpenetrate, no mater what number I tried above "2.993" it didn't interpenetrate. If I move my box from 0,0,10 to something else then the numbers would need to be different, but its the same case. It gets to a point to where after a certain number, it will no longer interpenetrate.
    (Note - this may not be the case for others since physics may act a bit different on everyones computers)
    - If I add a mesh collider instead of a box collider to the non rigidbody, then if the value is at "0.781" or lower it interpenetrates, but at "0.782" or higher it doesnt.
    If I use AddRelativeForce then at value "78.125" or lower it interpenetrates, but at "78.126" or higher it doesn't.
    EDIT - I noticed that if I go as low as .5 force, it wont interpenetrate, but at 1 it would. It might be inconsistent at lower velocities, but at higher it seems consistent (though of course I didn't test every value)

    My timestep is at .01, and my physics default contact offset at .01, solver iteration at 6
    I am also using unity 5.02f1 in case that matters.

    Here is what I am seeing.

    The red line indicates an interpenetration happened.

    What I want to know if there is something being done behind the scene for performance reasons to make the rigidbody have bad collision detection unless velocities are met, and if so, can we have a checkbox somewhere on the rigidbodies to remove the bad collision detection even if there is a drop in performance. I pretty much cannot use rigidbodies as it looks disgusting when it just goes inside walls for a few frames, at such low speeds, and to do a bunch of things to fight this is a pain.

    There also might be a bug in that a rigidbody that is in continuous dynamic mode before pressing play, press play, and then turned to discrete will seemingly act like its still on continuous dynamic as it wont go through things when moving really fast, compared to starting in discrete mode where it now is going through things.
     
    Last edited: Dec 20, 2016
  2. HiddenMonk

    HiddenMonk

    Joined:
    Dec 19, 2014
    Posts:
    987
    Am I the only one having such an issue? Having clean collision is, in my opinion, fundamental to games. How can anyone use physics when the collision, at slow speeds, interpenetrates the colliders it is hitting. It looks so terrible and unprofessional. What is it that everyone is doing to get such basic collisions working? (by basic collisions, I don't mean that collision detection is a basic thing in terms of code and math).
    Why does going faster make the collisions so much better? How can I get that for all my collisions? Iv'e already raised my timestep to 100 times per second, when it is advised not to. With only telling a rigidbody to AddForce in a direction and nothing else, it shows poor collision at lower speeds.
    I fail to see how anyone can utilize the physics engine when it gives results like this. If I start trying to counteract the poor results the physics give (and by poor results, I only mean in regards to the interpenetration), it only causes me to start fighting the physics which in return causes undesirable results in other areas.
    It would be understandable to me if I was told that this is just how it is, but seeing how the collision detection is so spot on when its at higher speeds (Not even that high. Around a velocity of 70 against a box collider, around 30 against mesh collider) I refuse to accept that unless given more info on why it is as it is. I am hoping someone posts telling me how wrong I am, and that all I had to do was something like "rigidBody.enableBetterCollision".

    I don't want to use a character controller, Id prefer my own rigidbody controller.
    I don't really want to have a character that is not in the physics world, but have a hidden physical representation of it behind the scenes (or vice versa). To have to go through the trouble of blending the two together in a clean way. Or to use sweeptests to get the collision beforehand and calculate the new position, needing to handle bounce, slide, etc... myself (may as well just always do it myself then without the physics). I have already tried these and it just keeps bringing up other problems.
    Of course if there is no other way, then I will have to continue down that path, but having good collision at little higher speeds being dangled in front of me is painful T.T
     
    Last edited: Jun 15, 2015
  3. HiddenMonk

    HiddenMonk

    Joined:
    Dec 19, 2014
    Posts:
    987
    The higher the timestep (less calculations per frame), the better the collision detection?

    Box collider going against box mesh collider.

    Fixed Timestep at .01
    velocity of about 40 and under is bad collision detection

    Fixed Timestep at .02
    velocity of about 20 and under is bad collision detection

    Fixed Timestep at .03
    velocity of about 13 and under is bad collision detection

    Fixed Timestep at 1
    velocity of about .5 and under is bad collision detection

    Anything above would be seemingly perfect collision detection.
    I would really like to know what is going on.

    To what I can guess... it has to do with cumulative errors.
    There are less calculations done in a higher FixedTimestep, therefore, errors from calculations affect less calculations.
    While in a lower FixedTimestep, each error keeps adding to the next calculation.

    That might explain why at lower timesteps, there are worst collision detection over a higher velocity span than at higher timesteps, but this still doesn't explain why suddenly all the errors seem to stop at a certain velocity.

    What can I do to get someone to look into this T.T
     
  4. MatthewW

    MatthewW

    Joined:
    Nov 30, 2006
    Posts:
    1,356
  5. HiddenMonk

    HiddenMonk

    Joined:
    Dec 19, 2014
    Posts:
    987
    =) that was a nice watch, but it doesnt explain the main question of the thread, which is, why after a certain velocity (which can be as low as about 30 velocity) are there seemingly no interpenetration, but under that velocity there can be lots. (mainly in regards to continuous dynamic).
    I would expect slower speeds to have better collision detection, and higher to have worst.
    I am wondering if unity, for performance reasons, is disabling continuous dynamic unless a certain velocity is reached, and if so, can there be a checkbox to have it always on.
     
  6. MatthewW

    MatthewW

    Joined:
    Nov 30, 2006
    Posts:
    1,356
    Your post is missing some details about your setup:

    - Where are you modifying velocity? FixedUpdate? Update? Modifying velocity isn't ideal; you should be using forces.

    - If you wanted to test various speeds, you should set an initial impulse rather than modifying it every frame.

    - What game use case/behavior are you trying to model/test here?
     
  7. HiddenMonk

    HiddenMonk

    Joined:
    Dec 19, 2014
    Posts:
    987
    FixedUpdate

    I am simply doing "myRigidbody.AddForce(transform.forward * force, forceMode);" or "myRigidbody.velocity += (Vector3.forward * force);" How I test this shouldn't really matter as long as I am doing very basic everyday things.

    All I am trying to do is prevent my rigidbody character from interpenetrating at such generic speeds. I am noticing that after a certain velocity, continuous dynamic seems to kick in and give great results. I am making this thread asking if this is something done for performance reasons, and if so, can we have the ability to disable it so our rigidbodies dont interpenetrate.

    For you to test this... create a new scene.
    1 - Put your fixedTimestep to .01 and min penetration/default contact offset to .01
    2 -Create 2 cubes and put that at the origin (0,0,0)...
    One cube gets a mesh collider (mesh colliders give better results from my tests). Set this mesh collider cube 10 units in the z axis.
    The other cube, add this script on it.
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. [RequireComponent(typeof(Rigidbody))]
    5. public class TestInterpenetration : MonoBehaviour
    6. {
    7.     public bool interpenetrate = true;
    8.  
    9.     Rigidbody myRigidbody;
    10.     Vector3 startPosition;
    11.  
    12.     void Start()
    13.     {
    14.         myRigidbody = gameObject.GetComponent<Rigidbody>();
    15.         myRigidbody.useGravity = false;
    16.         myRigidbody.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
    17.         myRigidbody.constraints = RigidbodyConstraints.FreezeRotation;
    18.         startPosition = transform.position;
    19.         StartCoroutine(GoBack());
    20.     }
    21.  
    22.     void FixedUpdate()
    23.     {
    24.         if(interpenetrate) myRigidbody.AddForce(transform.forward * 50);
    25.         if(!interpenetrate) myRigidbody.AddForce(transform.forward * 100);
    26.         CheckInterpenetration();
    27.     }
    28.  
    29.     void CheckInterpenetration()
    30.     {
    31.         RaycastHit[] hitInfos;
    32.         hitInfos = Physics.RaycastAll(transform.position + ((transform.forward * .49f) + (-transform.right * 2)), transform.right, 5);
    33.         if(hitInfos.Length == 0) return;
    34.         foreach(RaycastHit hitInfo in hitInfos)
    35.         {
    36.             if(hitInfo.collider && hitInfo.collider.transform != transform)
    37.             {
    38.                 Debug.DrawRay(transform.position + ((transform.forward * .5f) + (-transform.right * 2)), transform.up * 10, Color.red, .5f);
    39.                 Debug.Log("Interpenetrated");
    40.             }
    41.         }
    42.     }
    43.  
    44.     IEnumerator GoBack()
    45.     {
    46.         while(true)
    47.         {
    48.             myRigidbody.velocity = Vector3.zero;
    49.             transform.position = startPosition;
    50.             yield return new WaitForSeconds(2);
    51.         }
    52.     }
    53. }
    3 - Press play. Notice in the inspector there is a checkbox for "Interpenetration". It is by default set to true. You should see red lines appearing when the boxes collide. If so, this means there was an interpenetration. Uncheck this box and you will notice the box moves faster, but there should be no more red lines, meaning collision is being handled properly. All this checkbox is doing is changing the force from 50 to 100.

    I am trying to find out why this is happening.

    If you try this out and you don't get the same results as I do, let me know what unity version you are using. I am using 5.0.2f1

    Ive had a method of trying to fix the interpenetration, but it could probably cause other inaccuracies to the physics since it just teleports the rigidbody. (I havent tested it a lot, doesnt seem to work very well against other moving rigidbodies. I have a more accurate method for capsulecolliders, but its more complicated.)
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class RigidbodyFixCollision : MonoBehaviour
    5. {
    6.     Rigidbody myRigidbody;
    7.     Collider myCollider;
    8.  
    9.     void Start()
    10.     {
    11.         myCollider = gameObject.GetComponent<Collider>();
    12.         myRigidbody = gameObject.GetComponent<Rigidbody>();
    13.     }
    14.  
    15.     void FixedUpdate()
    16.     {
    17.         Sweep();
    18.     }
    19.  
    20.     void Sweep()
    21.     {
    22.         RaycastHit hitInfo;
    23.         myRigidbody.SweepTest(myRigidbody.velocity, out hitInfo, myRigidbody.velocity.magnitude * Time.fixedDeltaTime);
    24.         if(hitInfo.collider)
    25.         {
    26.             Vector3 fixedPosition = ExtPhysics.AnyCastCenterOnCollision(myCollider, myRigidbody.velocity, hitInfo.point);
    27.             if(fixedPosition != Vector3.zero) myRigidbody.position = fixedPosition;
    28.         }
    29.     }
    30. }
    Code (CSharp):
    1.  
    2.     public static LayerMask MaskPhysicsCastLayer = LayerMask.NameToLayer("PhysicsCast");
    3.     public static int MaskPhysicsCastIndex = (1 << LayerMask.NameToLayer("PhysicsCast"));
    4.  
    5.     public static Vector3 AnyCastCenterOnCollision(Collider collider, Vector3 castDirection, Vector3 hitPoint)
    6.     {
    7.         RaycastHit hitInfo;
    8.         Ray ray = new Ray(hitPoint, -castDirection);
    9.         collider.Raycast(ray, out hitInfo, Mathf.Infinity);
    10.         if(!hitInfo.collider)
    11.         {
    12.             LayerMask originalLayer = collider.gameObject.layer;
    13.             collider.gameObject.layer = MaskPhysicsCastLayer;
    14.             Physics.SphereCast(ray, .005f, out hitInfo, Mathf.Infinity, MaskPhysicsCastIndex); // we do a small spherecast since sometimes the raycast misses
    15.             collider.gameObject.layer = originalLayer;
    16.         }
    17.  
    18.         if(hitInfo.collider)
    19.         {
    20.             return (collider.transform.position + (castDirection.normalized * hitInfo.distance));
    21.         }
    22.         return Vector3.zero;
    23.     }
     
    Last edited: Jun 17, 2015
  8. MatthewW

    MatthewW

    Joined:
    Nov 30, 2006
    Posts:
    1,356
    I guess my broader point is, where in higher-level systems are you getting bad-feeling results because of this? Platformer logic fails? Character controls feel mushy? Physics-based AI detection doing false positives...?

    At a glance, your interpenetration code is a weird ad hoc approach, but it's late and I don't have the attention to actually try it out.

    You can lower min penetration penalty to prevent more interpenetration, but it'll also introduce more jitter. Again, what sort of gameplay are you building on top of your physics where this is an issue?
     
  9. HiddenMonk

    HiddenMonk

    Joined:
    Dec 19, 2014
    Posts:
    987
    The main concern is for my third person character. When I move the character against the wall, I will interpenetrate the wall for a split second. This is very noticable if you have the camera following the player straight on from the back. I do not want to have the camera delayed or slowed etc...

    Here is an example...(Due to youtube making the video 30fps, it misses some of the interpenetrations. About halfway through the video you should see one, though it happened more.)

    Notice how at about 9 seconds in you can see some kind of stutter when hitting the wall. This is the collider interpenetrating the wall for a few frames. You will probably not get this problem if you character has a more constant velocity to its movement.
    All I was using to move the character was "AddForce((transform.forward * Input.GetAxisRaw("Horizontal") * speed));" (the speed was set to 10)

    Now I could start doing my own checks to prevent this from happening, which I have, but it will probably mess up the physics since I am just teleporting the character to fix it. I could go into more depth and try to calculate more things, or maybe have a visual follow a invisible rigidbody etc...
    However, my main question in this thread is, why at certain velocities does it seem that all interpenetration errors go away (at least in regards to a moving rigidbody with continuous collision detection against a non moving rigidbody. Have not testing 2 moving rigidbodies).

    Collision detection with angular stuff can get pretty bad too. I don't know a nice way to handle things like this.


    The methods I use to fix collisions are probably similar to what the character controller does (raycasts). This is why you need to be moving to detect collision. For this video above, if I stop moving and have the collider spin and interpenetrate into me, its basically game over. At that point I would need to let the physics do its thing, or I will get glitchy visuals.
     
    Last edited: Jun 18, 2015
  10. LeShadow

    LeShadow

    Joined:
    May 4, 2014
    Posts:
    12
    HiddenMonk likes this.
  11. HiddenMonk

    HiddenMonk

    Joined:
    Dec 19, 2014
    Posts:
    987
    Well ill be damned. I remember reading that blog, but was a while ago.
    So it seems it is true that only after a certain velocity continuous dynamic collision detection will start to kick in. Also, since it is on physx side, unity probably wont be able to give us the ability to disable this so we can have constant continuious dynamic collision detection.

    Thanks for the find! ;)
     
  12. Zeblote

    Zeblote

    Joined:
    Feb 8, 2013
    Posts:
    1,102
    You could try building the whole scene at 10x scale, everything will look the same but velocities are much larger
     
  13. HiddenMonk

    HiddenMonk

    Joined:
    Dec 19, 2014
    Posts:
    987
    I just did a little test and I don't think this works. I would expect the physx to take into account the scale of the object/collider.
     
  14. LeRan

    LeRan

    Joined:
    Nov 24, 2015
    Posts:
    118
    @HiddenMonk Hi ! I take the liberty to revive this thread for the sake of science. Could you solve that problem ? I have exactly the same with a sort of billiard game I'm making (pétanque actually but that's basically identical as far as physics go), where the precise calculation of collisions is paramount - and so far only fast moving balls have a physically correct behaviour.
     
  15. HiddenMonk

    HiddenMonk

    Joined:
    Dec 19, 2014
    Posts:
    987
    Unfortunately I dont think there is anything we can do about it. It seems unity checks the velocity of the rigidbody and if it is moving fast they will activate continuous collision detection, which is the collision detection that will prevent rigidbodies from going into eachother (whether or not its more physically accurate I am not sure). However, if the rigidbody is moving slow enough, unity will use the discrete collision detection which is more performance friendly, but rigidbodies can go into other rigidbodies.

    Honestly, unity should give us the option to enable or disable this (assuming they didnt in a recent update), per rigidbody even, but maybe there is more going on that makes it difficult?
     
  16. LeRan

    LeRan

    Joined:
    Nov 24, 2015
    Posts:
    118
    @HiddenMonk Thanks for your answer. Unfortunately I can't just let that situation as is, because I need perfectly emulated collision for a game that is all about angles and speeds (the resulting angles at low speeds are like 20° off !)... I think I'll just have to calculate resulting trajectories manually with OnCollisionEnter, which kind of defeats the point of having a physical engine in charge of things :(

    And I totally support your point that we should have an option to force the use of continuous collision detection... Anyway, I hope you could find a workaround. Thanks again for the heads up concerning the new PhysX features.
     
  17. ashayemartins

    ashayemartins

    Joined:
    Sep 29, 2020
    Posts:
    5
    Hola it 2020
    Am having issues on continuous collision my collider keeps getting knocked off course why
     
  18. Luwyliscious

    Luwyliscious

    Joined:
    Mar 21, 2021
    Posts:
    3
    I wanted to get to the bottom of this.

    Indeed, unity physics use PhysX which does save on performance by only using CCD when the velocity between two shapes are above a certain threshold as mentionned in their documentation : https://nvidia-omniverse.github.io/PhysX/physx/5.1.2/docs/AdvancedCollisionDetection.html

    BUT I asked around on physX's GitHub and got a reply that there is an exposed parameter in physX to tweak this behavior called PxSceneDesc::ccdThreshold. See my question here: https://github.com/NVIDIA-Omniverse/PhysX/discussions/91

    The documentation for that parameter (https://gameworksdocs.nvidia.com/Ph...neDesc.html#a5b8bb05a5b151da44d30986e729172c7) states that:

    As far as I know, this parameter is not exposed in Unity (Am I wrong?) but it could be, which would enable users to set the desired trade off between simulation performance and collision accuracy.

    @yant As I have seen you expose the pxContactModifyCallback in the past, do you think it would make sens to expose control over the PxSceneDesc::ccdThreshold? (Feel free to discourage me from at mentioning you if you don't apreciate it, ahah. :) )
     
    ThaBullfrog likes this.
  19. ThaBullfrog

    ThaBullfrog

    Joined:
    Mar 14, 2013
    Posts:
    49
    +1, my game also needs access to that parameter. There are situations where the performance penalty is a small price to pay for higher quality collision resolution.