简介
圆与 OBB 进行碰撞检测,和圆与 AABB 进行碰撞检测类似,实质可以化为一个点与 OBB 进行检测,然后根据检测结果与圆的半径进行比较。
原理
和圆与 AABB 碰撞检测类似,圆与 OBB 碰撞检测也是化为点与 OBB 检测。因此,我们需要把点的坐标化为 OBB 坐标系的坐标。这里我们利用投影,把点的坐标投影到 OBB 坐标系中,然后再使用点与 AABB 检测的思路进行检测,最后把得到的交点转换回原来的坐标系中。
如图所示,我们连接两个物体的中心点 C1 和 C2,得到一个距离向量 DIR(矩形中心到圆心)。然后我们把 dir 投影到矩形的坐标轴上,得到 X 方向的投影 X’和 Y 方向的投影 Y’,由此可以得出 C1 点在矩形坐标系中的位置。
然后我们利用圆与 AABB 检测的方法得到 OBB 上距离 C1 点的最近点,并且还原这个坐标到原始坐标轴中。具体方法是令最近点 Pnear 初始值为 C2 坐标,然后在求交点在每个轴坐标的时候让 P 的每个轴的值加上坐标值distance * 坐标轴向量的分量
,即 Pnear.x += distance * axis[i].x
,以此类推。
得到最近点之后,我们就可以用和圆与 AABB 检测一样的方法来判断是否相交了。
三维空间和二维空间的求法一样,只不过多一个轴。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| using System.Collections; using System.Collections.Generic; using UnityEngine;
public class CollisionData : MonoBehaviour { public Vector3 center = Vector3.zero; public Vector3 extents = Vector3.zero; public Vector3[] axes = new Vector3[3]; public float radius = 1.0f; } ---------------------------------------------
private bool CollisionCircle2OBB(CollisionData data1,CollisionData data2) { Vector3 nearP = GetClosestPointOBB(data1,data2); float distance = (nearP - data1.center).sqrMagnitude; float radius = Mathf.Pow(data1.radius, 2); if (distance <= radius) { return true; } else { return false; } }
private Vector3 GetClosestPointOBB(CollisionData data1,CollisionData data2) { Vector3 nearP = data2.center; Vector3 center1 = data1.center; Vector3 center2 = data2.center; Vector3 dist = center1 - center2;
float[] extents = new float[3] { data2.extents.x, data2.extents.y, data2.extents.z }; Vector3[] axes = data2.axes;
for (int i = 0; i < 3; i++) { float distance = Vector3.Dot(dist, axes[i]); distance = Mathf.Clamp(distance, -extents[i], extents[i]); nearP.x += distance * axes[i].x; nearP.y += distance * axes[i].y; nearP.z += distance * axes[i].z; } return nearP; }
|
其他
和圆与圆的检测一样,圆与 OBB 检测求两点距离的时候也使用平方来计算,减少开方的性能消耗。
碰撞检测示例工程