Stop Kinematic Rigidbody2D at collision point using MovePosition

Unity 2017.3

Hello, i’ve been trying to stop my object movement at the collision point once it collides with an obstacle.
The MovePosition applies movement downwards, the OnCollisionEnter2D tells it when to stop moving.

Body Type: Kinematic
Simulated: true
Use Full Kinematic Contacts: true
Collision Detection: Continuous


The contact point seems to be correct, but the object just goes right through it.

public bool colliding;
    protected Rigidbody2D rb2d;

    void OnEnable()
    {
        rb2d = GetComponent<Rigidbody2D>();
        colliding = false;
    }

    void FixedUpdate()
    {
        if (colliding == false)
        {
            rb2d.MovePosition(rb2d.position + Vector2.down * 5);
        }
    }

    public void OnCollisionEnter2D(Collision2D collision)
    {
        colliding = true;

        ContactPoint2D[] contactArray = new ContactPoint2D[4];
        int contacts = collision.GetContacts(contactArray);
        for (int i = 0; i < contactArray.Length; i++)
        {
            // Debugging
            Debug.DrawLine(contactArray[i].point + Vector2.up * 0.2f,
                contactArray[i].point + Vector2.down * 0.2f, Color.yellow);
            Debug.DrawLine(contactArray[i].point + Vector2.left * 0.2f,
                contactArray[i].point + Vector2.right * 0.2f, Color.yellow);
        }
    }

I think that the problem occur by my misuse of MovePosition, which tries to move first, overlaps with the collider, then calls OnCollisionEnter2D which is already too late.

Setting Body Type to Dynamic does not fix the problem, it just makes the object slide down the slope, still not stopping at the collision point.

Instead of moving to a position that might be into an overlap, try using Rigidbody2D.Cast (or Collider2D.Cast if it’s more appropriate) to see where the body and its colliders contact. If it can move to the position you want then move it there and you’ll know it won’t contact anything. If the cast does find a contact you’ll know where and can move there instead.

1 Like

So, i’ve came up with this solution, but i’m not sure if it is optimal for building a platformer, which is my intention. I’ve thought that it would be possible to build a platformer without using casts, but i guess it would be harder.

public Vector2 direction;
    public float speed;
    float distance;
    RaycastHit2D[] hit = new RaycastHit2D[1];

    void FixedUpdate()
    {
        int results = rb2d.Cast(direction.normalized, hit, speed - 0.01f);
        if (results > 0)
        {
            distance = hit[0].fraction * speed;
        }
        if (results == 0)
        {
            distance = speed;
        }
        rb2d.MovePosition(rb2d.position + direction.normalized * distance);
    }

Looks good to me.

It would depend on what you mean by optimal here. Sheer performance? Are you going to scale this up have hundreds of them perhaps?

You could make this faster by only casting one of the colliders with Collider2D.Cast rather than the Rigidbody2D one which uses all attached (non-trigger) colliders. Alternately, you could use a Physics2D.CircleCast which is faster than other collider shapes albeit slightly slower than Line/Raycasts. A Physics2D.CapsuleCast is often better suited as it generally fits characters better, especially in platformers.

It is possible but it highly depends on what your movement mechanic is. Obviously it’s a rigid-body physics simulation which can be argued isn’t especially suited to platformer behaviour which is often non-realistic or at least tends to stretch the boundaries of that definition. Manipulating gravity is super simple to give you good jumping i.e. modifying the gravity-scale of the Rigidbody2D depending on whether you’re jumping up or falling down is easy but can take some tweaking for the collision response depending on how you’re moving stuff (assuming you’re going with dynamic body-types).

Using Kinematic obviously gives you full control (or close) and you can even use Rigidbody2D.useFullKinematicContacts to ask for all contacts between kinematic v kinematic/static allowing you to have all your characters be kinematic under your control but still get contacts produced (identical to dynamic body-type but with no collision response). This means that the physics system calculates the contacts and you can decide what to do with them either by using the collision callbacks or the Physics2D.GetContacts methods. You can also use Physics2D.Distance to calculate exactly what the physics system would normally do when it gets a contact which gives you a contact, distance and normal to be used to move the object out of overlap i.e. a simple solver.

Hope some of that helps.

1 Like