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.pswp.cn/diannao/91861.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/91861.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据库(five day finally)——物物而不物于物,念念而不念于念。(数据库到此结束!祝世间美好与各位不期而遇,善意常伴汝身!)

1.子查询&#xff08;1&#xff09;where 子查询①多行单列配合in和not in操作&#xff08;类似于数据范围查询&#xff09;例&#xff1a;显示工资与各个经理相同的雇员信息&#xff08;包含经理本身&#xff09;。select * from empwhere sal(select sal from emp where jobM…

【甲烷数据集】Sentinel-5P 卫星获取的全球甲烷数据集-TROPOMI L2 CH₄

目录 数据概述 传感器 & 卫星信息 监测目标:甲烷(CH₄) 数据产品内容 空间与时间覆盖 云筛选与协同观测 技术文档资源 数据下载 Python 代码绘制 CH4 数据 参考 数据概述 Sentinel-5 Precursor Level 2 Methane (TROPOMI L2 CH₄) 数据集是由欧洲哥白尼计划的 Sentinel…

【数据结构】单链表练习(有环)

1.判断是否是环形链表 141. 环形链表 - 力扣&#xff08;LeetCode&#xff09; bool hasCycle(struct ListNode *head) {struct ListNode *fast,*slow;fastslowhead;while(fast&&fast->next){fastfast->next->next;slowslow->next;if(fastslow)return tr…

VR 污水厂初体验:颠覆传统认知​

第一次戴上 VR 设备走进 VR 污水厂时&#xff0c;那种震撼的感觉至今难以忘怀。仿佛一瞬间&#xff0c;我被传送到了一个全新的世界&#xff0c;平日里只能在图纸或实地看到的污水厂&#xff0c;此刻就立体地呈现在眼前。脚下是纵横交错的管道&#xff0c;头顶巨大的处理设备有…

父类 div 自适应高度 子类如何撑满其高度

使用绝对定位 如果你想要子元素完全撑满父元素的高度&#xff0c;可以使用绝对定位。这种方法适用于当子元素需要完全覆盖父元素时。<div class"parent"><div class"child"><!-- 子类内容 --></div> </div>.parent {positio…

从0开始学习R语言--Day51--PH检验

在用cox回归做分析时&#xff0c;我们一般会得出各种变量在结局的风险影响&#xff08;HR大于1&#xff0c;就代表变量值增大&#xff0c;对应结局影响的风险就随之增大&#xff09;&#xff0c;但是这里有个坏处是&#xff0c;cox回归得到的是瞬时风险值&#xff0c;我们最多得…

Docker 网络原理

Linux 常见网络虚拟化 虚拟网卡:tun/tap虚拟网卡&#xff08;又称虚拟网络适配器&#xff09;&#xff0c;即用软件模拟网络环境&#xff0c;模拟网络适配器。在计算机网络中&#xff0c;tun 与 tap 是操作系统内核中的虚拟网络设备。不同于普通靠硬件网络适配器实现的设备&…

【通识】PCB文件

1. PCB文件的导入 在PORTEL99 PCB编辑器的文件菜单中选择导入先前绘制的CAD文件。导入成功后&#xff0c;编辑器将显示出元件封装的基本图形&#xff0c;为后续操作奠定基础。将需要抄板的PCB放置于扫描仪中随后启动扫描仪&#xff0c;之后启动AUTO CAD软件&#xff0c;之后插入…

分布式弹性故障处理框架——Polly(1)

1 前言之服务雪崩 在我们实施微服务之后&#xff0c;服务间的调用变得异常频繁&#xff0c;多个服务之前可能存在互相依赖的关系&#xff0c;当某个服务出现故障或者是因为服务间的网络出现故障&#xff0c;导致服务调用的失败&#xff0c;进而影响到某个业务服务处理失败&…

【机器学习深度学习】大模型推理速度与私有化部署的价值分析

目录 前言 一、主流推理框架速度对比 二、为什么 HuggingFace 框架更适合微调验证&#xff1f; 三、大模型私有化部署的必要性分析 ✅ 私有化部署的主要动因 1. 数据隐私与业务安全 2. 可控性与性能保障 ❌ 哪些情况不建议私有部署&#xff1f; 四、总结与选型建议 &…

elementui-admin构建

1、vue-element-admin vue-element-admin是基于element-ui 的一套后台管理系统集成方案。 功能&#xff1a;介绍 | vue-element-adminA magical vue adminhttps://panjiachen.github.io/vue-element-admin-site/zh/guide/# GitHub地址&#xff1a;https://github.com/PanJia…

深入排查:编译环境(JDK)与运行环境(JRE/JDK)不一致时的常见 Java 错误及解决方案

深入排查&#xff1a;编译环境&#xff08;JDK&#xff09;与运行环境&#xff08;JRE/JDK&#xff09;不一致时的常见 Java 错误及解决方案 在后端 Java 项目中&#xff0c;编译环境&#xff08;JDK&#xff09; 与 运行环境&#xff08;JRE/JDK&#xff09; 版本不一致&…

[JS逆向] 微信小程序逆向工程实战

博客配套代码与工具发布于github&#xff1a;微信小程序 &#xff08;欢迎顺手Star一下⭐&#xff09; 相关爬虫专栏&#xff1a;JS逆向爬虫实战 爬虫知识点合集 爬虫实战案例 逆向知识点合集 前言&#xff1a; 微信小程序对于很多尝试JS逆向的人群来说&#xff0c;都是一个…

基于5G系统的打孔LDPC编码和均匀量化NMS译码算法matlab性能仿真

目录 1.引言 2.算法仿真效果演示 3.数据集格式或算法参数简介 4.算法涉及理论知识概要 4.1打孔技术 4.2 均匀量化NMS译码 5.参考文献 6.完整算法代码文件获得 1.引言 在5G通信系统中&#xff0c;信道编码技术是保障高速率、高可靠性数据传输的核心支撑&#xff0c;而低…

基于Java标准库读取CSV实现天地图POI分类快速导入PostGIS数据库实战

目录 前言 一、天地图POI分类简介 1、数据表格 2、分类结构 二、从CSV导入到PG数据库 1、CSV解析流程 2、数据转换及入库 3、入库成果及检索 三、总结 前言 在之前的博客中&#xff0c;曾经对高德地图和百度地图的POI分类以及使用PostGIS数据库来进行管理的模式进行了详…

人-AI交互中的信息论不同于传统的信息论,其信息的增量≠不确定性的减量

在人机交互&#xff08;Human-AI Interaction, HAI&#xff09;领域&#xff0c;信息论的应用确实与传统的信息论有所不同。这种差异主要源于人机交互HAI中信息的复杂性、动态性以及人类认知的特点。1. 传统信息论的核心概念传统信息论由克劳德香农&#xff08;Claude Shannon&…

K8s 通过 Scheduler Extender 实现自定义调度逻辑

1. 为什么需要自定义调度逻辑 什么是所谓的调度? 所谓调度就是指给 Pod 对象的 spec.nodeName 赋值 待调度对象则是所有 spec.nodeName 为空的 Pod 调度过程则是从集群现有的 Node 中为当前 Pod 选择一个最合适的 实际上 Pod 上还有一个平时比较少关注的属性&#xff1a;…

7.19 换根dp | vpp |滑窗

lcr147.最小栈通过两个栈 维护实现class MinStack { public:stack<int> A, B;MinStack() {}void push(int x) {A.push(x);if(B.empty() || B.top() > x)B.push(x);}void pop() {if(A.top() B.top())B.pop();A.pop();}int top() {return A.top();}int getMin() {retur…

以太坊的心脏与大脑:详解执行客户端(EL)与共识客户端(CL)

好的&#xff0c;各位技术同道&#xff0c;欢迎再次光临我的博客。在上一篇文章中&#xff0c;我们聊了如何搭建一个以太坊测试节点&#xff0c;并提到了节点需要同时运行“执行客户端”和“共识客户端”。很多朋友对此表示了浓厚兴趣&#xff0c;想深入了解这两者究竟是什么&a…

Debian-10,用glibc二进制预编译包,安装Mysql-5.7.44 笔记250716

Debian-10,用glibc二进制预编译包,安装Mysql-5.7.44 笔记250716 &#x1f4e6; 一步脚本 #!/bin/bash### 安装依赖 apt install -y libaio1 libnuma1 libncurses5### 下载MySQL-5.7.44 的 glib二进制包: mysql-5.7.44-linux-glibc2.12-x86_64.tar.gz ,(如果不存在) mkdir…