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

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

实现效果:

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

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

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

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

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

Box版本代码如下:

using UnityEngine;

public class OBB : MonoBehaviour
{
public bool enableDebug;
public int debug_axisIndex;
int mDebugInternalAxisIndex; public Vector2 size; public Color gizmosColor = Color.white; Vector2 P0 { get { return transform.localToWorldMatrix.MultiplyPoint3x4(-size * 0.5f); } }
Vector2 P1 { get { return transform.localToWorldMatrix.MultiplyPoint3x4(new Vector3(size.x * 0.5f, -size.y * 0.5f, )); } }
Vector2 P2 { get { return transform.localToWorldMatrix.MultiplyPoint3x4(size * 0.5f); } }
Vector2 P3 { get { return transform.localToWorldMatrix.MultiplyPoint3x4(new Vector3(-size.x * 0.5f, size.y * 0.5f, )); } } 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 = ; 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[];
public int debug_Index;
int mDebug_Index;
public Color gizmosColor = Color.white; public bool Intersects(OBB other)
{
var isNotIntersect = false; mDebug_Index = ;
for (int i = ; i <= points.Length; i++)
{
var p0 = transform.localToWorldMatrix.MultiplyPoint3x4(points[i - ]);
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 = ; 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 = ; 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 = ; i <= points.Length; i++)
{
var p0 = transform.localToWorldMatrix.MultiplyPoint3x4(points[i - ]);
var p1 = transform.localToWorldMatrix.MultiplyPoint3x4(points[i % points.Length]); Gizmos.DrawLine(p0, p1);
} Gizmos.color = cacheColor;
}
}

OBB.cs

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

2D空间的OBB碰撞实现的更多相关文章

  1. 游戏中的2D OBB碰撞模型的碰撞算法介绍和实践

    前言 上一篇博文说道,射线与场景中模型上的所有三角形求交时,会大幅度影响效率且花费比较多的时间,因此会采取使用包围盒的形式,进行一个加速求交.在此文中介绍OBB碰撞模型的碰撞算法 OBB的碰撞模型 有 ...

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

    操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Unity2017.2.0f3 原文出处 : Quick Tip: Use Quadtrees to Detect Lik ...

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

    要注意如果是XY坐标轴的2D空间,要取差乘分量z而不是y. 实现原理是,将三角形ABC三个边(AB,BC,CA)分别与比较点判断差乘,如果这3个差乘结果表示的方向一致,说明就在三角形内. 效果: 代码 ...

  4. 2D空间中求一点是否在多边形内

    参考自这篇博文:http://www.cnblogs.com/dabiaoge/p/4491540.html 一开始没仔细看做法,浪费了不少时间.下面是最终实现的效果: 大致流程: 1.随便选取多边形 ...

  5. Cocos2d-x教程(34)-三维物体OBB碰撞检測算法

    欢迎增加Cocos2d-x 交流群:193411763 个中心点.1个旋转矩阵和3个1/2边长(注:一个旋转矩阵包括了三个旋转轴,若是二维的OBB包围盒则是一个中心点,两个旋转轴,两个1/2边长). ...

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

    本文我们讨论复数及其旋转的含义.复数很有意思,本文介绍了复数的基本定义和性质,以及它关于旋转的几何意义. 复数对于旋转的两个方面极为重要: 1. 它引入了旋转算子(rotational operato ...

  7. OBB碰撞

    OBB碰撞检测,坐标点逆时针 class OBBTest extends egret.DisplayObjectContainer { private obb1:OBB; private obb2:O ...

  8. 2D空间中求两圆的交点

    出处:https://stackoverflow.com/questions/19916880/sphere-sphere-intersection-c-3d-coordinates-of-colli ...

  9. 2D空间中求线段与圆的交点

    出处: https://answers.unity.com/questions/366802/get-intersection-of-a-line-and-a-circle.html 测试脚本(返回值 ...

随机推荐

  1. SpringMVC(二六) SpringMVC配置文件中使用mvc:view-controller标签

    在springmvc中使用mvc:view-controller标签直接将访问url和视图进行映射,而无需要通过控制器. 参考springmvc.xml内容: <?xml version=&qu ...

  2. SpringBoot使用AOP

    本文介绍SpringBoot中使用Spring AOP. 简介 AOP简介 AOP可能对于广大开发者耳熟能详,它是Aspect Oriented Programming的缩写,翻译成中文就是:面向切面 ...

  3. AE文件特别大

    解决AE的输出文件太大_百度经验 在渲染队列的输出组件中把格式选项改为H.264,然后你会得到一个MP4文件. 然后最关键的一步,打开格式选项,把目标比特率和最大比特率均设10(比特率越高,视频质量越 ...

  4. easyui中对数据的判断来显示,formatter控制

    需求效果图:(把编辑按钮根据信息是否发布,来选择显示与不显示,已发布的不能够进行编辑所以不显示) 上图中的flag为发布标识,flag值1为已发布,值2为未发布 思路:第一想到的是给这个button按 ...

  5. CODEVS 1074 食物链 2001年NOI全国竞赛(洛谷 P2024)

    题目描述 Description 动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形.A吃B,B吃C,C吃A. 现有N个动物,以1-N编号.每个动物都是A,B,C中的一种,但是我们并 ...

  6. for each ...in / for ...in / for...of

    参考博客: https://www.cnblogs.com/ruoqiang/p/6217929.html https://www.cnblogs.com/dupd/p/5895474.html 1 ...

  7. git 撤销更改的文件

    在没有git add之前: 1.撤销所有更改:git checkout . 2.撤销指定文件的更改:git checkout -- file.txt git add之后: git reset HEAD ...

  8. java多态的向上转型与向下转型(与编译时类型与运行时类型有关)

    1.编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定. 当编译时类型和运行时类型不一致时,就会出现所谓的多态. 因为子类是一个特殊的父类,因此java允许把一个子类对象直接 ...

  9. c c++ 函数不要返回局部变量的指针

    结论:普通的变量(非new的变量)都是系统自动分配的,在栈空间(连续分配),无需程序员操作,速度快,但是...空间有限,不适合大量数据,大量的话就需要自己new new出来的变量是处于大容量的堆空间, ...

  10. HTML常用标签3

    1.<a>标签,是内联标签 href:需要跳转的网址 2.id属性: 相当于一个身份证 例如: 设置id属性 3.列表标签 <ul>: 无序列表 [type属性:disc(实心 ...