2D空间的Obb碰撞实现

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

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

实现效果:

网上有许多OBB的讲解,其具体步骤也未必一样,我是这么做的

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

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

这里先上一个BOX的版本,如下图:

可以看见在右侧方块的投影轴上,得到了非相交结果

Box版本代码如下:

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

增加了一个debug开关,可以单独查看每个轴的映射信息。

那么下面是凸多边形的版本,垂线通过叉乘获取:

脚本如下:

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

public class OBB : MonoBehaviour
{
    public Vector2[] points = new Vector2[0];
    public int debug_Index;
    int mDebug_Index;
    public Color gizmosColor = Color.white;

    public bool Intersects(OBB other)
    {
        var isNotIntersect = false;

        mDebug_Index = 0;
        for (int i = 1; i <= points.Length; i++)
        {
            var p0 = transform.localToWorldMatrix.MultiplyPoint3x4(points[i - 1]);
            var p1 = transform.localToWorldMatrix.MultiplyPoint3x4(points[i % points.Length]);

            var axis = Vector3.Cross((p1 - p0), Vector3.forward).normalized;
            isNotIntersect |= ProjectionIsNotIntersect(this, other, axis);

            mDebug_Index++;
        }

        return isNotIntersect ? false : true;
    }

    bool ProjectionIsNotIntersect(OBB x, OBB y, Vector2 axis)
    {
        float xMin, xMax, yMin, yMax;
        GetMinMax(x.transform.localToWorldMatrix, x.points, axis, out xMin, out xMax);
        GetMinMax(y.transform.localToWorldMatrix, y.points, axis, out yMin, out yMax);

        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 GetMinMax(Matrix4x4 matrix, Vector2[] points, Vector2 projectAxis, out float min, out float max)
    {
        min = float.MaxValue;
        max = float.MinValue;

        for (int i = 0; i < points.Length; i++)
        {
            var p = matrix.MultiplyPoint3x4(points[i]);

            var projectValue = Vector3.Project(p, projectAxis).magnitude * Mathf.Sign(Vector3.Dot(Vector3.Project(p, projectAxis), projectAxis));

            if (projectValue > max)
                max = projectValue;
        }

        for (int i = 0; i < points.Length; i++)
        {
            var p = matrix.MultiplyPoint3x4(points[i]);

            var projectValue = Vector3.Project(p, projectAxis).magnitude * Mathf.Sign(Vector3.Dot(Vector3.Project(p, projectAxis), projectAxis));

            if (projectValue < min)
                min = projectValue;
        }
    }

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

        Gizmos.color = gizmosColor;

        for (int i = 1; i <= points.Length; i++)
        {
            var p0 = transform.localToWorldMatrix.MultiplyPoint3x4(points[i - 1]);
            var p1 = transform.localToWorldMatrix.MultiplyPoint3x4(points[i % points.Length]);

            Gizmos.DrawLine(p0, p1);
        }

        Gizmos.color = cacheColor;
    }
}

OBB.cs

到这里就结束了,如果是一个具体形状和点进行比较方法其实是非常多的,而类似这样的凸包之间进行相交测试OBB倒是一个蛮实用的办法。

原文地址:https://www.cnblogs.com/hont/p/9501169.html

时间: 2024-11-06 07:09:12

2D空间的Obb碰撞实现的相关文章

[译]2D空间中使用四叉树Quadtree进行碰撞检测优化

操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Unity2017.2.0f3 原文出处 : Quick Tip: Use Quadtrees to Detect Likely Collisions in 2D Space 许多游戏需要使用碰撞检测算法去判定两个对象是否发生碰撞,但是这些算法通常意味着昂贵操作,拖慢游戏的运行速度.在这篇文章中我们将会学习四叉树 quadtrees,并学习如果通过四叉树跳过那些物理空间距离比较远的对象,最终提高碰撞检测速度. 注:原文

2D空间中判断一点是否在三角形内

本来打算做三角形填充多边形,但需要用到耳切法正在看.所以先研究了这个 要注意如果是XY坐标轴的2D空间,要取差乘分量z而不是y. 实现原理是,将三角形ABC三个边(AB,BC,CA)分别与比较点判断差乘,如果这3个差乘结果表示的方向一致,说明就在三角形内. 效果: 代码(Unity3D): using UnityEngine; using System.Collections; using System.Collections.Generic; public class TriangleColl

复数基础及其2D空间的旋转

本文我们讨论复数及其旋转的含义.复数很有意思,本文介绍了复数的基本定义和性质,以及它关于旋转的几何意义. 复数对于旋转的两个方面极为重要: 1. 它引入了旋转算子(rotational operator)的思想:可以通过复数表示一个旋转变换. 2. 它是四元数和多向量的内在属性. 虽然我们暂时不讨论四元数和多向量(后面文章会介绍),但是我们会讨论复数的旋转含义(复平面上的 2D 旋转),以及引入的旋转子(rotor),我们发现通过特定的复数可以描述一个 2D 旋转. 介绍 复数(complex

2D空间中求两圆的交点

出处:https://stackoverflow.com/questions/19916880/sphere-sphere-intersection-c-3d-coordinates-of-collision-points 修改(加入包含和不相交情况的判断): using System.Collections; using System.Collections.Generic; using UnityEngine; public class CircleIntersect : MonoBehav

游戏碰撞之OBB算法实现(java代码实现)

公司业务需求 游戏2D模型有圆形和矩形,判断碰撞说白了就是检测 : 1.圆形跟圆形是否有相交 2.圆形跟矩形是否相交 3.矩形和矩形是否相交 先明白要实现的原理,才能有思路写代码 第1个最好判断,判断两个圆中心点的矩形是否小于这两个圆的半径之和 第2个纠结了我一下,不过也不难先看图圆跟矩形关系有4种情况,如下图 只要判断圆心到矩形4条边的距离都小于圆的半径或者圆心在矩形内则它们相交,还要判断圆心在矩形内是防止出现上面第四张图那样的特殊情况 圆心到边的距离有如下两种情况 两个箭头表示点到线段的距离

带着canvas去流浪系列之八 碰撞【华为云技术分享】

[摘要] canvas动画-碰撞仿真 示例代码托管在:http://www.github.com/dashnowords/blogs 经过前面章节相对枯燥的练习,相信你已经能够上手canvas的原生API了,那么从这一节开始,我们就开始接触点好玩的东西——动画. 经过前面章节相对枯燥的练习,相信你已经能够上手canvas的原生API了,那么从这一节开始,我们就开始接触点好玩的东西——动画. 一. canvas的能力 如果你以为canvas只能绘制图表那真的就图样图森破了,且不谈webgl的绘图上

AABB包围盒、OBB包围盒、包围球的比較

1) AABB 包围盒: AABB 包围盒是与坐标轴对齐的包围盒, 简单性好, 紧密性较差(尤其对斜对角方向放置的瘦长形对象, 採用AABB, 将留下非常大的边角空隙, 导致大量不是必需的包围盒相交測试).当物体旋转之后需对AABB 进行相同的旋转并更新; 当物体变形之后仅仅需对变形了的基本几何元素相应的包围盒又一次计算; 然后能够自下向上由子结点的AABB 合成父结点的AABB, 最后进行包围盒树的更新. 2) OBB 包围盒: OBB 碰撞检測方法紧密性是较好的, 可以大大降低參与相交測试的

AABB边框、OBB边框、通过比较球包围

1) AABB 包围盒: AABB 包围盒是与坐标轴对齐的包围盒, 简单性好, 紧密性较差(尤其对斜对角方向放置的瘦长形对象, 採用AABB, 将留下非常大的边角空隙, 导致大量不是必需的包围盒相交測试).当物体旋转之后需对AABB 进行相同的旋转并更新; 当物体变形之后仅仅需对变形了的基本几何元素相应的包围盒又一次计算; 然后能够自下向上由子结点的AABB 合成父结点的AABB, 最后进行包围盒树的更新. 2) OBB 包围盒: OBB 碰撞检測方法紧密性是较好的, 可以大大降低參与相交測试的

基于cocos2d-x对俯视角游戏碰撞检测及碰撞处理的探究

作者:i_dovelemon 来源:CSDN 日期:2015/2/10 主题:俯视角游戏,碰撞检测,碰撞处理 引言 对于任何游戏来说,碰撞检测和碰撞处理都是非常重要的内容.最近自己在编写一个俯视角的类rouge-like的游戏.游戏基于网格来设计地图,在游戏设计过程中,尤其是在设计游戏的碰撞系统的时候遇到了麻烦.经过多方面的努力,终于解决了问题,现在就此问题,记录下我的心得和体会. 碰撞检测 在我的游戏中,大部分的时候都是使用AABB-AABB这种碰撞盒来进行碰撞.对于此种的碰撞体,在前面的文章