胶囊体是由中间一个长度为 d,半径为 r 的圆柱体和两端两个半径为 r 的半球连接在一起的3D模型,下图
胶囊体的数学定义:到一条线段(上图圆柱体中心的红线)距离 小于等于 r 的所有点的集合。
球的数学定义:到一个点距离小于等于 r 的所有点的集合。
胶囊体与球的相交检测可以转换为点到线段的距离判断。
胶囊体半径为 r1,长度为 d,圆柱体两端中心点坐标(上图红线段两端点)分别为 A、B
球半径为 r2,球心坐标为 P
则如果点 P 到线段 AB 的距离 小于等于 r1 + r2,则球与胶囊体相交。
连接向量 AB,向量 AP,会有如下三种位置关系
第一种:
t = AP * AB = -|AC| <= 0
P不在线段 AB 范围内,且P在A左侧,P到线段最短距离为 向量 AP的长度 length = |AP|
第二种:
t = AP * AB = |AD| * |AB| <= |AB| * |AB|
P在线段AB范围内,P点到线段最短距离为 PD 的长度 length = |PD|
第三种:
t = AP * AB = |AE| * |AB| > |AB| * |AB|
P不在线段AB范围内,且P在B右侧,P到线段最短距离为 向量 BP的长度 length = |BP|
代码逻辑如下
public class CapsuleSphereIntersect
{
/// <summary>
/// 球与胶囊器相交检测
/// </summary>
/// <param name="sphereCenter">球心坐标</param>
/// <param name="sphereRadius">球半径</param>
/// <param name="capusleStart">胶囊体圆柱体一端中心坐标</param>
/// <param name="capusleEnd">胶囊体圆柱体另一端中心坐标</param>
/// <param name="capusleRadius">胶囊体半径</param>
/// <returns></returns>
public bool IsIntersect(Vector3 sphereCenter, float sphereRadius, Vector3 capusleStart, Vector3 capusleEnd, float capusleRadius)
{
float radiusSquare = (sphereRadius + capusleRadius) * (sphereRadius + capusleRadius);
float lengthSquare = PointToLineSquare(sphereCenter, capusleStart, capusleEnd);
return radiusSquare >= lengthSquare;
}
/// <summary>
/// 点到线段的距离
/// </summary>
/// <param name="point">点坐标</param>
/// <param name="lineStart">线段起点坐标</param>
/// <param name="lineEnd">线段终点坐标</param>
/// <returns></returns>
public float PointToLine(Vector3 point, Vector3 lineStart, Vector3 lineEnd)
{
float square = PointToLineSquare(point, lineStart, lineEnd);
return (float)Math.Sqrt(square);
}
/// <summary>
/// 点到线段的距离平方
/// </summary>
/// <param name="point">点坐标</param>
/// <param name="lineStart">线段起点坐标</param>
/// <param name="lineEnd">线段终点坐标</param>
/// <returns></returns>
public float PointToLineSquare(Vector3 point, Vector3 lineStart, Vector3 lineEnd)
{
Vector3 SP = point - lineStart;
Vector3 SE = lineEnd - lineStart;
float dot = Vector3.Dot(SP, SE);
if (dot <= 0)
{
return Vector3.Dot(SP, SP);
}
else if (dot < Vector3.Dot(SE, SE))
{
return Vector3.Dot(SP, SP) - dot * dot / (Vector3.Dot(SE, SE));
}
else
{
Vector3 EP = point - lineEnd;
return Vector3.Dot(EP, EP);
}
}
}
测试效果,变红说明相交