下面我将详细解释您提供的代码,重点讲解 Unity JobSystem 和 BurstCompiler 的使用原理和实现细节。
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
public abstract partial class Ships : Objects
{
// BurstCompile 属性标记此结构体可使用 Burst 编译器优化
[BurstCompile]
public struct ShipNavigationJob : IJob // 实现 IJob 接口表示这是一个并行任务
{
// 输入数据 - 从主线程传递到 Job
public float3 targetPosition; // 目标位置 (float3 是 Unity.Mathematics 的高性能向量类型)
public float3 selfPosition; // 飞船自身位置
public float3 enginePosition; // 引擎位置
public float3 bodyPosition; // 船体位置
public float stopRotateDistance; // 停止旋转的距离阈值
public float angularDrag; // 角阻力值
public float recordTorque; // 记录的扭矩值 (未使用)
// 输出数据 - 使用 NativeArray 安全地在 Job 和主线程间传递数据
public NativeArray<float> rotationDirection; // 旋转方向输出
public NativeArray<float> moveForce; // 移动力输出
public NativeArray<float> angularDragOutput; // 角阻力输出
// Job 的核心执行方法 - 在工作线程上运行
public void Execute()
{
// 1. 计算旋转方向
float3 targetDir = math.normalize(targetPosition - selfPosition); // 计算目标方向向量
float3 selfDir = math.normalize(bodyPosition - enginePosition); // 计算飞船当前朝向
// 使用叉乘计算旋转方向 (2D 空间中)
float3 cross = math.cross(selfDir, targetDir); // 叉乘结果表示旋转方向
float rotationSign = math.sign(cross.z); // 取 z 分量符号 (2D 旋转)
// 2. 计算移动力
float distance = math.distance(selfPosition, targetPosition); // 计算到目标的距离
float moveForceValue = math.clamp(distance * 0.5f, 0, 100); // 根据距离计算移动力并限制范围
// 3. 设置输出结果
rotationDirection[0] = rotationSign; // 存储旋转方向
moveForce[0] = moveForceValue; // 存储移动力
angularDragOutput[0] = angularDrag; // 直接传递角阻力值
}
}
// NativeArray 用于线程安全的数据共享
private NativeArray<float> rotationDirection; // 修正为 float 类型
private NativeArray<float> moveForce;
private NativeArray<float> angularDragOutput;
private JobHandle jobHandle; // 用于跟踪 Job 执行状态
private void Start()
{
// 创建持久化的 NativeArray (需要手动释放)
rotationDirection = new NativeArray<float>(1, Allocator.Persistent);
moveForce = new NativeArray<float>(1, Allocator.Persistent);
angularDragOutput = new NativeArray<float>(1, Allocator.Persistent);
}
private void OnDestroy()
{
// 释放 NativeArray 避免内存泄漏
rotationDirection.Dispose();
moveForce.Dispose();
angularDragOutput.Dispose();
}
private void FixedUpdate_ShipTowardsAndMoveWithParentTest()
{
// 检查执行条件
if (Objects_Order == null || !Engine(engineTrigger) || targetObject == null || Formation)
return;
// 创建并配置 Job
var job = new ShipNavigationJob
{
targetPosition = targetObject.transform.position,
selfPosition = transform.position,
enginePosition = engineTransform.position,
bodyPosition = bodyTransform.position,
stopRotateDistance = this.stopRotateDistance,
angularDrag = this.angularDrag,
recordTorque = this.recordTorque,
rotationDirection = rotationDirection, // 传递引用
moveForce = moveForce,
angularDragOutput = angularDragOutput
};
// 调度 Job 到工作线程池
jobHandle = job.Schedule();
// 等待 Job 完成 - 阻塞主线程直到计算完成
jobHandle.Complete();
// 从 Job 获取结果并应用到物理系统
rigid2D.angularDrag = angularDragOutput[0]; // 设置角阻力
RotateRigid(rotationDirection[0]); // 应用旋转
// 应用移动力
rigid2D.AddForce(transform.up * moveForce[0]);
pushForce = recordPushForce; // 更新其他状态
}
}
Unity JobSystem 核心概念详解
1. IJob 接口
IJob 是最简单的 Job 接口,用于定义可以在工作线程上执行的任务
必须实现 Execute() 方法,包含实际的计算逻辑
适合独立、无依赖的计算任务
2. NativeArray
线程安全容器:允许主线程和工作线程安全地共享数据
内存管理:
必须显式分配和释放内存
Allocator.Persistent:长期存在,需要手动释放
Allocator.TempJob:短期存在,自动释放(推荐)
数据传递:
输入数据:通过值传递
输出数据:通过 NativeArray 引用传递
3. JobHandle
跟踪 Job 执行状态
可用于依赖管理(当多个 Job 有执行顺序要求时)
Complete() 方法会阻塞主线程直到 Job 完成
4. 执行流程
主线程准备数据
创建 Job 实例并配置参数
调用 Schedule() 将 Job 加入工作队列
工作线程池中的线程执行 Execute()
主线程调用 Complete() 等待结果
主线程使用计算结果
BurstCompiler 优化原理
1. 作用
将 C# Job 代码编译为高度优化的本地机器码
利用 SIMD 指令集(单指令多数据)并行处理
避免 .NET 虚拟机的开销
2. 使用方式
添加 [BurstCompile] 特性到结构体
Burst 会自动检测并优化标记的代码
要求代码使用 Unity.Mathematics 而不是 System.Math
3. 性能优势
比传统 C# 快 2-10 倍
特别适合数学密集型计算(如向量运算、物理模拟)
减少 GC 压力,提高帧率稳定性
4. 限制
仅支持有限的数据类型和操作
不能访问托管对象(如 UnityEngine.Object)
不能使用 try/catch、虚方法等高级特性
最佳实践与改进建议
1. 内存分配优化
// 使用 TempJob 分配器提高性能
private void FixedUpdate()
{
// 每帧创建临时 NativeArray
var rotDir = new NativeArray<float>(1, Allocator.TempJob);
var mForce = new NativeArray<float>(1, Allocator.TempJob);
var angDrag = new NativeArray<float>(1, Allocator.TempJob);
// ... 调度 Job
jobHandle.Complete();
// 使用结果
rigid2D.angularDrag = angDrag[0];
// 无需手动释放,TempJob 分配器会在帧结束时自动回收
}
2. 依赖管理与并行执行
// 多个 Job 并行执行
JobHandle handle1 = job1.Schedule();
JobHandle handle2 = job2.Schedule(handle1); // 依赖 handle1
JobHandle handle3 = job3.Schedule();
// 等待所有 Job 完成
JobHandle.CombineDependencies(handle1, handle2, handle3).Complete();
3. 使用 IJobParallelFor 处理大量对象
[BurstCompile]
struct FleetNavigationJob : IJobParallelFor
{
[ReadOnly] public NativeArray<float3> targetPositions;
[ReadOnly] public NativeArray<float3> shipPositions;
public NativeArray<float3> moveForces;
public void Execute(int index)
{
// 为每个飞船独立计算
float3 dir = math.normalize(targetPositions[index] - shipPositions[index]);
moveForces[index] = dir * 10f;
}
}
// 调度
var job = new FleetNavigationJob { /* 初始化 */ };
job.Schedule(shipCount, 32).Complete(); // 32 表示每批处理的数量
4. 避免主线程阻塞
private JobHandle lateJobHandle;
void FixedUpdate()
{
// 调度但不立即等待
lateJobHandle = job.Schedule();
}
void LateUpdate()
{
// 在帧结束前等待结果
lateJobHandle.Complete();
// 使用结果
}
重要注意事项
线程安全:
Job 中不能访问任何 Unity 对象(Transform、Rigidbody 等)
只能使用值类型和 NativeContainer
数据竞争:
确保 NativeArray 在 Job 执行期间不被修改
使用 [ReadOnly] 属性标记只读数据
性能分析:
使用 Unity Profiler 的 "Deep Profile" 分析 Job 性能
监控 "Jobs" 面板查看工作线程利用率
Burst 兼容性:
避免在 Burst Job 中使用控制流(if/switch)中的分支
优先使用 math 函数而非自定义逻辑
内存泄漏:
始终在 OnDestroy 中释放 Persistent NativeArray
使用 Allocator.TempJob 可减少泄漏风险
通过合理使用 JobSystem 和 BurstCompiler,您可以将 CPU 密集型计算转移到工作线程,显著提升游戏性能,特别是在有大量飞船需要同时计算的场景中。
DeepSeek生成

1622

被折叠的 条评论
为什么被折叠?



