OnCollisionEnter2D get collider position at time of contact

Hello, I am trying to find some information on how to get the collider position at the point/time of contact in OnCollisionEnter2D.

It seems that this is available when doing a simple 2d geometry cast, but I think this is insufficient for my purposes, as I would like to determine collisions against other moving colliders:
https://docs.unity3d.com/2023.3/Documentation/ScriptReference/RaycastHit2D-centroid.html

For full context, the example that I am trying to get working properly involves 2 circle colliders which are set as triggers with continuous collision detection. Is there some way to calculate backwards using the information given in ContactPoint2D, or something simpler that I am missing here? Ultimately I just want to get the center position of the circle when it first makes contact with another collider.

Continuous collision detection doesn't work with triggers as continous works by stopping at the point of impact; something which triggers don't do. Also, triggers don't produce contacts so you don't have contacts to work with.

With circle triggers overlapping, you can assume that to separate them you use the vector (line segment) between their current centers i.e. CircleA position - CircleB position. This is the direction/distance between them. The point at which they'd contact is calculated by extending this line segment either both ends or one end so that it's equal to both their radii.

Alternately, if you have velocity information then you can separate them back out along the velocity direction. You then have to project the closest point along that vector.

I just put this together for the case where they are overlapped as triggers but don't have any velocity information:

using System;
using UnityEngine;

public class Separate : MonoBehaviour
{
    public CircleCollider2D CircleA;
    public CircleCollider2D CircleB;

    private void OnDrawGizmos()
    {
        if (CircleA == null || CircleB == null)
            return;

        // Fetch the circle transform details.
        var transformA = CircleA.transform;
        var transformB = CircleB.transform;
        Vector2 circleCentroidA = transformA.position;
        Vector2 circleCentroidB = transformB.position;
        var circleScaleA = transformA.lossyScale;
        var circleScaleB = transformB.lossyScale;

        // We need to scale the circle radius by the transform scale.
        var circleRadiusA = CircleA.radius * Mathf.Max(circleScaleA.x, circleScaleA.y);
        var circleRadiusB = CircleB.radius * Mathf.Max(circleScaleB.x, circleScaleB.y);
        var totalRadius = circleRadiusA + circleRadiusB;

        // Fetch the overlapped direction/distance.
        var contactDirection = circleCentroidB - circleCentroidA;
        var contactDistance = contactDirection.magnitude;

        // Edge-case where circles are exactly on top of each other.
        // You can choose a direction to separate or here we just early-out.
        if (contactDistance < Mathf.Epsilon)
            return;

        // Ignore if the circles are not overlapped.
        if (contactDistance > totalRadius)
            return;

        // Draw the calculated centroid.
        var contactNormal = contactDirection.normalized;
        var contactOffset = contactDistance - totalRadius;
        var contactCentroidA = circleCentroidA + contactNormal * contactOffset;
        Gizmos.DrawSphere(contactCentroidA, circleRadiusA);
    }
}

The OnCollisionEnter2D seems to be called correctly on objects with triggers & continuous detection. I also am able to get some information from the collision contacts. Are you saying that this information is inaccurate or garbage?

I am a little bit confused about this as the colliders are already not in the same position as when the collision occurred. In the worst cases, where both are moving at high velocity, the direction vector between them after the physics update would not even be close to the direction vector between them when they would have collided, unless I am misunderstanding?

After thinking about the problem a bit more, OnCollisionEnter2D can be used to determine that a collision did occur and which colliders were involved. I can then use CircleCast with relative positions and velocities (prior to the physics update) to get the desired position at contact time from RaycastHit2D centroid. I tested this quickly and it seems to work correctly for my purposes.


This isn't the callback for triggers, it's never called for them. That'll be OnTriggerEnter2D so you're getting something confused here. If you're getting OnCollisionEnter2D call then you have something that isn't a trigger.


Not for triggers.

Sorry, you are right. I have been messing around with this in different ways and they were not triggers. The circles are kinematic with continuous detection. Appreciate the feedback.

Kinematic isn't affected by forces so there won't be any continuous collision detection available so it'll be discrete.

I still don't understand why my solution above won't work if you consider the velocity.

Hmm, not sure if I'm still getting something wrong here. The rigidbodies are kinematic with continuous detection. I have tested with the rigidbodies moving fast enough that they will never overlap. The collision registers in this case when they are both set as continuous, but does not when they are switched to discrete.

In the image here, the black circles are the positions of the same circle before and after a physics update, and we want to know where black and red would have collided. I don't think the method that you posted above works in this case, unless I am still misunderstanding.

9277636--1299523--upload_2023-9-6_20-13-3.png

Also, I do have one small remaining issue that I am seeing, which is that the collisions always have an error of 0.015. It seems that the circles must penetrate by this amount for the collision to register. I am not sure where that number is coming from, and it seems to be independent of radius. If necessary I could just increase all the collider radii by half this amount to compensate, but I would like to understand why it is happening.


No, because they are no longer overlapped (apparently). You didn't mention anything about the state of them when asking the original question so I had to make the assumption (because you said they were triggers) that they had contacted and were still overlapping.

Apparently now they're not triggers and they're not overlapping so you need to look at my original reply in that context. If I had known they were not triggers and not overlapping (as shown in the image above) then it would've been a different answer. ;)

Presumably you're using useFullKinematicContacts so yes, you can get the continuous contact information from that.


Not an error, intentional:
https://github.com/erincatto/box2d/blob/411acc32eb6d4f2e96fc70ddbdf01fe5f9b16230/src/dynamics/b2_contact_solver.cpp#L751

https://github.com/erincatto/box2d/blob/411acc32eb6d4f2e96fc70ddbdf01fe5f9b16230/include/box2d/b2_common.h#L65C12-L65C12

b2_linearSlop * -3 = 0.005 * -3 = -0.015

Physics isn't about being a geometry intersection library but instead it's about a stable rigidbody dynamics system. To achieve that, it requires various offsets/margins etc. This is one of them.

Only features that are not used by the physics system itself will give an exact intersection and that is limited to Line/Raycast which are indeed pure geometry intersection tests.

Thanks for clearing that up. I appreciate the help in understanding these systems better.

1 Like