当前位置: 首页 > news >正文

unity Physics.RaycastNonAlloc

Physics.RaycastNonAlloc 是 Unity 中用于 3D 物理射线检测的高性能方法,它是 Physics.Raycast 的非分配版本。

方法签名

public static int RaycastNonAlloc(Ray ray, RaycastHit[] results, float maxDistance = Mathf.Infinity, int layerMask = DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal)public static int RaycastNonAlloc(Vector3 origin, Vector3 direction, RaycastHit[] results, float maxDistance = Mathf.Infinity, int layerMask = DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal)

参数说明

  • ray/origin+direction: 射线或起点+方向
  • results: 预分配的 RaycastHit 数组
  • maxDistance: 射线最大距离
  • layerMask: 层级掩码
  • queryTriggerInteraction: 是否检测触发器

数组大小限制的重要性

问题说明

当可能击中的目标数量超过 results 数组的大小时,超出容量的击中目标将被忽略,这可能导致重要的碰撞检测被遗漏。

演示示例

using UnityEngine;public class RaycastLimitationDemo : MonoBehaviour
{[SerializeField] private int arraySize = 3;[SerializeField] private float rayDistance = 100f;private RaycastHit[] smallArray;private RaycastHit[] largeArray;void Start(){smallArray = new RaycastHit[arraySize];      // 小数组largeArray = new RaycastHit[50];             // 大数组}void Update(){if (Input.GetKeyDown(KeyCode.Space)){CompareRaycastResults();}}void CompareRaycastResults(){Vector3 origin = transform.position;Vector3 direction = transform.forward;// 使用小数组检测int smallHitCount = Physics.RaycastNonAlloc(origin, direction, smallArray, rayDistance);// 使用大数组检测int largeHitCount = Physics.RaycastNonAlloc(origin, direction, largeArray, rayDistance);Debug.Log($"小数组(大小:{arraySize})检测到: {smallHitCount} 个目标");Debug.Log($"大数组(大小:50)检测到: {largeHitCount} 个目标");if (largeHitCount > smallHitCount){Debug.LogWarning($"⚠️ 遗漏了 {largeHitCount - smallHitCount} 个目标!");// 显示被遗漏的目标for (int i = smallHitCount; i < largeHitCount; i++){Debug.LogWarning($"遗漏目标: {largeArray[i].collider.name} (距离: {largeArray[i].distance:F2})");}}}
}

实际问题场景

1. 子弹穿透系统问题

public class BulletPenetration : MonoBehaviour
{[SerializeField] private int maxPenetrations = 3;private RaycastHit[] hits;void Start(){// ❌ 错误:数组太小,可能遗漏目标hits = new RaycastHit[maxPenetrations];}public void FireBullet(Vector3 origin, Vector3 direction, float range){int hitCount = Physics.RaycastNonAlloc(origin, direction, hits, range);Debug.Log($"检测到 {hitCount} 个目标");// 问题:如果路径上有5个目标,但数组只能容纳3个// 最后2个目标不会被检测到,即使它们在射线路径上for (int i = 0; i < hitCount && i < maxPenetrations; i++){ProcessHit(hits[i]);}}void ProcessHit(RaycastHit hit){Debug.Log($"击中: {hit.collider.name}");}
}

2. 改进的解决方案

public class ImprovedBulletPenetration : MonoBehaviour
{[SerializeField] private int maxPenetrations = 3;[SerializeField] private int maxDetectionTargets = 20; // 增大检测容量private RaycastHit[] allHits;void Start(){// ✅ 正确:使用更大的数组确保不遗漏目标allHits = new RaycastHit[maxDetectionTargets];}public void FireBullet(Vector3 origin, Vector3 direction, float range){int totalHits = Physics.RaycastNonAlloc(origin, direction, allHits, range);Debug.Log($"路径上共检测到 {totalHits} 个目标");// 根据距离排序(RaycastNonAlloc 默认已按距离排序)int processedHits = 0;for (int i = 0; i < totalHits && processedHits < maxPenetrations; i++){if (CanPenetrate(allHits[i])){ProcessHit(allHits[i]);processedHits++;}else{// 遇到无法穿透的目标,停止处理ProcessHit(allHits[i]);break;}}// 显示未处理的目标(因为穿透限制)if (totalHits > processedHits){Debug.Log($"因穿透限制,忽略了后续 {totalHits - processedHits} 个目标");}}bool CanPenetrate(RaycastHit hit){// 检查材质或标签决定是否可穿透return hit.collider.CompareTag("Penetrable");}void ProcessHit(RaycastHit hit){Debug.Log($"处理击中: {hit.collider.name} (距离: {hit.distance:F2})");}
}

3. 动态数组大小管理

public class DynamicRaycastSystem : MonoBehaviour
{private RaycastHit[] raycastBuffer;private int currentBufferSize = 10;private const int MAX_BUFFER_SIZE = 100;void Start(){raycastBuffer = new RaycastHit[currentBufferSize];}public RaycastHit[] PerformRaycast(Vector3 origin, Vector3 direction, float distance){int attempts = 0;int hitCount;do{hitCount = Physics.RaycastNonAlloc(origin, direction, raycastBuffer, distance);// 如果数组已满,说明可能还有更多目标if (hitCount == raycastBuffer.Length && currentBufferSize < MAX_BUFFER_SIZE){// 扩大数组currentBufferSize = Mathf.Min(currentBufferSize * 2, MAX_BUFFER_SIZE);raycastBuffer = new RaycastHit[currentBufferSize];Debug.LogWarning($"扩大射线检测缓冲区至 {currentBufferSize}");attempts++;}else{break;}}while (attempts < 3); // 最多尝试3次扩展// 返回实际击中的结果RaycastHit[] results = new RaycastHit[hitCount];System.Array.Copy(raycastBuffer, results, hitCount);return results;}
}

最佳实践建议

1. 合理估算数组大小

public class RaycastBestPractices : MonoBehaviour
{// 根据场景复杂度设置缓冲区大小private RaycastHit[] hits;void Start(){// 分析你的场景:// - 最密集区域可能有多少个碰撞器?// - 射线最长距离内可能遇到多少目标?// - 加上安全余量int estimatedMaxTargets = AnalyzeSceneComplexity();int safetyBuffer = estimatedMaxTargets / 2;int bufferSize = estimatedMaxTargets + safetyBuffer;hits = new RaycastHit[bufferSize];Debug.Log($"射线检测缓冲区大小: {bufferSize}");}int AnalyzeSceneComplexity(){// 简单的场景复杂度分析Collider[] allColliders = FindObjectsOfType<Collider>();// 可以根据场景大小、碰撞器密度等因素计算return Mathf.Max(20, allColliders.Length / 10);}
}

2. 性能监控

public class RaycastPerformanceMonitor : MonoBehaviour
{private RaycastHit[] hits = new RaycastHit[50];private int maxHitsRecorded = 0;void Update(){if (Input.GetMouseButton(0)){PerformMonitoredRaycast();}}void PerformMonitoredRaycast(){Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);int hitCount = Physics.RaycastNonAlloc(ray, hits, 100f);// 记录最大击中数if (hitCount > maxHitsRecorded){maxHitsRecorded = hitCount;Debug.Log($"新的最大击中数记录: {maxHitsRecorded}");}// 检查是否接近数组限制if (hitCount >= hits.Length * 0.8f){Debug.LogWarning($"射线检测接近缓冲区限制!当前: {hitCount}/{hits.Length}");}}void OnGUI(){GUI.Label(new Rect(10, 10, 300, 20), $"最大击中记录: {maxHitsRecorded}");GUI.Label(new Rect(10, 30, 300, 20), $"缓冲区大小: {hits.Length}");}
}

总结

使用 Physics.RaycastNonAlloc 时,数组大小的选择至关重要:

  1. 过小的数组:会导致遗漏目标,可能影响游戏逻辑
  2. 过大的数组:浪费内存,但确保完整性
  3. 最佳实践:根据场景复杂度合理估算,加上安全余量
  4. 监控机制:在开发阶段监控实际使用情况,调整数组大小

记住:宁可数组稍大一些,也不要因为大小不足而遗漏重要的碰撞检测

http://www.dtcms.com/a/286511.html

相关文章:

  • 反序列化漏洞1-PHP序列化基础概念(0基础超详细)
  • 从 Spring Boot 2.x 到 Spring Boot 3.x:全面对比与快速上手指南
  • 高精度流体分配系统的设计与分析
  • 加速度计和气压计、激光互补滤波融合算法
  • 接口测试时如何上传文件(图片、安装包等)
  • 基于DeepSeek大模型实现Function Call功能
  • elementui-admin构建
  • 行为型设计模式:解释器模式
  • ES v.s Milvus v.s PG
  • 【unitrix】 6.8 加一运算(add_one.rs)
  • ubuntu源码安装ceres-solves
  • docker--Dockerfile
  • CherryStudio+playwright-mcp-server实现AI自动化
  • 20250718-1-Kubernetes 应用程序生命周期管理-应用部署、升级、弹性_笔记
  • C语言-一维数组,二维数组
  • 七彩喜康养平台:大数据时代,养老服务从智能走向智慧
  • 实习十三——传输层协议
  • iOS 数据持久化
  • iOS OC 图片压缩
  • 如何快速下载 MT4 交易平台
  • Linux学习之认识Linux的基本指令
  • LeetCodeHot100---螺旋矩阵---一题两解
  • Unity 常见数据结构分析与实战展示 C#
  • 基于单片机自行车自动防盗报警系统设计
  • VUE目录结构详解
  • NW983NW988美光固态闪存NW991NW992
  • 无符号乘法运算的硬件逻辑实现 ————取自《湖科大教书匠》
  • PAT 1012 The Best Rank
  • QML vscode语法高亮和颜色区分。
  • 【vLLM 学习】Encoder Decoder Multimodal