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?

3 Answers

3

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(i);
        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);

That’s the correct way, although you should use FixedUpdate instead of Update for this because you’re dealing with physics.

I have the same problem with the unity 2019.3.12f. What is exactly your intermediate fix ?

For future users, it might be easier to use the collision.relativeVelocity.magnitude, if you are just looking for the relative velocity between your object and the object it collides with.
Like this if you are in 2d

void OnCollisionEnter2D(Collision2D collision)
{
	float velocity = collision.relativeVelocity.magnitude; 
}

Unity - Scripting API: Collision2D.relativeVelocity (unity3d.com)