I’d be happy to be proved wrong but as far as I know Unity and/or nVidia don’t publish the equations uses for PhysX calculations, nor make anything like physic materials user extendable, so it is basically impossible to construct accurate physics systems based on precisely researched values. Unfortunately this seems to extend to edge cases like elastic collisions (zero energy loss). I’ve seen the same thing where full bounciness with zero friction and drag results in energy being added, and balls gaining rotational energy when they should be slowing down due to friction. I’m not sure if this is a floating point math issue, a bug, or a known limitation.
Fortunately elastic collisions with fixed surfaces are easy enough to calculate yourself. Forget Physic Materials and do something like this:
Don’t use Unity collision handling (not sure if this is possible when using OnCollisionEnter)
Use OnCollisionEnter or OnTriggerEnter to be alerted when a collision occurs
OnCollisionEnter: Calculate the average of the normals of the collision… OnTriggerEnter: use a Raycast to find the collision surface and get the normal
Use Vector3.Reflect to reflect velocity: “rigidbody.velocity = Vector3.Reflect(rigidbody.velocity, normal);”
For my game Stryker: First Person Football I had to use a hybrid custom + PhysX physics system to work around the limitations of the builtin Unity Physics, including modifying the dynamic friction based on the speed of the ball. It’s far from perfect but the result is fairly realistic.
I found that it only bounces perfectly (reaches the exact same height each time it bounces) if the “Linear Drag” and “Angular Drag” properties are set to 0 and the “Collision Detection” property is set to “Discrete” on the Rigidbody (I am using a 2D Rigidbody).
The Physics material must have exactly 1 “Bounciness” and 0 “Friction”.
No, this is a perfect bounce, unless you want an impulse each bounce using acceleration makes the bounces higher, consistently over time.
void OnCollisionEnter ( Collision collision )
{
// Get the contact point's normal (opposite direction)
Vector3 collisionNormal = collision.contacts[0].normal;
// Apply force in the opposite direction of the collision normal
rb.AddForce(-collisionNormal * bounce, ForceMode.Acceleration);
}