/**
                     * sort Used to order an array of integers in ascending order (O(n)).
                     * @param {Array} arr
                     * @return {Promise}
                     */
                    
                    function sort (arr) {
                        return new Promise(resolve => {
                            const sorted = [];
                            
                            for (const item of arr) {
                                setTimeout(item => sorted.push(item), item, item);
                            }
                            
                            setTimeout(() => resolve(sorted), arr.reduce((a, b) => a + b, 0) + 1);
                        });
                    }
                
                
                    «Nullam elementum blandit molestie.
                    Aenean condimentum lectus ut felis.
                    Phasellus fringilla nisl tincidunt dolor.»
                    
                    «Nam quis ante sed neque rhoncus.
                    Proin porta, turpis quis iaculis.
                    Maecenas tristique vulputate magna, vel laoreet.»
                    
                    «Nullam lobortis turpis a tempor molestie.
                    Maecenas fringilla nisl at malesuada porta.»
                
            
01/25/2019, Vasile Pește

How to detect collision side in Unity 2D

TL;DR: To implement the methods detecting the collision side download the following files and import them in your Unity project. When a 2D collision occurs, you just have to call the GetContactSide method from the Collision2D instance, take a look also at the usage example at the end of this post.

Detecting the side of a collision might be fundamental for certain games, Unity doesn’t help doing this: when a collision occurs only basic information is calculated, like the contact points of the collision. This post explains how to get the collision side of a collision in Unity 2D and how to get the collision side generically starting from a contact point.

  • This solution can be easily used in any context and game engine, Unity 2D is only taken as an example.
  • This solution works with any type of Collider2D (BoxCollider2D, PolygonCollider2D, CapsuleCollider2D…).
  • This solution is designed to define collision side intended as Left, Right, Top or Bottom: if a collision occurs with a PolygonCollider2D it it is possible to know only which side of the box containing the polygon collided.

How to detect collision side

As a first step imagine two entities A and B: for instance a Zombie and a Ninja, both with their own collider.

Suppose that one of the two collides with the other, for example the Zombie collides with the Ninja (A collides with B), at this phase the information necessary to retrieve the collision side is available: one collision contact point, the center point of the box containing the Zombie (A) and the maximal point of the box containing the Zombie (A).

As you can see in the figure, the maximal point is the blue dot, while one contact point is the red dot, through those points the collision side can be retrieved: if the angle of the line passing through the contact point and the center of the Zombie is between the angle of the line passing through the maximal point and the center of the Zombie, and 360 degrees minus the same angle, then the collision side is Right, the same logic is applied for the other sides by varying the range of angles in which the angle between the center and the contact point must be between.

How to detect collision side in Unity 2D

To start, a collision side type is defined, it will be returned by the method that detects the collision side.

public enum Collision2DSideType {
    None,
    Left,
    Right,
    Top,
    Bottom,
}

The Collision2D class is extended by providing a method that returns the collision side type of the current collision instance. At this point the three points necessary to detect the collision side can be retrieved and passed to the method GetContactSide which is the implementation of the algorithm previously defined.

public static class Collision2DExtensions {
    public static Collision2DSideType GetContactSide (Vector2 max, Vector2 center, Vector2 contact) {
        Collision2DSideType side = Collision2DSideType.None;
        float diagonalAngle = Mathf.Atan2(max.y - center.y, max.x - center.x) * 180 / Mathf.PI;
        float contactAngle = Mathf.Atan2(contact.y - center.y, contact.x - center.x) * 180 / Mathf.PI;
        
        if (contactAngle < 0) {
            contactAngle = 360 + contactAngle;
        }

        if (diagonalAngle < 0) {
            diagonalAngle = 360 + diagonalAngle;
        }

        if (
            ((contactAngle >= 360 - diagonalAngle) && (contactAngle <= 360)) ||
            ((contactAngle <= diagonalAngle) && (contactAngle >= 0))
        ) {
            side = Collision2DSideType.Right;
        }
        else if (
            ((contactAngle >= 180 - diagonalAngle) && (contactAngle <= 180)) ||
            ((contactAngle >= 180) && (contactAngle <= 180 + diagonalAngle))
        ) {
            side = Collision2DSideType.Left;
        }
        else if (
            ((contactAngle >= diagonalAngle) && (contactAngle <= 90)) ||
            ((contactAngle >= 90) && (contactAngle <= 180 - diagonalAngle))
        ) {
            side = Collision2DSideType.Top;
        }
        else if (
            ((contactAngle >= 180 + diagonalAngle) && (contactAngle <= 270)) ||
            ((contactAngle >= 270) && (contactAngle <= 360 - diagonalAngle))
        ) {
            side = Collision2DSideType.Bottom;
        }

        return side.Opposite();
    }
    
    public static Collision2DSideType GetContactSide (this Collision2D collision) {
        Vector2 max = collision.collider.bounds.max;
        Vector2 center = collision.collider.bounds.center;
        Vector2 contact = collision.GetContact(0).point;
        
        return GetContactSide(max, center, contact);
    }
}

Note that if A collides with B, and the collision side of A is Left, the collision side of B will be the opposite of the collision side of A, which is Right in this specific example. This is why the Opposite method is defined for the Collision2DSideType enum, this allows to get the collision side of both A and B independently if the collision event is triggered in A or B.

public static class Collision2DSideTypeExtensions {
    public static Collision2DSideType Opposite (this Collision2DSideType sideType) {
        Collision2DSideType opposite;

        if (sideType == Collision2DSideType.Left) {
            opposite = Collision2DSideType.Right;
        }
        else if (sideType == Collision2DSideType.Right) {
           opposite = Collision2DSideType.Left;
        }
        else if (sideType == Collision2DSideType.Top) {
            opposite = Collision2DSideType.Bottom;
        }
        else if (sideType == Collision2DSideType.Bottom) {
            opposite = Collision2DSideType.Top;
        }
        else {
            opposite = Collision2DSideType.None;
        }

        return opposite;
    }
}

Usage example

Given a Character class listening for a collision, when a collision occurs, the collision side can be retrieved from the Collision2D instance through the GetContactSide method, nothing else is required!

public class Character : MonoBehaviour {
    private void OnCollisionEnter2D (Collision2D collision) {
        Collision2DSideType collisionSide = collision.GetContactSide();

        if (collisionSide == Collision2DSideType.Bottom) {
            // Handle Character bottom collision.
        }
        else if (collisionSide == Collision2DSideType.Right) {
            // Handle Character right collision.
        }
    }
}

Conclusion

By using the algorithm defined in this post we can get the collision side of a collision easily using basic trigonometry. If you are using Unity you can import the files provided at the beginning of this post, they contain the code previously written, just add them in your project and act like in the usage example.