Files
Cielonos/Assets/Scripts/SLSFramework/General/ColliderExtensions.cs
2025-12-24 16:58:51 -05:00

85 lines
3.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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);
}
}
}