如何在Unity Job中使用Raycast Commands实现射线检测?(C#)
解决Unity Job System多线程射线检测的问题
错误原因
你遇到的UnityException核心问题是:在Job线程中访问了RaycastHit.collider属性。这个属性属于Unity的Object类型,访问它会触发内部的FindObjectFromInstanceID调用,而该操作只能在主线程执行,因此报错。
修正方案
要在多线程中实现射线检测,需遵循以下规则:
- 用
RaycastCommand批量处理所有射线,避免循环内重复调度Job - Job中仅处理值类型数据,绝不直接访问Unity对象(如
Collider、GameObject) - 所有涉及Unity对象的操作(如
Debug.Log、访问对象属性)移至主线程执行
修正后的完整代码
using System.Collections.Generic; using Unity.Burst; using Unity.Collections; using Unity.Jobs; using UnityEngine; public class SimpleRaycast : MonoBehaviour { [SerializeField] private List<Agent> agentList; public class Agent { public Vector3 position; // 存储Agent位置,用于发射射线 } void Start() { agentList = new List<Agent>(); for (int i = 0; i < 5000; i++) { var agent = new Agent(); // 随机生成Agent位置,模拟场景分布 agent.position = new Vector3(Random.Range(-50f, 50f), 10f, Random.Range(-50f, 50f)); agentList.Add(agent); } } void Update() { int agentCount = agentList.Count; // 创建对应数量的Native数组,存储射线命令、命中结果和命中状态 NativeArray<RaycastCommand> raycastCommands = new NativeArray<RaycastCommand>(agentCount, Allocator.TempJob); NativeArray<RaycastHit> raycastHits = new NativeArray<RaycastHit>(agentCount, Allocator.TempJob); NativeArray<bool> hitFlags = new NativeArray<bool>(agentCount, Allocator.TempJob); // 为每个Agent生成射线命令 for (int i = 0; i < agentCount; i++) { Vector3 startPos = agentList[i].position; // 射线从Agent位置向下发射,长度20 raycastCommands[i] = new RaycastCommand(startPos, Vector3.down, 20f); } // 批量调度射线检测Job,64为批处理粒度,可根据性能调整 JobHandle raycastHandle = RaycastCommand.ScheduleBatch(raycastCommands, raycastHits, 64); // 调度结果处理Job,依赖射线检测完成 ProcessRaycastJob processJob = new ProcessRaycastJob { RaycastHits = raycastHits, HitFlags = hitFlags }; JobHandle processHandle = processJob.Schedule(raycastHandle); // 等待所有Job执行完毕 processHandle.Complete(); // 主线程处理结果:访问Unity对象、打印日志 for (int i = 0; i < agentCount; i++) { if (hitFlags[i]) { Debug.Log($"Agent {i} 命中: {raycastHits[i].collider.name}"); } } // 释放Native资源,避免内存泄漏 raycastCommands.Dispose(); raycastHits.Dispose(); hitFlags.Dispose(); } } [BurstCompile] public struct ProcessRaycastJob : IJobParallelFor { [ReadOnly] public NativeArray<RaycastHit> RaycastHits; public NativeArray<bool> HitFlags; public void Execute(int index) { // 仅判断是否命中,不访问Unity对象,完全在Job线程执行 HitFlags[index] = RaycastHits[index].collider != null; } }
核心注意事项
- 批量处理射线:
RaycastCommand.ScheduleBatch比循环单独调度效率高得多,适合大量射线场景 - 值类型传递:用
NativeArray<bool>存储命中状态,避免Job线程触发Unity对象访问 - 资源管理:所有
NativeArray必须调用Dispose()释放,否则会导致内存泄漏 - Burst优化:给Job加上
[BurstCompile]标签,大幅提升多线程执行速度
内容的提问来源于stack exchange,提问作者MathMith
相关产品推荐
相关产品推荐

