85 lines
3.7 KiB
C#
85 lines
3.7 KiB
C#
|
|
using UnityEngine;
|
|||
|
|
|
|||
|
|
namespace SLSUtilities.General
|
|||
|
|
{
|
|||
|
|
public static class ColliderExtensions
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// 判断一个世界坐标点是否在 BoxCollider 内部(支持旋转和缩放)
|
|||
|
|
/// </summary>
|
|||
|
|
public static bool IsPointInside(this BoxCollider box, Vector3 worldPoint)
|
|||
|
|
{
|
|||
|
|
// 1. 将世界坐标转为该 Collider 的本地坐标(处理了旋转和位置)
|
|||
|
|
Vector3 localPoint = box.transform.InverseTransformPoint(worldPoint);
|
|||
|
|
|
|||
|
|
// 2. 减去中心偏移
|
|||
|
|
localPoint -= box.center;
|
|||
|
|
|
|||
|
|
// 3. 计算实际的半长宽高(考虑 LossyScale 缩放)
|
|||
|
|
// 注意:BoxCollider 的 size 已经包含了本地缩放,InverseTransformPoint 已经处理了缩放的影响
|
|||
|
|
Vector3 halfSize = box.size * 0.5f;
|
|||
|
|
|
|||
|
|
// 4. AABB 判定
|
|||
|
|
return Mathf.Abs(localPoint.x) <= halfSize.x &&
|
|||
|
|
Mathf.Abs(localPoint.y) <= halfSize.y &&
|
|||
|
|
Mathf.Abs(localPoint.z) <= halfSize.z;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 判断一个世界坐标点是否在 SphereCollider 内部
|
|||
|
|
/// </summary>
|
|||
|
|
public static bool IsPointInside(this SphereCollider sphere, Vector3 worldPoint)
|
|||
|
|
{
|
|||
|
|
// 考虑中心偏移的世界坐标中心点
|
|||
|
|
Vector3 center = sphere.transform.TransformPoint(sphere.center);
|
|||
|
|
|
|||
|
|
// 计算缩放后的实际半径(Unity 以三个轴中缩放最大的为准)
|
|||
|
|
Vector3 lossyScale = sphere.transform.lossyScale;
|
|||
|
|
float maxScale = Mathf.Max(lossyScale.x, Mathf.Max(lossyScale.y, lossyScale.z));
|
|||
|
|
float scaledRadius = sphere.radius * maxScale;
|
|||
|
|
|
|||
|
|
// 距离平方判定,性能最高
|
|||
|
|
float sqrDistance = (worldPoint - center).sqrMagnitude;
|
|||
|
|
return sqrDistance <= (scaledRadius * scaledRadius);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 判断一个世界坐标点是否在 CapsuleCollider 内部
|
|||
|
|
/// </summary>
|
|||
|
|
public static bool IsPointInside(this CapsuleCollider capsule, Vector3 worldPoint)
|
|||
|
|
{
|
|||
|
|
// 转为本地坐标
|
|||
|
|
Vector3 localPoint = capsule.transform.InverseTransformPoint(worldPoint);
|
|||
|
|
localPoint -= capsule.center;
|
|||
|
|
|
|||
|
|
// 计算胶囊体内部中心轴线的半高度(不含两头的半圆)
|
|||
|
|
float radius = capsule.radius;
|
|||
|
|
float halfHeight = Mathf.Max(0f, (capsule.height * 0.5f) - radius);
|
|||
|
|
|
|||
|
|
// 根据方向计算点到中心线段的最短距离
|
|||
|
|
// direction: 0 = X, 1 = Y, 2 = Z
|
|||
|
|
Vector3 closestPointOnAxis = Vector3.zero;
|
|||
|
|
switch (capsule.direction)
|
|||
|
|
{
|
|||
|
|
case 0: closestPointOnAxis.x = Mathf.Clamp(localPoint.x, -halfHeight, halfHeight); break;
|
|||
|
|
case 1: closestPointOnAxis.y = Mathf.Clamp(localPoint.y, -halfHeight, halfHeight); break;
|
|||
|
|
case 2: closestPointOnAxis.z = Mathf.Clamp(localPoint.z, -halfHeight, halfHeight); break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return (localPoint - closestPointOnAxis).sqrMagnitude <= (radius * radius);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 通用入口:根据 Collider 类型自动选择检测方法
|
|||
|
|
/// </summary>
|
|||
|
|
public static bool IsPointInside(this Collider collider, Vector3 worldPoint)
|
|||
|
|
{
|
|||
|
|
if (collider is BoxCollider box) return box.IsPointInside(worldPoint);
|
|||
|
|
if (collider is SphereCollider sphere) return sphere.IsPointInside(worldPoint);
|
|||
|
|
if (collider is CapsuleCollider capsule) return capsule.IsPointInside(worldPoint);
|
|||
|
|
|
|||
|
|
// 如果是 MeshCollider 等复杂形状,回退到 Bounds 粗略检测
|
|||
|
|
return collider.bounds.Contains(worldPoint);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|