Worker专责AI行为树纯计算任务,主线程负责调度、渲染与指令执行;通过postMessage传递序列化状态与指令,用Transferable对象零拷贝传输数据,指令含时间戳与标识以防过期。

Worker线程本身不能直接操作DOM或调用游戏主循环中的渲染逻辑,但它非常适合承担AI行为树的**纯计算密集型任务**:比如状态评估、节点遍历、条件判断、动作优先级排序、指令生成等。关键在于“分离计算”与“协调输出”,而非让Worker控制游戏逻辑全流程。
行为树计算交给Worker,结果通过postMessage传回主线程
将整个行为树(如JSON结构)和当前AI实体的状态(位置、血量、视野内目标列表等)序列化后发送给Worker。Worker加载行为树解析器(可预编译为函数或使用轻量JS解释器),执行tick计算,输出一个明确的**指令对象**(例如{action: "attack", targetId: 5, priority: 0.92})。主线程收到后,在下一帧中执行该指令(移动、攻击、播放动画等)。
- Worker中不访问
document、canvas、requestAnimationFrame,只做数据处理 - 行为树节点(Selector、Sequence、Condition、Action)用纯函数实现,无副作用
- 避免频繁通信:可批量处理多个AI单位(如每100ms统一计算一批NPC),减少postMessage开销
使用Transferable对象提升大数据传输效率
若AI状态包含大量数值(如路径网格、感知向量、历史轨迹),用ArrayBuffer或TypedArray组织数据,通过postMessage(data, [buffer])零拷贝传递。Worker解析时直接视图操作,避免JSON序列化/反序列化的CPU和内存开销。
- 例如:把100个AI的2D位置+朝向打包成
Float32Array([x1,y1,rot1,x2,y2,rot2,...]) - Worker中用
new Float32Array(transferableBuffer)快速读取 - 主线程更新状态后,重用同一buffer并transfer出去,避免重复分配
指令需带时间戳与上下文标识,避免过期执行
Worker输出的指令必须包含timestamp(如Date.now())和aiId,主线程收到后检查是否仍在有效窗口内(例如延迟超过150ms则丢弃),并核对实体是否还存活。防止因计算耗时导致指令作用于已死亡或已转移的目标。
立即学习“前端免费学习笔记(深入)”;
- Worker在计算开始时记录
startTime = performance.now(),指令中附带该值 - 主线程对比
performance.now() - startTime > MAX_AI_LATENCY(如120ms)则跳过 - 指令对象结构建议:
{aiId: 7, action: "moveTo", params: {x: 124.3, y: 87.1}, ts: 1715234890123, version: 42}
主线程负责调度与融合,Worker不决策何时运行
Worker不自行启动或轮询。主线程按固定频率(如每秒60次)调用worker.postMessage({type: 'tick', state: currentState})。Worker响应后立即返回结果,不缓存中间状态。所有“决策时机”“实体生命周期”“指令执行反馈”均由主线程管理。
- Worker代码中无
setInterval、setTimeout或while(true) - 主线程可动态调整发送频率(如战斗时升频,潜行时降频)
- 若某帧未收到回复,可沿用上一帧指令(缓存lastCommand),或触发降级逻辑(如停在原地)
不复杂但容易忽略:Worker里的行为树不是“活”的对象,而是每次tick都重建上下文、执行一次快照计算。它像一个无状态的AI编译器,输入状态,输出动作——真正让AI“活起来”的,永远是主线程如何调度、融合、执行这些输出。



















