在 Unity 开发中,射线检测是一个非常重要的功能,广泛应用于碰撞检测、物理交互等场景。合理选择和使用射线检测方法,不仅可以提高检测的准确性,还能显著优化性能。
今天在学习中突然想到游戏中频繁的使用射线,GC 也会大量产生
如何进行优化呢 ?
射线检测方法概览
- Physics.Raycast
- Physics.RaycastAll
- Physics.RaycastNonAlloc
- Physics.SphereCast
- Physics.SphereCastAll
- Physics.SphereCastNonAlloc
- Physics.CapsuleCast
- Physics.CapsuleCastAll
- Physics.CapsuleCastNonAlloc
性能优化建议
- 选择合适的方法:根据具体需求选择最合适的射线检测方法。如果只需要检测第一个相交物体,使用
Physics.Raycast
而不是Physics.RaycastAll
。 - 使用 NonAlloc 版本:在高频率调用的检测场景中,优先使用
NonAlloc
版本以减少垃圾回收和内存分配。 - 优化检测频率:尽量减少不必要的射线检测调用,优化逻辑使射线检测只在需要时调用。
- 使用 LayerMask:通过
LayerMask
过滤不必要的碰撞体,提高检测效率。 - 合理设计检测区域:避免过大范围的检测区域,合理设计检测区域以减少无效的检测
Tip: 主体是就是在频繁使用射线检测时候,使用避免GC的方法等等去实现减少GC
射线检测方法及其使用场景
1. Physics.Raycast
- 描述:检测从起点沿指定方向的射线是否与任何碰撞体相交。
- 使用场景:用于单一目标的检测,如射击、视线检测、点击物体等
-
if (Physics.Raycast(origin, direction, out RaycastHit hitInfo, maxDistance, layerMask)) { // 处理碰撞 }
2. Physics.RaycastAll
- 描述:检测从起点沿指定方向的射线与所有相交的碰撞体。
- 使用场景:需要检测射线路径上所有物体,如激光光束穿过多个目标。
-
RaycastHit[] hits = Physics.RaycastAll(origin, direction, maxDistance, layerMask); foreach (RaycastHit hit in hits) { // 处理每个碰撞 }
3. Physics.RaycastNonAlloc
- 描述:检测从起点沿指定方向的射线与所有相交的碰撞体,结果存储在预先分配的数组中。
- 使用场景:频繁射线检测且希望避免垃圾回收的场景。
if (Physics.SphereCast(origin, radius, direction, out RaycastHit hitInfo, maxDistance, layerMask)) { // 处理碰撞 }
4. Physics.SphereCast
- 描述:在指定方向上发射一个球体,检测它是否与任何碰撞体相交。
- 使用场景:模拟具有体积的物体检测,如角色视野检测、环境扫描。
if (Physics.SphereCast(origin, radius, direction, out RaycastHit hitInfo, maxDistance, layerMask))
{
// 处理碰撞
}
5. Physics.SphereCastAll
- 描述:在指定方向上发射一个球体,检测它与所有相交的碰撞体。
- 使用场景:需要检测球体路径上所有相交物体的情况。
-
RaycastHit[] hits = Physics.SphereCastAll(origin, radius, direction, maxDistance, layerMask); foreach (RaycastHit hit in hits) { // 处理每个碰撞 }
6. Physics.SphereCastNonAlloc
- 描述:在指定方向上发射一个球体,检测它与所有相交的碰撞体,结果存储在预先分配的数组中。
- 使用场景:频繁球体检测且希望避免垃圾回收的场景。
-
RaycastHit[] results = new RaycastHit[10]; int hitCount = Physics.SphereCastNonAlloc(origin, radius, direction, results, maxDistance, layerMask); for (int i = 0; i < hitCount; i++) { RaycastHit hit = results[i]; // 处理每个碰撞 }
7. Physics.CapsuleCast
- 描述:在指定方向上发射一个胶囊体,检测它是否与任何碰撞体相交。
- 使用场景:检测具有高度和宽度的物体,如角色的碰撞检测、隧道穿越检测。
if (Physics.CapsuleCast(point1, point2, radius, direction, out RaycastHit hitInfo, maxDistance, layerMask))
{
// 处理碰撞
}
8. Physics.CapsuleCastAll
- 描述:在指定方向上发射一个胶囊体,检测它与所有相交的碰撞体。
- 使用场景:需要检测胶囊体路径上所有相交物体的情况。
RaycastHit[] hits = Physics.CapsuleCastAll(point1, point2, radius, direction, maxDistance, layerMask);
foreach (RaycastHit hit in hits)
{
// 处理每个碰撞
}
9. Physics.CapsuleCastNonAlloc
- 描述:在指定方向上发射一个胶囊体,检测它与所有相交的碰撞体,结果存储在预先分配的数组中。
- 使用场景:频繁胶囊体检测且希望避免垃圾回收的场景。
-
RaycastHit[] results = new RaycastHit[10]; int hitCount = Physics.CapsuleCastNonAlloc(point1, point2, radius, direction, results, maxDistance, layerMask); for (int i = 0; i < hitCount; i++) { RaycastHit hit = results[i]; // 处理每个碰撞 }
3.将自定义射线检测写成接口的形式,可以使代码更加灵活和可扩展
using UnityEngine;
public interface IRaycastManager
{
int Raycast(Vector3 origin, Vector3 direction, float maxDistance, LayerMask layerMask, out RaycastHit[] results);
}
using UnityEngine;
using System.Collections.Generic;
public class RaycastManager : MonoBehaviour, IRaycastManager
{
private List<RaycastHit[]> raycastHitPools;
private int poolSize = 10; // 池的大小
private int maxResults = 10; // 每个数组可以存储的最大结果数
void Start()
{
// 初始化对象池
raycastHitPools = new List<RaycastHit[]>(poolSize);
for (int i = 0; i < poolSize; i++)
{
raycastHitPools.Add(new RaycastHit[maxResults]);
}
}
public int Raycast(Vector3 origin, Vector3 direction, float maxDistance, LayerMask layerMask, out RaycastHit[] results)
{
// 从对象池中获取一个预分配的数组
results = GetRaycastHitArrayFromPool();
// 使用 NonAlloc 方法进行射线检测
int hitCount = Physics.RaycastNonAlloc(origin, direction, results, maxDistance, layerMask);
// 返回检测到的碰撞体数量
return hitCount;
}
private RaycastHit[] GetRaycastHitArrayFromPool()
{
// 简单实现:循环使用池中的数组
RaycastHit[] resultArray = raycastHitPools[0];
raycastHitPools.RemoveAt(0);
raycastHitPools.Add(resultArray);
return resultArray;
}
// 调试时绘制射线
void OnDrawGizmos()
{
Gizmos.color = Color.red;
Gizmos.DrawLine(transform.position, transform.position + transform.forward * 10);
}
}
使用自定义射线检测接口
using UnityEngine;
public class TestRaycast : MonoBehaviour
{
private IRaycastManager raycastManager;
public LayerMask layerMask;
public float maxDistance = 100f;
void Start()
{
// 获取 RaycastManager 组件
raycastManager = GetComponent<RaycastManager>();
}
void Update()
{
// 射线起点和方向
Vector3 origin = transform.position;
Vector3 direction = transform.forward;
// 使用自定义射线检测方法
int hitCount = raycastManager.Raycast(origin, direction, maxDistance, layerMask, out RaycastHit[] results);
// 处理检测结果
for (int i = 0; i < hitCount; i++)
{
RaycastHit hit = results[i];
Debug.Log("Hit: " + hit.collider.name);
}
}
}