Strange collision behavior between Sphere and Concave Mesh colliders

Hello guys,

I’m trying to create a small golf game and I’m bumping into a really annoying bug, I can’t explain what’s going on here… (Unity 2020.3.0f1)

Here is what it looks like:

I just give a direction and a force to a small ball using AddForce (Impulse mode) but as soon as the ball pass above the dark green mesh, it seems to hit something and skyrockets… There’s definitely something around the distance between the ball and this green mesh, if I disabled its collider component, if I move the ball a bit higher or a bit further, everything is fine.
Also, if I put a breakpoint on Collision Hit, it says it collided with that mesh, although there’s at least 30cm between the ball and the ground at this moment.

Disabling collision on that dark green mesh:

I double checked collision distance, even with the Physics Debugger, but everything seems ok. I also checked my Physics project parameters.

Here is my setup :

The ball is a mesh with a capsule collider (but exactly the same goes for a sphere collider). “Kinematic” is disabled upon hit.

The terrain has default mesh collider parameters, flagged as not convex.

And physics parameters are as follow :

I’ve tried many things: Reduce ball’s collider size, add subdivision on ground’s mesh, … but I can’t figure out what to do…

Any help or advice would be welcome!

Chupon

To me this sounds like a problem related to continuous collision detection. Just in case, try to use “Discrete” and see if that happens.

Maybe this (see 19:26):

Wow, that’s definitely something related to Speculative Contacts as mentioned in the video.
You’re right, if I turn collision detection to Discrete, the bug doesn’t occur anymore (it creates other collision bugs though).
Thanks for the advice, I’ll dig into this!

1 Like

If continuous collision detection doesn’t work, when the ball is moving fast enough, I’d consider using raycasts following parabolic arcs that the ball is expected to follow over the next timeste. Then use a large box collider oriented to fit the predicted hit surface, if one is detected.

You may want to use layer masks and IgnoreCollision to ensure that the only object that can collide with it is the ball, and that the ball only collides with that surface.

Though, a sphere travelling through static geometry should be an ideal scenario for using non-speculative contiunous detection.

Thank you, I already had the raycast method in mind, but I didn’t know how to handle collision detection once the ray hit something. The large box collider is definitely something I’ll try :slight_smile:

The box will need to be sufficiently big to prevent it from generating a speculative contact at the edge or corner of the box.

Though I am curious as to whether “Continuous” collision detection mode doesn’t work. What happens?

Also, I’d use a spherecast instead of a raycast, given that the ball is thick. It’ll produce more stable contacts for the sphere as well.

Be warned though, if the spherecast (or raycast) starting point intersects at all with something, it will ignore that something. It is one of the most annoying thing about any sort of cast while working in Unity. You’d expect it to return the closest surface within the spherecast’s starting radius instead of nothing at all.

Finally, one thing you could do is to do all of the collision handling between the ball and static objects yourself. The process could work like this:

  • The ball rigidbody initially ignores all collision with static and kinematic objects. You’ll handle these colisions yourself.
  • The ball rigidbody can collide with other rigidbodies so that they interact appropriately with one another.

For each FixedUpdate cycle:

  • Before Physics.Simulate, store the initial state of the ball rigidbody.

  • Physics.Simulate occurs.

  • After Physics.Simulate:

  • Spherecast from where the ball was (the initial state we stored earlier) to where it would have arrived at the end of the timestep, had it not collided with anything. If a surface is detected along its path, calculate the time of impact from its initial state, and from that derive the velocity the ball would have immediately before colliding with the surface. From this you can calculate the velocity the ball would have after the collision occurs.

  • Store the time of impact, position of impact and velocity of the ball after the collision into a variable called “intermediateState”

  • If a collision occurred with a rigidbody (a collision message will be sent by Physics.Simulate):

  • Estimate the time of impact with the rigidbody using the same process above.

  • If the time of impact from the rigidbody is earlier than the time of impact you calculated, etimate the velocity the ball would have immediately after the collision with the rigidbody at the point of impact. Replace the value stored in intermediateState with this new time, position and velocity.

  • If any time remains between the intermediateState.timeOfImpact, and the current time, if means you need to continue making sweeps for further contacts with static objects. Repeat the process using the new intermediateState as a staritng point, and trace for contacts with static geometry. Eventually the remaining delta time will reach 0 or you’ll fail to find another contact.

  • Once you have the final state of the rigidbody, set its position and velocity to this value.

Caveats:

  • False collisions with rigidbodies are still possible. If the collision with the rigidbody occurred after the collision with the static geometry you determined, the rigidbody will still be affected.
  • If you want your rigidbody to be influenced by external forces (e.g. forcefields, variable wind), you’ll have to check if the state of the ball after Physics.Simulate is what you expect given its initial state as outlined in 3.1. If there are differences you’ll have to determine whether they are caused by external forces or collisions with rigidbodies and adjust your initial trajectory accordingly.
  • This doesn’t consider angular velocity changes introduced by collision with the floor, but it can be similarly calculated. For a sphere this is easier.

You can get get a callback after Physics.Simulate occurs using a co-routine that uses WaitForFixedUpdate. FixedUpdate() occurs before Physics.Simulate. See Unity - Manual: Order of execution for event functions

Wow thank you! I don’t have access to my code right now but be sure I’ll check all of this asap :slight_smile:

IIRC, enabling “Continuous” collision detection gives the same result as with “Continuous Speculative”.

1 Like

It is odd that Continuous collision detection will create false contacts.

I’ve used Continuous before, but found it often causes rigidbodies to get stuck, even when colliding with flat surfaces.

Also continuous is known to handle rotations poorly, as it does something to Rigidbody.Sweep to test for collisions, which doesn’t consider changes in rotation as it travels.

Ok I can confirm that “Continuous” have the same effect than “Continuous Speculative” in my case.
So let’s try the hard way :slight_smile:

Edit> I’ve figure out why “Continuous” behaves like this. I put the ball to “Kinematic” mode as soon as it turns to Sleep, so that I don’t have to deal with ghost collisions at all. I switch kinematic off just before hitting the ball. But it seems Unity doesn’t like the combo Kinematic + Continuous and reverts back to Speculative under the hood.