Meaning of Collision2D.normalImpulse on kinematic VS dynamic collision

Hi everyone!

I’m trying to wrap my head around Collider2D.normalImpulse. If I understood correctly (provably not) this value is the linear impulse the rigidbody recieves in the collision. This linear impulse could then be added to the velocity to solve the collision. But I’m getting way too high values for this to make sense.

I’ve got the folllwoing scenario:

  • Red: Kinematic rigidbody2D with a CircleCollide2D and a TestCollision component.
  • Green: Dynamic rigidbody2D with a CircleCollide2D.
  • Blue: A BoxCollider2D.
    TestCollision:
using UnityEngine;
using System.Collections.Generic;

public class CollisionTest : MonoBehaviour
{
    [SerializeField] private Rigidbody2D _playerRigidBody;

    private Vector2 _velocity;

    private void FixedUpdate()
    {
        float deltaTime = Time.fixedDeltaTime;
        Vector2 acceleration = Physics2D.gravity;
        _velocity += getContactImpulse();
        _playerRigidBody.MovePosition(PhysicsUtils.computeUniformlyAcceleratedMovement(transform.position, _velocity, acceleration, deltaTime));
        _velocity = PhysicsUtils.computeAcceletaredVelocity(_velocity, acceleration, deltaTime);
    }

    private readonly List<ContactPoint2D> _contacts = new();
    private Vector2 getContactImpulse()
    {
        Vector2 contactImpulse = Vector2.zero;
        _playerRigidBody.GetContacts(_contacts);
        foreach (ContactPoint2D contactPoint in _contacts)
        {
            Vector2 normal = contactPoint.normal;
            contactImpulse += normal * contactPoint.normalImpulse;
        }
        return contactImpulse;
    }
}

PhysicsUtils:

using UnityEngine;

static class PhysicsUtils
{
    public static Vector2 computeUniformlyAcceleratedMovement(Vector2 position, Vector2 velocity, Vector2 accelerated, float time) =>
        position + velocity * time + 0.5f * accelerated * time * time;

    public static Vector2 computeAcceletaredVelocity(Vector2 velocity, Vector2 accelerated, float time) =>
        velocity + accelerated * time;
}

When I hit play, the green circle falls to the floor and rests there. The red circle falls on top of the green one and I would expect it to lay there (which is what happens when both circles are dynamic and the physics engine handles the whole situation). But instead it recieves an upward velocity of over 400 and shoots high in the sky.

Could somebody shed some light about the meaning of normaImpulse? Is it possible to solve Kinemic vs dyamic collisions with this approach?

Thanks!

It seems normalImpulse is always zero when using a kinematic rigidbody2D. Also, the physics engine doesn’t simply add the normal and impulse to the velocity because if it did then everything would fly apart upon contact. The bounce settings will partly determine how the velocity is changed.

What’s likely happening with your circles is that the green circle is being pushed back out of the ground and generating contacts with the red circle and your script is applying the green circle’s contact normal and impulse to the red circle’s velocity.

Hi @zulo3d! Thanks a lot for answering!

As far as I can tell, normlaImpulse is 0 when a kinematic hits a static or another kinematic (or colliders without a rigidbody). Of course, only when “Use full kinematic contacts” is ticked, otherwise the collision doesn’t even register. But when hitting a dynamic normlaImpulse can be non-zero. I added an OnEnterCollision2D to test it in my test component:

private void OnCollisionEnter2D(Collision2D collision)
{
    collision.GetContacts(_contacts);
    foreach (ContactPoint2D contactPoint in _contacts)
    {
        Debug.Log($"gameObject = {gameObject}");
        Debug.Log($"rigidbody = {contactPoint.rigidbody}, otherRigidbody = {contactPoint.otherRigidbody}, normalImpulse = {contactPoint.normalImpulse}");
    }
}

And the result in the log was:

gameObject = Kinematic (UnityEngine.GameObject)
rigidbody = Dynamic (UnityEngine.Rigidbody2D), otherRigidbody = Kinematic (UnityEngine.Rigidbody2D), normalImpulse = 911,1533

As stated, I’m a bit lost about the meaning of normalImpulse (I thought it could be interpreted as a speed differential), so if anyone has some directions that would be super useful.

I attach my test project in case it helps at all.
PhysicsTest.zip (105.6 KB)

Thanks!

They are not relative speeds/velocities, that would be Collision2D.relativeVelocity or ContactPoint2D.relativeVelocity alongside them in the API.

Normal Impulse is the impulse the solver applies to the contact point along the collision normal to keep things separated i.e it’s part of the contact constraint and informs you of what impulse the solver applied. It’s an impulse meaning it’s the force scaled by the time-step.

These impulses are applied to any Dynamic body involved in a contact. Obviously Static and Kinematic bodies don’t have forces/impulses applied to them by definition.

Thanks @MelvMay !

So, since normalImpulse is a force by time, If I want my Kinematic red ball to react to the collision with the Dynamic green ball the same way both were dynamic, how should I go about it?

  • Should I extract the acceleration by dividing by time-step (let’s assume both balls have a mass is 1 for now) and apply an uniformly accelerated movement in the normal direction for a time-step duration?
  • Can I assume time-step is equivalent to Time.fixedDeltaTime?

Thanks a lot for you time!

You’ll not get the exact same behaviour as Dynamic because velocity changes are only one part of what the solver does. Per-contact impulses also affect rotation alongside the friction impulse, then there’s position adjustment etc.

Time-step is whatever the simulation step is so if the scene is automatically simulated using fixed-update then yes, use “Time.fixedDeltaTime”, if update then “Time.deltaTime” or manually via script then whatever you pass in there. You absolutely must scale the impulse by the time-step though.

Be aware though that you’ll be missing important things such as sleeping so you should be cautious about issue MovePosition if you’re not actually going anywhere.

Here’s some info on how Box2D uses impulses:

I’ll take a look later at your test project.

So I just took a quick look at your code and yes, just scale those impulses by “Time.fixedDeltaTime”.

Note though that your “OnCollisionEnter2D” reports after the simulation has run which is after your previous MovePosition and that FixedUpdate runs before the simulation. Just ensure you don’t get those confused. :slight_smile:

using UnityEngine;
using System.Collections.Generic;

public class CollisionTest : MonoBehaviour
{
    [SerializeField] private Rigidbody2D _playerRigidBody;

    private Vector2 _velocity;
    private Vector2 _lastImpulse;

    private void FixedUpdate()
    {
        float deltaTime = Time.fixedDeltaTime;
        Vector2 acceleration = Physics2D.gravity;
        _velocity += _lastImpulse;

        _playerRigidBody.MovePosition(PhysicsUtils.computeUniformlyAcceleratedMovement(transform.position, _velocity, acceleration, deltaTime));
        _velocity = PhysicsUtils.computeAcceletaredVelocity(_velocity, acceleration, Time.fixedDeltaTime);

        _lastImpulse = Vector2.zero;
    }
    
    private void OnCollisionEnter2D(Collision2D collision) => UpdateLastImpulse(collision);
    private void OnCollisionStay2D(Collision2D collision) => UpdateLastImpulse(collision);

    private readonly List<ContactPoint2D> _contacts = new();
    private void UpdateLastImpulse(Collision2D collision)
    {
        _lastImpulse = Vector2.zero;
        
        if (collision.GetContacts(_contacts) == 0)
            return;
        
        foreach (var contactPoint in _contacts)
        {
            _lastImpulse += contactPoint.normal * contactPoint.normalImpulse;
        }

        _lastImpulse *= Time.fixedDeltaTime;
    }
}

A few notes here:

  • You should always refer to the Rigidbody2D.position NOT the Transform.position as the source of truth on position
  • Unlike 3D physics, in 2D you can directly write the linear velocity of a Rigidbody2D to cause movement. You don’t need to use MovePosition. You can use this to store the current velocity.
1 Like

I see, it’s a more involved process than I assumed. I’m trying to manually replicate the dynamic collision reaction behavior so that my red kinematic ball stays on top of the green dynamic one instead of pushing it out of the way and falling through the floor. This is just an experiment to allow me to understand how to handle kinematic VS dynamic collisions using Unity simulation data (rather than trying to solve it my own cumbersome way), and I though this scenario would be the simplest one to begin with (I used balls so I can ignore rotation and one falling right on top of another to ignore friction).

Thanks a lot for taking the time to check this sample so thoroughly @MelvMay, I learnt a lot of interesting stuff. It still doesn’t work just like the dynamic VS dynamic solution (as expected, since you already mentioned there is more to it than just impulses), but it’s much closer. This has been on my mind for quiet a while.

1 Like

Scaling your impulse by the time-step works just fine in your test though. I did that and it worked.

It really depends on how sophisticated the dynamic behaviour is that you need to get.

Yes, It looks credible but it’s much bouncier than when setting the red circle as dynamic (which in this specific scenario doesn’t bounce in any visible way). I’ll need to dig deeper before trying something more complex.

May I ask why you’re using Kinematic as it seem that you want everything that a Dynamic body already provides?

Of course. I’m working on a player controller that needs to be able to push dynamic boxes (for physics-based puzzle-platforming shenanigans). It’s a substantially more complex target than this simple experiment but I need to get the basics right. I want to make it kinematic so I can have as much control over the player movement as possible. I still can’t rule out switching to a dynamic controller, but it would be quiet a setback.

1 Like

What is it about a Dynamic body you don’t have control over? I’m just ensuring you’re not participating in the myth that a Dynamic body doesn’t give you control! :slight_smile:

Hard to know the details but you can always use a Dynamic body then after the simulation step, modify where the body pose or something else so use those results to essentially get what the solver decided and then do whatever with it.

It sound like it all comes down to what you’re loosing by using Dynamic.

1 Like

Hmmm… it has been a while since I tried the dynamic controller approach. There were many problems I remember being able to overcome in rather hacky ways (such as making the player stand still in a slope). But I also remember having problems with the collide&slide movement (mainly not being able to stop at the exact collision point in a slope).

Then again, it’s not like a kinematic character controller doesn’t come with its own set of challenges.

Perhaps you’re right regarding the idea of having an attached dynamic rigidbody drive the interactions with other dynamic objects, although I’m a bit worried about being able to stay in sync. It’s worth giving it a shot, nonetheless.

Thanks a lot for your help @MelvMay, it was really interesting.

1 Like