- Home /
Velocity before collision
I have some problems, with velocity. I want play sound when rigidbody collide something or other object collide with that rigidbody only when velocity is enough big. When i take velocity in OnColliderEnter function I receive very small value and always a same. Now i update velocity in Update() and when collision is detected use last velocity. Maybe exist another way?
Answer by Eric5h5 · Sep 04, 2011 at 08:10 PM
That's the correct way, although you should use FixedUpdate instead of Update for this because you're dealing with physics.
Answer by DMGregory · Aug 17, 2019 at 02:32 PM
I nearly used caching as described here, but I was unsatisfied with that for two reasons:
It neglects anything that happened over the course of the last physics step that might have changed the velocity just prior to collision.
If you need the incident velocity of both members of the collision, then you need a velocity caching script on both of them - so it doesn't work for general collisions against arbitrary bodies
Fortunately, we can reconstruct the velocities using Collision.impulse:
static Vector3 ComputeIncidentVelocity(Rigidbody body, Collision collision, out Vector3 otherVelocity) {
Vector3 impulse = collision.impulse;
// Both participants of a collision see the same impulse, so we need to flip it for one of them.
if (Vector3.Dot(collision.GetContact(0).normal, impulse) < 0f)
impulse *= -1f;
otherVelocity = Vector3.zero;
// Static or kinematic colliders won't be affected by impulses.
var otherBody = collision.rigidbody;
if(otherBody != null) {
otherVelocity = otherBody.velocity;
if(!otherBody.isKinematic)
otherVelocity += impulse / otherBody.mass;
}
return body.velocity - impulse / body.mass;
}
In 2D, we don't have the total impulse just handed to us, so we have to compute it, but we don't need to flip it afterwards, nor pass in "our" rigidbody2D as a parameter:
static Vector2 ComputeTotalImpulse(Collision2D collision) {
Vector2 impulse = Vector2.zero;
int contactCount = collision.contactCount;
for(int i = 0; i < contactCount; i++) {
var contact = collision.GetContact(0);
impulse += contact.normal * contact.normalImpulse;
impulse.x += contact.tangentImpulse * contact.normal.y;
impulse.y -= contact.tangentImpulse * contact.normal.x;
}
return impulse;
}
static Vector2 ComputeIncidentVelocity(Collision2D collision, out Vector2 otherVelocity) {
Vector2 impulse = ComputeTotalImpulse(collision);
otherVelocity = Vector2.zero;
var otherBody = collision.rigidbody;
if (otherBody != null) {
otherVelocity = otherBody.velocity;
if(otherBody.isKinematic == false)
otherVelocity += impulse / otherBody.mass;
}
var myBody = collision.otherRigidbody;
return myBody.velocity - impulse / myBody.mass;
}
In testing this, I find it shows small errors for glancing collisions - so there may be an additional factor I'm missing. Corrections to improve the accuracy for these cases are welcome.
Thank you for this examle! I will try it out.
I only noticed in 2D version that this var contact = collision.GetContact(0);
should probably be this: var contact = collision.GetContact(i);
Your answer
Follow this Question
Related Questions
Move until collision sticks 0 Answers
How do I move this rigidbody? 1 Answer
Sword slashing logic on Gear VR using controller? 0 Answers
Prevent character controller from changing movement direction when colliding with something. 0 Answers
Velocity powered rigidbody on a moving platform without parenting. 3 Answers