How to differentiate standing over a collider from being overlapped

Hi there.

I’ve got a question regarding Rigidbody2D.Distance: how can I tell if a rigidbody is actually overlapped with a a collider or just standing over it?

I prepared a small test case:

  • Ground: The blue square. Has a box collider.
  • DynamicObject: The green square. Has box collider and a rigidbody2D.
  • Blocker: The red square. Has a box collider.

DynamicObject has the following script attached to debug the distance to its contacts:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DebugContacts : MonoBehaviour
{
    public Rigidbody2D PlayerRigidbody;
    private void OnGUI()
    {
        GUI.Label(new Rect(0f, 0f, 200f, 30f), "DynamicObject contacts:");
        List<ContactPoint2D> contacts = new List<ContactPoint2D>();
        int contactsCount = PlayerRigidbody.GetContacts(contacts);
        if (contactsCount == 0)
        {
            GUI.Label(new Rect(0f, 15f, 200f, 30f), "\t- NO CONTACTS -");
        }
        else
        {
            HashSet<Collider2D> colliders = new HashSet<Collider2D>();
            for (int contactIndex = 0; contactIndex < contactsCount; ++contactIndex)
            {
                colliders.Add(contacts[contactIndex].collider);
            }

            int colliderIndex = 0;
            foreach(Collider2D collider in colliders)
            {
                ColliderDistance2D colliderDistance = PlayerRigidbody.Distance(collider);
                GUI.Label(new Rect(0f, 15f * (1 + 3 * colliderIndex), 500f, 15f * (2 + 3 * colliderIndex)), $"\t* {collider.gameObject.name}");
                GUI.Label(new Rect(0f, 15f * (2 + 3 * colliderIndex), 500f, 15f * (3 + 3 * colliderIndex)), $"\t\t* Is overlapped: {colliderDistance.isOverlapped}");
                GUI.Label(new Rect(0f, 15f * (3 + 3 * colliderIndex), 500f, 15f * (4 + 3 * colliderIndex)), $"\t\t* Distance: {colliderDistance.distance}");
                ++colliderIndex;
            }
        }
    }
}

DynamicObject is suspended over Ground and when it falls I get the result showcased in the previous image. As you can see, it’s reported to be overlapped. If I sandwich DynamicObject between Ground and Blocker I get this result;


As expected DynamicObject is again overlapped with Ground.

Is there a way to tell these two cases appart besides picking some arbitrary Distance threshold to decide whether we’re over the Ground or overlapped with it?

Thanks!

Look at the magnitude of the overlap. The solver is in a constant battle of solving the contact from being overlapped and then not being overlapped. This is what the default contact offset is about. Also continuous will help with this to stop it being overlapped.

In the end though, just look at a “normal” contact in the first image, it’s +/- around the contact offset.

Why NOT use the distance threshold? It seems to be exactly what you want. Something along the lines of > -Physics2D.defaultContactOffset or >-0.005f approximately might also do but it depends on the types of primitives. Circle vs Circle is different i.e. something that doesn’t involve polygons which is where the contact offset applies.

1 Like

Thanks @MelvMay , I missed that constant, I’ll keep it in mind. The problem with the threshold is that the same value might not necessarily be the most appropriate depending on the situation. I guess identifying problem cases and fine-tuning them is the way to go.

The thing to bear in mind is that physics isn’t going to make the perimeter of colliders come into perfect contact each simulation step. It’s about solving multiple contacts such that they all no longer overlap and in the case of polygons only, this involves an offset for stabilty.

In your second image, the solver cannot solve that, it’s impossible as there is no solution. In that case you’ll likely have two contacts above that are near perfect solution and two below that are way off and it’ll likely iterate somewhere between the two, possibly cycling.

I don’t really follow what specifically you’re trying to do here. Maybe I’d have a suggestion but not sure.

Of course, sorry, I was just testing the behavior of Physics2D.Distance here. I’m working on a Kinematic Character Controller that can push dynamic objects and I’m trying to decide whether I’m just touching one of them or inside. If the dynamic object cannot move (I’m assuming due to being sandwiched against static collisions) I want to force the character outside. But if it’s just touching it I want to let it be. This is important in the case where the character is resting on a slope with a box being pulled by gravity towards it (as if I get a false positive the box will appear to push the character down the slope)