Ball inexplicably losing velocity randomly two frames *after* collision events

Hi,

What could cause velocity to suddenly change two frames after a collision? I have a ball, no physics materials, no drag, bouncing around with no gravity and continuous collision detection in an enclosed space. I compute collision reactions in OnCollisionEnter myself (it’s a zero-g ball-and-paddle game) using this very simple code:

void OnCollisionEnter(Collision collision)
{
        Debug.Log($"---- COLLISION ---- {Time.time}");
        ContactPoint contact = collision.GetContact(0);
        float ballSpeed = _velocityLastFrame.magnitude;
        Vector3 travelDirection = _velocityLastFrame.normalized;
        Vector3 reboundDirection = _velocityLastFrame == Vector3.zero ? Vector3.zero : Vector3.Reflect(inDirection: travelDirection, inNormal: contact.normal);
        Vector3 reboundVelocity = ballSpeed * reboundDirection;
        _rb.velocity = reboundVelocity;
}

_velocityLastFrame is saved from _rb.velocity every FixedUpdate().

private void FixedUpdate()
{
        _velocityLastFrame = _rb.velocity;
        Debug.Log($"vel={_velocityLastFrame.magnitude} - {Time.time}");
}

Here is what I see in the log:

---- COLLISION ---- 40.44
---- COLLISION ---- 40.44
---- COLLISION ---- 40.44
---- EXIT ---- 40.44
---- EXIT ---- 40.44
vel=4.996597 - 40.46
vel=3.046128 - 40.48

EXIT is printed during OnCollisionExit, which does nothing.

Why does velocity drop from 4.996 to 3.046? Nothing weird happens around this point. It’s often just a collision off of a flat surface. I inserted a Debug.Break() statement when these cases are detected and I see situations like:

Any idea what could be causing this behavior?

Thanks!

– B.

Okay, so, after poking around a bit I discovered that OnCollisionStay events continue to fire after OnCollisionExit (which is very confusing). The velocity change can first be detected within these callbacks. A “solution” I came up with is to save the collision resolution velocity I want in OnCollisionEnter, set rb.velocity there, and then in any subsequent OnCollisionStay, I assume the event is related to the same set of contacts that triggered the most recent OnCollisionEnter and I simply rescale the new velocity vector to have the same magnitude as the velocity I had set in OnCollisionEnter.

I therefore allow the direction to change as Unity’s physics system sees fit but conserve velocity.

This isn’t suitable for collisions with dynamic objects such as the paddle because the ball can “stick to” (move along with) the paddle for several frames before going off in ultimately a different direction than the initial contact and also with added velocity. But for collisions with static objects, this seems to work acceptably well for now.

It does still feel like a bit of a nasty hack so if anyone has any better advice, this question is still open :smile: