自定义任务与装饰器
内置的移动与等待任务不够用时,就要写自定义节点。这一节给出"写一个自定义攻击任务"的完整套路。
任务节点的生命周期
自定义任务(蓝图的 BTTask 或 C++ 的 UBTTaskNode)有几个关键回调:
| 阶段 | 回调 | 做什么 |
|---|---|---|
| 进入 | ExecuteTask | 开始执行:播放动画 / 启动移动等,返回 InProgress(进行中) |
| 进行中 | TickTask | 每帧检查:动画播完了吗 |
| 收尾 | OnTaskFinished | 清理:停止动画、归位黑板 |
| 中断 | OnTaskFinished(参数为 Interrupted) | 被高优先级分支打断时的收尾 |
最常见的错误:在 ExecuteTask 里执行一个"要花时间"的动作,但不返回 InProgress,直接返回 Success——任务瞬间完成,AI 表现为"抽搐"。正确做法是:
- ExecuteTask 里启动动作,返回 InProgress;
- 在 TickTask 或事件回调里判断完成条件;
- 完成时用 FinishLatentTask 通知行为树。
装饰器的两种执行模式
| 模式 | 触发 | 适合 |
|---|---|---|
| 条件判断(PerformConditionCheck) | 行为树每次评估到这个节点时 | 简单条件:"有目标吗" |
| 观察者(Observer) | 装饰器值变化时通知中断 | 需要随时打断当前行为的条件 |
观察者.abort 机制是行为树"反应性"的核心:你的抽象条件(如"目标可见")必须实现为带观察通知的版本,否则 AI 会"走到一半才发现目标不见了"。
一个实战任务:带节奏的攻击
ExecuteTask:
检查目标有效(黑板里的 TargetEnemy)
设置攻击标志(黑板 IsAttacking = true)
启动攻击动画(带蒙太奇事件)
返回 InProgress
TickTask / 动画完成事件:
IsAttacking = false
FinishLatentTask(Success)
OnTaskFinished:
保证 IsAttacking 已复位(中断情况下也要清理)
写自定义节点时养成一个习惯:OnTaskFinished 里永远做一次状态复位。中断发生时 ExecuteTask 的"收尾半截子",收拾干净才能保证下次播放正确。
