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…
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!
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
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.
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
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.