自定义任务与装饰器

内置的移动与等待任务不够用时,就要写自定义节点。这一节给出"写一个自定义攻击任务"的完整套路。

任务节点的生命周期

自定义任务(蓝图的 BTTask 或 C++ 的 UBTTaskNode)有几个关键回调:

阶段 回调 做什么
进入 ExecuteTask 开始执行:播放动画 / 启动移动等,返回 InProgress(进行中)
进行中 TickTask 每帧检查:动画播完了吗
收尾 OnTaskFinished 清理:停止动画、归位黑板
中断 OnTaskFinished(参数为 Interrupted) 被高优先级分支打断时的收尾

最常见的错误:在 ExecuteTask 里执行一个"要花时间"的动作,但不返回 InProgress,直接返回 Success——任务瞬间完成,AI 表现为"抽搐"。正确做法是:

  1. ExecuteTask 里启动动作,返回 InProgress;
  2. 在 TickTask 或事件回调里判断完成条件;
  3. 完成时用 FinishLatentTask 通知行为树。

装饰器的两种执行模式

模式 触发 适合
条件判断(PerformConditionCheck) 行为树每次评估到这个节点时 简单条件:"有目标吗"
观察者(Observer) 装饰器值变化时通知中断 需要随时打断当前行为的条件

观察者.abort 机制是行为树"反应性"的核心:你的抽象条件(如"目标可见")必须实现为带观察通知的版本,否则 AI 会"走到一半才发现目标不见了"。

一个实战任务:带节奏的攻击

ExecuteTask:
  检查目标有效(黑板里的 TargetEnemy)
  设置攻击标志(黑板 IsAttacking = true)
  启动攻击动画(带蒙太奇事件)
  返回 InProgress
TickTask / 动画完成事件:
  IsAttacking = false
  FinishLatentTask(Success)
OnTaskFinished:
  保证 IsAttacking 已复位(中断情况下也要清理)

写自定义节点时养成一个习惯:OnTaskFinished 里永远做一次状态复位。中断发生时 ExecuteTask 的"收尾半截子",收拾干净才能保证下次播放正确。