85 lines
3.7 KiB
C#
85 lines
3.7 KiB
C#
using UnityEngine;
|
||
|
||
namespace SLSFramework.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);
|
||
}
|
||
}
|
||
} |