2D空间的Obb碰撞实现

Obb全称Oriented bounding box,方向包围盒算法。其表现效果和Unity的BoxCollider并无二致。由于3D空间的OBB需要多考虑一些情况

这里仅关注2D空间下的OBB实现。

实现效果:

网上有许多OBB的实现,其步骤也未必一样,我是这么做的

在两个凸多边形中找到一根轴,凸多边形所有在这根轴上的投影点不产生相交,则这两个凸多边形不相交。

如下图:

一般取每个边的垂线作为轴,逐个投影进行测试。

代码如下(注意含有debug测试内容):

public class OBB : MonoBehaviour
{
    public bool enableDebug;
    public int debug_axisIndex;
    int mDebugInternalAxisIndex;

    public Vector2 size;

    public Color gizmosColor = Color.white;

    public Vector2 P0 { get { return transform.localToWorldMatrix.MultiplyPoint3x4(-size * 0.5f); } }
    public Vector2 P1 { get { return transform.localToWorldMatrix.MultiplyPoint3x4(new Vector3(size.x * 0.5f, -size.y * 0.5f, 0)); } }
    public Vector2 P2 { get { return transform.localToWorldMatrix.MultiplyPoint3x4(size * 0.5f); } }
    public Vector2 P3 { get { return transform.localToWorldMatrix.MultiplyPoint3x4(new Vector3(-size.x * 0.5f, size.y * 0.5f, 0)); } }

    public float Width { get { return size.x; } }
    public float Height { get { return size.y; } }


    public bool Intersects(OBB other)
    {
        var axis1 = (P1 - P0).normalized;
        var axis2 = (P3 - P0).normalized;

        var axis3 = (other.P1 - other.P0).normalized;
        var axis4 = (other.P3 - other.P0).normalized;

        mDebugInternalAxisIndex = 0;

        var isNotIntersect = false;
        isNotIntersect |= ProjectionIsNotIntersect(this, other, axis1);
        isNotIntersect |= ProjectionIsNotIntersect(this, other, axis2);
        isNotIntersect |= ProjectionIsNotIntersect(this, other, axis3);
        isNotIntersect |= ProjectionIsNotIntersect(this, other, axis4);

        return isNotIntersect ? false : true;
    }

    bool ProjectionIsNotIntersect(OBB x, OBB y, Vector2 axis)
    {
        var x_p0 = Vector3.Project(x.P0, axis).magnitude * Mathf.Sign(Vector3.Dot(Vector3.Project(x.P0, axis), axis));
        var x_p1 = Vector3.Project(x.P1, axis).magnitude * Mathf.Sign(Vector3.Dot(Vector3.Project(x.P1, axis), axis));
        var x_p2 = Vector3.Project(x.P2, axis).magnitude * Mathf.Sign(Vector3.Dot(Vector3.Project(x.P2, axis), axis));
        var x_p3 = Vector3.Project(x.P3, axis).magnitude * Mathf.Sign(Vector3.Dot(Vector3.Project(x.P3, axis), axis));

        var y_p0 = Vector3.Project(y.P0, axis).magnitude * Mathf.Sign(Vector3.Dot(Vector3.Project(y.P0, axis), axis));
        var y_p1 = Vector3.Project(y.P1, axis).magnitude * Mathf.Sign(Vector3.Dot(Vector3.Project(y.P1, axis), axis));
        var y_p2 = Vector3.Project(y.P2, axis).magnitude * Mathf.Sign(Vector3.Dot(Vector3.Project(y.P2, axis), axis));
        var y_p3 = Vector3.Project(y.P3, axis).magnitude * Mathf.Sign(Vector3.Dot(Vector3.Project(y.P3, axis), axis));

        var xMin = Mathf.Min(x_p0, x_p1, x_p2, x_p3);
        var xMax = Mathf.Max(x_p0, x_p1, x_p2, x_p3);
        var yMin = Mathf.Min(y_p0, y_p1, y_p2, y_p3);
        var yMax = Mathf.Max(y_p0, y_p1, y_p2, y_p3);

        if (enableDebug)
        {
            if (debug_axisIndex == mDebugInternalAxisIndex)
            {
                Debug.DrawRay(Vector3.Project(x.P0, axis), Vector3.one * 0.1f);
                Debug.DrawRay(Vector3.Project(x.P2, axis), Vector3.one * 0.1f);

                Debug.DrawRay(Vector3.Project(y.P0, axis), Vector3.one * 0.1f, Color.white * 0.9f);
                Debug.DrawRay(Vector3.Project(y.P2, axis), Vector3.one * 0.1f, Color.white * 0.9f);

                Debug.DrawRay(Vector3.zero, Vector3.one * 0.1f, Color.black);
                Debug.DrawRay(Vector3.zero, axis, Color.yellow);
                Debug.DrawRay(xMin * Vector3.right, Vector3.one * 0.1f, Color.blue);
                Debug.DrawRay(xMax * Vector3.right, Vector3.one * 0.1f, Color.cyan);
                Debug.DrawRay(yMin * Vector3.right, Vector3.one * 0.1f, Color.red * 0.5f);
                Debug.DrawRay(yMax * Vector3.right, Vector3.one * 0.1f, Color.red * 0.5f);

                Debug.Log("(yMin >= xMin && yMin <= xMax): " + (yMin >= xMin && yMin <= xMax) + " frame count: " + Time.frameCount);
                Debug.Log("(yMax >= xMin && yMax <= xMax): " + (yMax >= xMin && yMax <= xMax) + " frame count: " + Time.frameCount);
                Debug.Log("(xMin >= yMin && xMin <= yMax): " + (xMin >= yMin && xMin <= yMax) + " frame count: " + Time.frameCount);
                Debug.Log("(xMax >= yMin && xMax <= yMax): " + (xMax >= yMin && xMax <= yMax) + " frame count: " + Time.frameCount);
            }
            mDebugInternalAxisIndex++;
        }

        if (yMin >= xMin && yMin <= xMax) return false;
        if (yMax >= xMin && yMax <= xMax) return false;
        if (xMin >= yMin && xMin <= yMax) return false;
        if (xMax >= yMin && xMax <= yMax) return false;

        return true;
    }

    void OnDrawGizmos()
    {
        var cacheMatrix = Gizmos.matrix;
        var cacheColor = Gizmos.color;

        Gizmos.matrix = transform.localToWorldMatrix;

        Gizmos.color = gizmosColor;
        Gizmos.DrawWireCube(Vector3.zero, new Vector3(size.x, size.y, 1f));

        Gizmos.color = cacheColor;
        Gizmos.matrix = cacheMatrix;
    }
}
obb.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Test : MonoBehaviour
{
    public OBB a;
    public OBB b;


    void Update()
    {
        var isIntersects = a.Intersects(b);
        if (isIntersects)
        {
            a.gizmosColor = Color.red;
            b.gizmosColor = Color.red;
        }
        else
        {
            a.gizmosColor = Color.white;
            b.gizmosColor = Color.white;
        }
    }
}
Test.cs

猜你喜欢

转载自www.cnblogs.com/hont/p/9501169.html
今日推荐