Unity 射线检测方法及其应用场景优化指南

在 Unity 开发中,射线检测是一个非常重要的功能,广泛应用于碰撞检测、物理交互等场景。合理选择和使用射线检测方法,不仅可以提高检测的准确性,还能显著优化性能。

今天在学习中突然想到游戏中频繁的使用射线,GC 也会大量产生 

如何进行优化呢 ? 

射线检测方法概览
  1. Physics.Raycast
  2. Physics.RaycastAll
  3. Physics.RaycastNonAlloc
  4. Physics.SphereCast
  5. Physics.SphereCastAll
  6. Physics.SphereCastNonAlloc
  7. Physics.CapsuleCast
  8. Physics.CapsuleCastAll
  9. Physics.CapsuleCastNonAlloc

性能优化建议

  1. 选择合适的方法:根据具体需求选择最合适的射线检测方法。如果只需要检测第一个相交物体,使用 Physics.Raycast 而不是 Physics.RaycastAll
  2. 使用 NonAlloc 版本在高频率调用的检测场景中,优先使用 NonAlloc 版本以减少垃圾回收和内存分配。
  3. 优化检测频率:尽量减少不必要的射线检测调用,优化逻辑使射线检测只在需要时调用。
  4. 使用 LayerMask:通过 LayerMask 过滤不必要的碰撞体,提高检测效率。
  5. 合理设计检测区域:避免过大范围的检测区域,合理设计检测区域以减少无效的检测

     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);
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值