Physics2D.GetRayIntersectionNonAlloc
是 Unity 中一个用于 2D 物理射线检测的高性能方法。它可以检测射线与 2D 碰撞器的交点,但不会分配新的内存(非分配版本)。
方法签名
public static int GetRayIntersectionNonAlloc(Ray ray, RaycastHit2D[] results, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers)
参数说明
- ray: 要投射的射线(Ray对象)
- results: 用于存储结果的 RaycastHit2D 数组(需要预先分配)
- distance: 射线的最大距离(可选,默认为无限远)
- layerMask: 层级掩码,指定要检测的层级(可选)
返回值
返回检测到的碰撞数量(int类型)
使用示例
using UnityEngine;
public class RaycastExample : MonoBehaviour
{
// 预分配结果数组,避免运行时分配内存
private RaycastHit2D[] raycastResults = new RaycastHit2D[10];
void Update()
{
// 创建从鼠标位置向右的射线
Vector3 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
mousePos.z = 0f;
Ray ray = new Ray(mousePos, Vector2.right);
// 执行射线检测
int hitCount = Physics2D.GetRayIntersectionNonAlloc(
ray,
raycastResults,
10f, // 最大距离
LayerMask.GetMask("Default", "Enemy") // 只检测特定层级
);
// 处理检测结果
for (int i = 0; i < hitCount; i++)
{
RaycastHit2D hit = raycastResults[i];
Debug.Log($"击中物体: {hit.collider.name}, 距离: {hit.distance}");
// 在击中点绘制标记
Debug.DrawLine(ray.origin, hit.point, Color.red, 0.1f);
}
// 绘制射线
Debug.DrawRay(ray.origin, ray.direction * 10f, Color.blue, 0.1f);
}
}
实际应用场景
1. 激光武器系统
public class LaserWeapon : MonoBehaviour
{
[SerializeField] private LineRenderer laserLine;
[SerializeField] private LayerMask targetLayers;
[SerializeField] private float maxRange = 50f;
private RaycastHit2D[] hits = new RaycastHit2D[5];
void Update()
{
FireLaser();
}
void FireLaser()
{
Ray ray = new Ray(transform.position, transform.right);
int hitCount = Physics2D.GetRayIntersectionNonAlloc(ray, hits, maxRange, targetLayers);
Vector3 endPoint = transform.position + transform.right * maxRange;
if (hitCount > 0)
{
// 使用最近的击中点
RaycastHit2D closestHit = hits[0];
for (int i = 1; i < hitCount; i++)
{
if (hits[i].distance < closestHit.distance)
closestHit = hits[i];
}
endPoint = closestHit.point;
// 对击中的目标造成伤害
if (closestHit.collider.TryGetComponent<IDamageable>(out var target))
{
target.TakeDamage(10f);
}
}
// 更新激光线渲染
laserLine.SetPosition(0, transform.position);
laserLine.SetPosition(1, endPoint);
}
}
2. 视线检测系统
public class VisionSystem : MonoBehaviour
{
[SerializeField] private LayerMask obstacleLayers;
[SerializeField] private LayerMask targetLayers;
private RaycastHit2D[] visionHits = new RaycastHit2D[10];
public bool CanSeeTarget(Transform target)
{
Vector2 direction = (target.position - transform.position).normalized;
float distance = Vector2.Distance(transform.position, target.position);
Ray ray = new Ray(transform.position, direction);
int hitCount = Physics2D.GetRayIntersectionNonAlloc(ray, visionHits, distance, obstacleLayers | targetLayers);
for (int i = 0; i < hitCount; i++)
{
// 如果先击中障碍物,则视线被阻挡
if (((1 << visionHits[i].collider.gameObject.layer) & obstacleLayers) != 0)
{
return false;
}
// 如果击中目标且没有障碍物阻挡
if (visionHits[i].collider.transform == target)
{
return true;
}
}
return false;
}
}
性能优势
- 零内存分配: 使用预分配的数组,避免 GC 压力
- 批量检测: 一次调用可以获得多个击中结果
- 高效率: 相比多次调用单次检测方法更高效
注意事项
- 数组大小: 确保 results 数组足够大以容纳所有可能的击中结果,如果需要检测的目标大于可容纳的数据,超过的部分将检测不到
- 数组重用: 重复使用同一个数组以最大化性能优势
- 结果排序: 返回的结果按距离从近到远排序
- 有效性检查: 始终检查返回的击中数量,避免访问无效数据
这个方法特别适合需要频繁进行射线检测的游戏系统,如射击游戏、视线检测等场景。