简介
本文主要阐述胶囊体与其他包围盒的碰撞检测原理和实现方式。由于其基本原理相同,因此放在同一篇讲。
原理
对于胶囊体来说,碰撞检测的原理都是一样的,都是确定两个检测点,然后判断点与点之间的关系。因此我们可以参考胶囊体之间的碰撞检测得出胶囊体与其他包围盒的碰撞检测原理。
胶囊体与圆的碰撞,其实就是简化版的胶囊体与胶囊体的碰撞。因为在胶囊体的碰撞检测中,我们最终得到的就是圆与圆的碰撞。
因此对于胶囊体与圆来说,圆就相当于省去一个胶囊体的计算,而直接得到结果。
胶囊体与 AABB 的碰撞,我们只要先拿到 AABB 中心点在胶囊体线段上的最近点,然后根据这个最近点求 AABB 表面的最近点,然后比较两点的距离是否小于胶囊体半径即可。
我们可以使用往期碰撞检测中获取最近点的方法即可。
判断碰撞的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| float totalRadius = Mathf.Pow(data2.radius, 2);
float distance = (closest1 - closest2).sqrMagnitude;
if (distance <= totalRadius) { return true; } else { return false; }
|
胶囊体与 OBB 的碰撞,我们只要先拿到 OBB 中心点在胶囊体线段的最近点,然后根据这个最近点求 OBB 表面上的最近点,然后比较两点的距离是否小于胶囊体半径即可。
我们可以使用往期碰撞检测中获取最近点的方法即可。
判断碰撞的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| float totalRadius = Mathf.Pow(data2.radius, 2);
float distance = (closest1 - closest2).sqrMagnitude;
if (distance <= totalRadius) { return true; } else { return false; }
|
胶囊体与射线的碰撞,我们不妨把射线看成另一个胶囊体的线段部分,根据射线的起点和终点我们可以得到线段的中心点,然后就和胶囊体碰撞检测的流程一样进行即可。
不过在最终的碰撞检测部分,逻辑要和 AABB 及 OBB 一样,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| float totalRadius = Mathf.Pow(data2.radius, 2);
float distance = (closest1 - closest2).sqrMagnitude;
if (distance <= totalRadius) { return true; } else { return false; }
|
代码
工具函数
求线段上最近点1 2 3 4 5 6 7 8
| private Vector3 GetClosestPointOnLineSegment(Vector3 start, Vector3 end, Vector3 point) { Vector3 line = end - start; float ratio = Vector3.Dot(point - start, line) / Vector3.Dot(line, line); ratio = Mathf.Min(Mathf.Max(ratio, 0), 1); return start + ratio * line; }
|
求AABB最近点1 2 3 4 5 6 7 8 9
| private Vector3 GetClosestPointAABB(Vector3 pos, CollisionData other) { Vector3 nearP = Vector3.zero; nearP.x = Mathf.Clamp(pos.x, other.min.x, other.max.x); nearP.y = Mathf.Clamp(pos.y, other.min.y, other.max.y); nearP.z = Mathf.Clamp(pos.z, other.min.z, other.max.z); return nearP; }
|
求OBB最近点1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| private Vector3 GetClosestPointOBB(Vector3 pos,CollisionData other) { Vector3 nearP = data2.center; Vector3 center1 = pos; 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; }
|
碰撞检测
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| private void CollisionCapsule2Circle(CollisionData data1,CollisionData data2) { Vector3 point1 = data1.center + data1.direction * data1.extents.y; Vector3 point2 = data1.center - data1.direction * data1.extents.y;
Vector3 closest = GetClosestPointOnLineSegment(point1, point2, data2.center);
float totalRadius = Mathf.Pow(data1.radius + data2.radius, 2); float distance = (closest - data2.center).sqrMagnitude; if (distance <= totalRadius) { return true; } else { return false; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| private void CollisionCapsule2AABB(CollisionData data1,CollisionData data2) { Vector3 pointA1 = data1.center + data1.direction * data1.extents.y; Vector3 pointA2 = data1.center - data1.direction * data1.extents.y;
Vector3 closest1 = GetClosestPointOnLineSegment(pointA1, pointA2, closest2); Vector3 closest2 = GetClosestPointAABB(closest1, data2);
float totalRadius = Mathf.Pow(data1.radius, 2); float distance = (closest1 - closest2).sqrMagnitude; if (distance <= totalRadius) { return true; } else { return false; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| private void CollisionCapsule2OBB(CollisionData data1,CollisionData data2) { Vector3 pointA1 = data1.center + data1.direction * data1.extents.y; Vector3 pointA2 = data1.center - data1.direction * data1.extents.y;
Vector3 closest1 = GetClosestPointOnLineSegment(pointA1, pointA2, closest2); Vector3 closest2 = GetClosestPointOBB(closest1, data2);
float totalRadius = Mathf.Pow(data1.radius, 2); float distance = (closest1 - closest2).sqrMagnitude; if (distance <= totalRadius) { return true; } else { return false; } }
|
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
| private void CollisionRay2Capsule(CollisionData data1,CollisionData data2) { Vector3 pointA1 = data1.center; Vector3 pointA2 = data1.center + data1.direction * data1.radius;
Vector3 pointB1 = data2.center + data2.direction * data2.extents.y; Vector3 pointB2 = data2.center - data2.direction * data2.extents.y;
Vector3 center = (pointA1 + pointA2) * 0.5f;
Vector3 closest2;
if ((pointB1 - center).magnitude <= (pointB2 - center).magnitude) { closest2 = pointB1; } else { closest2 = pointB2; }
Vector3 closest1 = GetClosestPointOnLineSegment(pointA1, pointA2, closest2); closest2 = GetClosestPointOnLineSegment(pointB1, pointB2, closest1);
float totalRadius = Mathf.Pow(data2.radius, 2); float distance = (closest1 - closest2).sqrMagnitude; if (distance <= totalRadius) { return true; } else { return false; } }
|
项目工程
更新日志
- 修复 AABB 与 OBB 求最近点参数带入错误。