Files
ichni_Official/Assets/Scripts/SLSUtilities/General/ColliderExtensions.cs

85 lines
3.7 KiB
C#
Raw Normal View History

2026-03-14 03:13:10 -04:00
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);
}
}
}