Unity JobSystem 与 BurstCompiler 资料

下面我将详细解释您提供的代码,重点讲解 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生成

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值