游戏制作中经常需要各种形状的相交检测,那么如何检测一个圆形和矩形是否相交呢?
答案是用BoxCollider和SphereCollider~
慢着,如果不想用物理引擎该怎么办呢?
可以使用代码进行相交检测,思路就是先把圆形转换到矩形的局部坐标系下,再判断圆形的上下左右四个点是否在矩形内,如果没有判断矩形四个顶点是否在圆形内。
下面扩展了Transform类的方法,用矩形的transform调用IsInSquareRange方法,传入相应的参数就可。
注:
- 要求矩形的transform.position就是矩形的中心。
- 下面只计算了XZ平面上的相交,忽略了Y轴
///获取点对物体的相对坐标,忽略缩放
public static Vector3 GetRelativePosition(this Transform transform,Vector3 position)
{
Vector3 vector = position - transform.position;
Vector3 relativePosition = Vector3.zero;
relativePosition.x = Vector3.Dot(vector, transform.right.normalized);
relativePosition.y = Vector3.Dot(vector, transform.up.normalized);
relativePosition.z = Vector3.Dot(vector, transform.forward.normalized);
return relativePosition;
}
private static bool IsPointInSquareRange(Vector2 halfSize, float posX, float posZ)
{
return Mathf.Abs(posX) <= halfSize.x && Mathf.Abs(posZ) <= halfSize.y;
}
///获取矩形四角点的世界坐标
public static Vector3[] GetSquareCorners(this Transform transform, float sizeX, float sizeZ)
{
sizeX = sizeX / 2;
sizeZ = sizeZ / 2;
var center = transform.localPosition;
float rotation = transform.localEulerAngles.y;
Vector3[] corners = new Vector3[4];
corners[0] = new Vector3(-sizeX, 0, sizeZ);
corners[1] = new Vector3(sizeX, 0, sizeZ);
corners[2] = new Vector3(-sizeX, 0, -sizeZ);
corners[3] = new Vector3(sizeX, 0, -sizeZ);
var q = Quaternion.AngleAxis(rotation, Vector3.up);
for (int i = 0; i < 4; i++)
{
corners[i] = q * corners[i] + center;
}
return corners;
}
/// <summary>
/// 检查圆中是否和矩形相交
/// </summary>
/// <param name="transform">矩形Transform</param>
/// <param name="squareSize">矩形尺寸</param>
/// <param name="center">圆中心坐标</param>
/// <param name="radius">圆形半径</param>
public static bool IsInSquareRange(this Transform transform, Vector2 squareSize, Vector3 center, float radius)
{
var halfSize = squareSize/ 2;
var localPos = transform.GetRelativePosition(center);
//粗略计算最大范围,减少计算量
if (localPos.sqrMagnitude > Mathf.Pow(radius + halfSize.x + halfSize.y, 2))
return false;
//圆的左右最近点
var closePoint = localPos;
var subValue = closePoint.x > 0 ? Mathf.Min(closePoint.x, radius) : Mathf.Max(closePoint.x, -radius);
closePoint.x -= subValue;
if (IsPointInSquareRange(halfSize, closePoint.x, closePoint.z))
return true;
//圆的上下最近点
closePoint = localPos;
subValue = closePoint.z > 0 ? Mathf.Min(closePoint.z, radius) : Mathf.Max(closePoint.z, -radius);
closePoint.z -= subValue;
if (IsPointInSquareRange(halfSize, closePoint.x, closePoint.z))
return true;
//检查矩形四角是否在圆内
var corners = GetSquareCorners(transform, squareSize.x, squareSize.y);
var len = corners.Length;
float maxDistanceSqr = radius * radius;
for (int i = 0; i < len; i++)
{
var point = corners[i];
if ((point - center).sqrMagnitude <= maxDistanceSqr)
return true;
}
return false;
}