Unity实现图形相交检测

2020-04-28 20:01:44王振洲

3、x 与 P 的距离即为所求;

/// <summary>
/// 线段与点的最短距离。
/// </summary>
/// <param name="x0">线段起点</param>
/// <param name="u">线段向量</param>
/// <param name="x">求解点</param>
/// <returns></returns>
public static float SqrDistanceBetweenSegmentAndPoint(Vector2 x0, Vector2 u, Vector2 x)
 {
 float t = Vector2.Dot(x - x0, u) / u.sqrMagnitude;
 return (x - (x0 + Mathf.Clamp01(t) * u)).sqrMagnitude;
 }

为避免开方计算,结果使用距离的平方。

圆形与胶囊体

分离轴是线段上距离圆心最近的点P与圆心所在方向。

定义胶囊体:

/// <summary>
/// 胶囊体
/// </summary>
 public struct CapsuleArea
 {
 public Vector2 X0;
 public Vector2 U;
 public float d;
 }

相交判断:

/// <summary>
/// 判断胶囊体与圆形相交
/// </summary>
/// <param name="capsuleArea"></param>
/// <param name="circleArea"></param>
/// <returns></returns>
public static bool Capsule(CapsuleArea capsuleArea, CircleArea circleArea)
 {
  float sqrD = SegmentPointSqrDistance(capsuleArea.X0, capsuleArea.U, circleArea.o);
  return sqrD < (circleArea.r + capsuleArea.d) * (circleArea.r + capsuleArea.d);
 }

圆形与扇形

当扇形角度大于180度时,就不再是凸多边形了,不能适用于分离轴理论。我们可以找出相交时圆心的所有可能区域,并把区域划分成可以简单验证的几个区域,逐个试验。

这里共划分了2个区间

1、半径为两者半径和的扇形区间,角度方向同扇形。验证方法是;验证距离与夹角。
2、扇形边为轴,圆形半径为大小组成的胶囊体空间,由于扇形的对称性,我们可以通过把圆心映射到一侧,从而只需要计算1条边。

定义扇形:

/// <summary>
/// 扇形区间。
/// </summary>
 public struct SectorArea
 {
  public Vector2 o;
  public float r;
  public Vector2 direction;
  public float angle;
 }

相交检测:

/// <summary>
/// 判断圆形与扇形相交。
/// </summary>
/// <param name="sectorArea"></param>
/// <param name="target"></param>
/// <returns></returns>
  public static bool Sector(SectorArea sectorArea, CircleArea target)
  {
   Vector2 tempDistance = target.o - sectorArea.o;
   float halfAngle = Mathf.Deg2Rad * sectorArea.angle / 2;
   if (tempDistance.sqrMagnitude < (sectorArea.r + target.r) * (sectorArea.r + target.r))
   {
    if (Vector3.Angle(tempDistance, sectorArea.direction) < sectorArea.angle / 2)
    {
     return true;
    }
    else
    {
     Vector2 targetInSectorAxis = new Vector2(Vector2.Dot(tempDistance,
      sectorArea.direction), Mathf.Abs(Vector2.Dot(tempDistance, new Vector2(-sectorArea.direction.y, sectorArea.direction.x))));
     Vector2 directionInSectorAxis = sectorArea.r * new Vector2(Mathf.Cos(halfAngle), Mathf.Sin(halfAngle));
     return SegmentPointSqrDistance(Vector2.zero, directionInSectorAxis, targetInSectorAxis) <= target.r * target.r;
    }
   }
   return false;
  }