How to debug dynamic rigidbody collisions?

Hi there!

I’m currently working on a Kinematic Character Controller, more specifically developing the interaction with dynamic rigid bodies. I’m currently having trouble pushing a box up a slope and hitting a wall: depending on the speed, the box may or may not actually collide with the wall (as in register a contact or call OnCollisionEnter2D). Keep in mind it will stop, though. In case the scenario is unclear, here you have a picture:


I don’t understand what is holding back the box from closing the very small gap left to reach the wall. I’d like to know if there is any way to debug the interaction between a specific RigidBody2D and the physics system.

Thanks for your time.

If you get a physics response then you’ll always get a callback… All the contacts are available for you to retrieve at any time, you can see them in the inspector (under “Info > Contacts”) for both Rigidbody2D & Collider2D and you can turn on the display of contact point/normals too so there’s lots of ways to access this information.

I don’t follow this. What does it do then, continue to overlap? That doesn’t make sense if you’ve set it to contact and it would certainly not be intermittent. Maybe you’re not describing the scenario well here and mean something else.

Upload a simple reproduction project with instructions on how to use and what you expect and I’ll take a look. Delete the library folder to reduce the size.

Yes, I know and it’s great it exists. But It doesn’t help me in this case as what I need to know is how is speed being modified. I’m setting the speed in every FixedUpdate to the value needed to reach the collision point (found by using a box-cast). More specifically distance/Time.fixedDeltaTime. I would understand it would not perfectly match due to gravity (the box has a physics material with null friction, so I’m assuming it’s not to blame), but I would expect it to eventually converge to that point. It doesn’t though. When I get the rigidbody.velocity at the next FixedUpdate it’s already null. I assume this is caused by a collision, but I don’t know which one. I can see there are 3 contacts registered: 2 on the ground and one on the player. Perhaps the player is actually overlapped and that prevents the advance, but I don’t know how to verify this (visually the colliders certainly don’t look overlapped).

It stays stable in this position until I make the player stop pushing the box. When the box collision is predicted the player speed is adjusted to advance as much as the box needs to reach the collision point (leaving a certain space between them). It’s not really intermittent, whether the contact is registered or not depends on the speed of the player (and, by extension, the speed of the box).

That could prove challenging, but I’ll try.

You only said you needed to debug interactions and that you may or may not collide with the wall hence my reply.

The contacts contain impulse/normal information too, not just contact point. They also show you which colliders in question the contacts are for so I’m not following how it doesn’t help?

I can only go from what you’ve said and the image above and assumptions based upon that. I’m assuming the “wall” is what is stopping you because you state “speed” which I presume to mean you’re setting the velocity (?) in the direction along the slope towards the wall. If you contact that wall which (again) I have to assume is a separate collider to the slope, you’ll get a contact with it shown in all the places I mentioned but you said you already knew that so I can only presume you don’t see a contact gizmo for the wall/box.

But you’re saying there’s no contact being shown so it really doesn’t matter. You simply cannot have a collision response without a contact because it’s the contact that is being solved and nothing else. Only two things are “solved” in physics, joint constraints and contact constraints.

Are you sure it’s not just that your query before you move is indicating you’re touching so you don’t move? At some point you’ll get that.

Yes, all your assumptions were correct.

Ok, I get it now. I followed your advise (thanks!) and created a minimal project to test this case and it became quiet clear what’s going on:


It’s not a collision but the gravity contribution what is countering the speed I’m adding.
At the same time, the distance between the box and the wall becomes less that 2 * Minimum Separation. And, since I try to keep that minimum distance between to wall, the box and the player, the player stops moving.

So, now I have a new question: What would be a sensible value for Minimum Separation?

9747268–1394800–BoxWallCollisionTest.7z (33.7 KB)

I honestly have no idea.

You seem to be trying to perform some kind of simple solver which is going to fall foul of the reality of how complex writing one is. From what I can tell, you want to “pretend” to interact with Dynamic bodies by detecting when you’ve almost hit them and directly apply some kind of velocity to move them. Again, sounds like a solver.

If you detect a “hit” with the box then the naive way would be to simply impart that movement impulse to the box itself too, similar to a technique when a platform moves, you also move the things sitting on top of it explicitly i.e. not using friction. It’s unclear why you’re doing checks of the box to the wall etc.

This kind of stuff is very tricky will all sorts of edge cases.

Yes, I have a feeling this is going to be an uphill battle and will never work great.
You’re right, I’m trying to fake interaction with dynamic physics object by setting the hit object at the same speed as the player. I’m doing the box->wall check in order to tell the player when it must stop to prevent pushing the box inside the wall.
Thanks for you advice @MelvMay

Is there a reason why you’re not letting physics do this? The Kinematic Player will contact the Dynamic Box and the Dynamic Box will contact the Static wall.

Maybe there’s another solution to the actual problem you’re encountering?

Hmmm… I think I’m missing something. Do you mean to keep the box ahead of the player and, when a contact between the player and the box is detected (for example in OnEnterCollision2D), interprete the box is colliding with the wall?

Not really, I mean why don’t you allow the Kinematic Player to contact the Dynamic box? WHy are you using queries and then doing all this detecting when you’re nearly touching it? Kinematic/Dynamic work pefectly fine.

I think I’m starting to understand. So the idea would be to leverage the player contacts to find out whether I’m touching a box and, in turn, use the box contacts to check for walls on the other side, right? Yes, you’re right I could do that, but by the time I can identify this situation the player will most likely be penetrating the box. Then I would need to move it outside the box, which may look kind of jittery (not sure about the effect that would have on the box either). Perhaps there is a feature I’m missing here? Or perhaps I’m missing the point entirely.

Guessing is not going to lead anywhere, so I created a quick sample to illustrate my idea.
In essence, to decide whether the player is blocked or not:

  • Check contacts on the movement direction (assume their normals go the opposite direction).
  • If the detected contacts are with dynamic objects, check if they in turn have contacts in the movement direction
  • Recursively check contacts of contacts to find out if they are against an unmovable object.
using System.Collections.Generic;
using UnityEngine;

public class PlayerKCC : MonoBehaviour
{
    public float Speed;
    public Rigidbody2D PlayerRigidbody;
    public GameObject Ground;

    private Vector2 _rightDirection;
    private float _horizontalAxis;
    private bool _isBlockedByWall;

    private void Start()
    {
        float groundAngleRad = Ground.transform.eulerAngles.z * Mathf.Deg2Rad;
        _rightDirection = new(Mathf.Cos(groundAngleRad), Mathf.Sin(groundAngleRad));
    }

    private void Update()
    {
        _horizontalAxis = Input.GetAxis("Horizontal");
    }

    private void OnGUI()
    {
        GUI.Label(new Rect(0f, 0f, 200f, 30f), $"Is blocked by wall = {_isBlockedByWall}");
    }

    private void FixedUpdate()
    {
        float frameSpeed = Speed;
        Vector2 frameDirection = _rightDirection * _horizontalAxis;
        if (_isBlockedByWall = IsAnyContactUnmovable(GetContactPointsInDirection(PlayerRigidbody, frameDirection), frameDirection))
        {
            frameSpeed = 0f;
        }

        PlayerRigidbody.velocity = frameDirection * frameSpeed;
    }

    private List<ContactPoint2D> GetContactPointsInDirection(Rigidbody2D originRigidBody, Vector2 direction)
    {
        List<ContactPoint2D> contacts = new();
        int contactsCount = originRigidBody.GetContacts(contacts);

        if(contactsCount > 0)
        {
            // Remove contact points whose normal doesn't go the opposite direction
            _ = contacts.RemoveAll(contact => Vector2.Dot(contact.normal, direction) > -0.01f);
        }

        return contacts;
    }

    private bool IsAnyContactUnmovable(in List<ContactPoint2D> contacts, Vector2 movementDirection)
    {
        bool isAnyContactUnmovable = false;

        for(int contactIndex = 0; !isAnyContactUnmovable && contactIndex < contacts.Count; ++contactIndex)
        {
            isAnyContactUnmovable = isAnyContactUnmovable || IsContactUnmovable(contacts[contactIndex], movementDirection);
        }

        return isAnyContactUnmovable;
    }

    private bool IsContactUnmovable(in ContactPoint2D contact, Vector2 movementDirection)
    {
        bool isContactUnmovable = true;

        Rigidbody2D rigidbody = contact.rigidbody;
        if (rigidbody != null && rigidbody.bodyType == RigidbodyType2D.Dynamic)
        {
            // Recursively check if the contacts of the contacts in the movement direction are unmovable
            List<ContactPoint2D> contacts = GetContactPointsInDirection(rigidbody, movementDirection);
            isContactUnmovable = IsAnyContactUnmovable(contacts, movementDirection);
        }

        return isContactUnmovable;
    }
}

Is this what you had in mind @MelvMay ?

9757503–1397262–BoxWallCollisionTest.7z (33.2 KB)