11-09-2020, 12:11 PM
(This post was last modified: 11-09-2020, 12:12 PM by adscott1982.)
Thank you for going into so much detail.
The problem I found with using a normal rigidbody, was that the spin on the ball, and the effect on the ball when bouncing with spin was very inaccurate. I spent a lot of time trying to resolve this in code to get it to behave more accurately, and it is improved to some extent, and yet there are still noticeable problems. I have included the code below if you are interested, it too a long time tinkering to try and get it to behave more or less realistically, but it feels hacky, and I would much rather simulate the whole thing in a non-hacky way.
Additionally, when the ball is rolling along the ground, it does not come to rest properly, it will just keep rolling indefinitely in some cases. When the ball is in the air it makes sense to have a slight angular drag, but on the ground it should come to rest more quickly depending on the friction of the surface. None of this works properly for the sphere rigidbody in my experimentation. This is why I mention that I thought the sphere rigidbody must have a tiny contact point, otherwise the friction when rolling along surfaces would make a difference.
The problem I found with using a normal rigidbody, was that the spin on the ball, and the effect on the ball when bouncing with spin was very inaccurate. I spent a lot of time trying to resolve this in code to get it to behave more accurately, and it is improved to some extent, and yet there are still noticeable problems. I have included the code below if you are interested, it too a long time tinkering to try and get it to behave more or less realistically, but it feels hacky, and I would much rather simulate the whole thing in a non-hacky way.
Additionally, when the ball is rolling along the ground, it does not come to rest properly, it will just keep rolling indefinitely in some cases. When the ball is in the air it makes sense to have a slight angular drag, but on the ground it should come to rest more quickly depending on the friction of the surface. None of this works properly for the sphere rigidbody in my experimentation. This is why I mention that I thought the sphere rigidbody must have a tiny contact point, otherwise the friction when rolling along surfaces would make a difference.
Code:
private void DoCollision(Collision collision)
{
var contact = collision.contacts[0];
// Get the rotation of the ball with respect to the collision normal, and its effective speed in units per second
var forwardRotation = Quaternion.AngleAxis(90, -contact.normal) * this._rb.angularVelocity;
var rotationsPerSecond = forwardRotation.magnitude / (2 * Mathf.PI);
var rotationUnitsPerSecond = rotationsPerSecond * this._circumference;
// The velocity of the ball when projected onto the normal plane
var projectedVelocity = Vector3.ProjectOnPlane(this._rb.velocity, contact.normal);
var collisionVelocity = Vector3.Project(this._rb.velocity, contact.normal);
// The rotation direction with respect to the collision normal plane
var temp = Vector3.Cross(contact.normal, forwardRotation);
var relativeRotationDirection = Vector3.Cross(temp, contact.normal).normalized * rotationUnitsPerSecond;
var vectorDifference = relativeRotationDirection - projectedVelocity;
// Distribute the angular/velocity difference between the angular and normal velocity, incorporating some energy loss
var staticFriction = collision.collider.material.staticFriction;
var scaledVelocity = vectorDifference.ScaledUniformly(0.2f * staticFriction);
var scaledRotationUnits = vectorDifference.ScaledUniformly(-0.8f * staticFriction);
var scaledRotationRadians = scaledRotationUnits.ScaledUniformly((2 * Mathf.PI) / this._circumference);
var scaledRotationRadiansCorrected = Quaternion.FromToRotation(relativeRotationDirection, forwardRotation) * scaledRotationRadians;
var scaledRotationAngularVelocity = Quaternion.AngleAxis(-90, -contact.normal) * scaledRotationRadiansCorrected;
this._rb.velocity += scaledVelocity;
this._rb.angularVelocity += scaledRotationAngularVelocity;
// Apply collision friction
var dynamicFriction = collision.collider.material.dynamicFriction;
this._rb.angularVelocity = this._rb.angularVelocity.ScaledUniformly(1 - dynamicFriction);
// TODO: Increase force of friction when the collision velocity is high
Debug.DrawRay(this.transform.position, relativeRotationDirection, Color.red);
Debug.DrawRay(this.transform.position, projectedVelocity, Color.blue);
Debug.DrawRay(this.transform.position, collisionVelocity, Color.cyan);
Debug.DrawRay(this.transform.position, vectorDifference, Color.yellow);
Debug.DrawRay(this.transform.position, contact.normal, Color.green);
}