# Behavior Designer Pro — 行为树节点编写完全指南 > 版本: Behavior Designer Pro (基于 DOTS 混合架构) > 适用项目: Cielonos (3D 第三人称动作游戏) --- ## 1. 架构总览 Behavior Designer Pro 采用 **DOTS 混合架构**:行为树的**遍历**始终由 ECS (Entity Component System) 驱动,但节点的**逻辑**可以选择 GameObject 工作流或纯 ECS 工作流。 ### 1.1 两种工作流对比 | 维度 | GameObject 工作流 (推荐) | Entity (ECS) 工作流 | |------|------------------------|-------------------| | 基类 | `Action`, `Conditional`, `ActionNode`, `ConditionalNode`, `DecoratorNode`, `CompositeNode` | `ECSActionTask` 等 | | 编写难度 | 低,类似 MonoBehaviour | 高,需要定义 `IBufferElementData`, `ISystem`, `Flag` | | 是否可引用对象 | ✅ 可以引用 `GameObject`, `Component` | ❌ 只能传值类型 | | 是否支持 SharedVariable | ✅ | ❌ | | 是否支持 Burst/Job | ❌ | ✅ | | 适用场景 | < 50 个 AI Agent | 成千上万个 Agent | **本项目 (Cielonos) 统一使用 GameObject 工作流。** ### 1.2 核心命名空间 ```csharp using Opsive.BehaviorDesigner.Runtime; // BehaviorTree 组件 using Opsive.BehaviorDesigner.Runtime.Tasks; // TaskStatus, Task 基类 using Opsive.BehaviorDesigner.Runtime.Tasks.Actions; // Action, ActionNode using Opsive.BehaviorDesigner.Runtime.Tasks.Conditionals; // Conditional, ConditionalNode using Opsive.BehaviorDesigner.Runtime.Tasks.Composites; // CompositeNode using Opsive.BehaviorDesigner.Runtime.Tasks.Decorators; // DecoratorNode using Opsive.BehaviorDesigner.Runtime.Tasks.Events; // EventNode (OnReceivedEvent等) using Opsive.GraphDesigner.Runtime; // NodeIcon, Description 等特性 using Opsive.GraphDesigner.Runtime.Variables; // SharedVariable using Opsive.Shared.Utility; // Category, Description, MemberVisibility using Opsive.Shared.Events; // EventHandler (事件系统) ``` --- ## 2. TaskStatus 枚举 ```csharp public enum TaskStatus : byte { Inactive, // 未激活 Queued, // 下一帧将执行 Running, // 正在执行(持续多帧) Success, // 执行成功 Failure, // 执行失败 } ``` **核心规则:** - **Action 节点**: 可以返回 `Running`(多帧持续), `Success`, `Failure` - **Conditional 节点**: 应当仅返回 `Success` 或 `Failure`,不应返回 `Running`(单帧内完成判定) --- ## 3. Task 基类生命周期 `Task` 是所有 GameObject 工作流节点的终极基类,其生命周期方法如下: ``` BehaviorTree 初始化 └─ Initialize() [internal, 自动调用] ├─ 缓存 m_GameObject, m_Transform, m_BehaviorTree ├─ 注册物理回调(如果子类启用了对应的 Receive*Callback) └─ OnAwake() ← 【初始化:缓存组件引用,仅调用一次】 BehaviorTree 启动 └─ OnBehaviorTreeStarted() ← 【树启动时回调】 每次节点被激活 └─ OnStart() ← 【节点开始执行,每次激活都会调用】 └─ OnUpdate() (每帧) ← 【核心逻辑,返回 TaskStatus】 └─ OnEnd() ← 【节点执行结束(Success/Failure 后)】 BehaviorTree 停止/暂停 └─ OnBehaviorTreeStopped(bool paused) BehaviorTree 销毁 └─ OnDestroy() ← 【清理,取消事件注册】 ``` ### 3.1 关键属性/字段 | 成员 | 类型 | 说明 | |------|------|------| | `m_GameObject` / `gameObject` | `GameObject` | 行为树挂载的 GameObject | | `m_Transform` / `transform` | `Transform` | 行为树的 Transform | | `m_BehaviorTree` | `BehaviorTree` | 行为树组件引用 | | `m_RuntimeIndex` | `ushort` | 节点运行时索引 | --- ## 4. 四种节点类型详解 ### 4.1 Action (行为节点) **用途**: 改变游戏状态(播放动画、移动、设置变量等)。 **两种基类选择**: - `Action` — **可堆叠** (Stacked):多个 Action 可放在同一个 StackedAction 节点中 - `ActionNode` — **不可堆叠** (Independent):独立节点 **编写模板(最简)**: ```csharp using Opsive.BehaviorDesigner.Runtime.Tasks; using Opsive.BehaviorDesigner.Runtime.Tasks.Actions; using Opsive.GraphDesigner.Runtime; using Opsive.Shared.Utility; using UnityEngine; [Description("描述此节点的功能")] [NodeIcon("Assets/路径/图标.png")] [Category("Cielonos")] public class MyAction : Action { [Tooltip("参数说明")] [SerializeField] float m_Speed = 5f; public override void OnAwake() { // 缓存组件 (仅一次) } public override void OnStart() { // 每次节点激活时的初始化 } public override TaskStatus OnUpdate() { // 核心逻辑 // 返回 Running = 继续执行, Success = 完成, Failure = 失败 return TaskStatus.Success; } public override void OnEnd() { // 节点结束时的清理 } public override void Reset() { // 编辑器重置默认值 m_Speed = 5f; } } ``` ### 4.2 Conditional (条件节点) **用途**: 检查游戏状态(距离、血量、事件等),不改变游戏状态。 **两种基类选择**: - `Conditional` — **可堆叠** + **支持 Conditional Abort 重评估** - `ConditionalNode` — **不可堆叠** (Independent) **关键方法**: `OnReevaluateUpdate()` — 在 Conditional Abort 重评估阶段调用,默认实现会调用 `OnUpdate()`。 **编写模板**: ```csharp using Opsive.BehaviorDesigner.Runtime.Tasks; using Opsive.BehaviorDesigner.Runtime.Tasks.Conditionals; using Opsive.Shared.Utility; using UnityEngine; [Category("Cielonos")] [Description("检查某个条件")] public class MyConditional : Conditional { public override void OnAwake() { // 缓存组件 } public override TaskStatus OnUpdate() { // 检查条件:Success = 条件满足, Failure = 不满足 return someCondition ? TaskStatus.Success : TaskStatus.Failure; } // 可选:Conditional Abort 重评估时走不同逻辑 public override TaskStatus OnReevaluateUpdate() { return OnUpdate(); // 默认行为 } } ``` ### 4.3 Composite (组合节点) **常用内置 Composite**: | 节点 | 行为 | |------|------| | `Sequence` | 顺序执行子节点,全部 Success 才返回 Success,任一 Failure 立即返回 Failure(AND 逻辑) | | `Selector` | 顺序执行子节点,任一 Success 立即返回 Success,全部 Failure 才返回 Failure(OR 逻辑) | | `Parallel` | 并行执行所有子节点 | | `ParallelSelector` | 并行执行,任一 Success 立即停止 | | `RandomSelector` | 随机顺序的 Selector | | `RandomSequence` | 随机顺序的 Sequence | | `PrioritySelector` | 按优先级排序的 Selector | | `UtilitySelector` | 效用评分选择器 | **自定义 Composite**: 继承 `CompositeNode`,不可堆叠。 ### 4.4 Decorator (装饰节点) **用途**: 修改子节点的返回状态或控制子节点的执行方式。只能有一个子节点。 **常用内置 Decorator**: | 节点 | 行为 | |------|------| | `Inverter` | 反转子节点结果 (Success↔Failure) | | `Repeater` | 重复执行子节点指定次数或无限次 | | `ReturnSuccess` | 无论子节点结果如何,始终返回 Success | | `ReturnFailure` | 无论子节点结果如何,始终返回 Failure | | `UntilSuccess` | 重复执行直到子节点返回 Success | | `UntilFailure` | 重复执行直到子节点返回 Failure | | `Cooldown` | 子节点执行后进入冷却 | | `ConditionalEvaluator` | 条件评估装饰器 | | `Iterator` | 遍历列表 | **自定义 Decorator**: 继承 `DecoratorNode`,不可堆叠。 --- ## 5. Conditional Aborts (条件打断) **核心机制**: 在行为树运行时,Conditional 节点可以被持续重评估。当条件状态变化时(Success→Failure 或 Failure→Success),触发打断。 ### 5.1 四种打断类型 (`ConditionalAbortType`) ```csharp public enum ConditionalAbortType : byte { None, // 不打断 LowerPriority, // 打断右侧(低优先级)分支 Self, // 打断当前分支内的任务 Both // LowerPriority + Self } ``` 设置在 **Composite 节点**(Sequence/Selector)上。 ### 5.2 经典设计模式 ``` Selector (根) ├── Sequence [AbortType=LowerPriority] ← 最高优先级 │ ├── HasTakenDamage (Conditional) │ └── ReactToDamage (Action) ├── Sequence [AbortType=LowerPriority] │ ├── CanSeeObject (Conditional) │ └── ChaseAndAttack (子树) ├── Sequence [AbortType=LowerPriority] │ ├── WithinDistance (Conditional) │ └── Investigate (Action) └── Patrol (Action) ← 最低优先级(兜底行为) ``` ### 5.3 OnReevaluateUpdate 注意事项 - 默认 `Conditional.OnReevaluateUpdate()` 调用 `OnUpdate()` - 如果需要在重评估时走特殊逻辑(如避免消费事件),应覆写此方法 - 项目中 `HasReceivedContextEvent` 就是一个好例子:`OnReevaluateUpdate()` 只检查不消费 --- ## 6. SharedVariable (共享变量) SharedVariable 是行为树节点间共享数据的机制。 ### 6.1 基本用法 ```csharp using Opsive.GraphDesigner.Runtime.Variables; // 声明 [SerializeField] SharedVariable m_Speed = 5f; // 带默认值 [SerializeField] SharedVariable m_Target; // 引用类型 [SerializeField] SharedVariable m_Position; [SerializeField] SharedVariable m_EventName; [SerializeField] SharedVariable m_IsEnabled; // 读取 float speed = m_Speed.Value; GameObject target = m_Target.Value; // 设置 m_Speed.Value = 10f; // 无类型 SharedVariable (用于泛型存储) [RequireShared] [SerializeField] SharedVariable m_StoredValue1; // 设置值 m_StoredValue1.SetValue(someObject); // 检查是否已共享 if (m_StoredValue1 != null && m_StoredValue1.IsShared) { ... } // 值变化回调 m_Speed.OnValueChange += OnSpeedChanged; ``` ### 6.2 通过 BehaviorTree 组件访问 ```csharp // 获取变量 var variable = m_BehaviorTree.GetVariable("VariableName"); // 设置变量值 m_BehaviorTree.SetVariableValue("VariableName", newValue); ``` --- ## 7. 事件系统 ### 7.1 Opsive.Shared.Events.EventHandler BD Pro 内置事件系统,用于节点间和外部脚本通信。 ```csharp // 发送事件 (到特定 BehaviorTree) EventHandler.ExecuteEvent(behaviorTree, "EventName"); EventHandler.ExecuteEvent(behaviorTree, "EventName", arg1); EventHandler.ExecuteEvent(behaviorTree, "EventName", arg1, arg2); // 注册事件 EventHandler.RegisterEvent(behaviorTree, "EventName", OnEventReceived); EventHandler.RegisterEvent(behaviorTree, "EventName", OnEventReceived); // 注销事件 EventHandler.UnregisterEvent(behaviorTree, "EventName", OnEventReceived); // 全局事件 EventHandler.RegisterEvent("GlobalEventName", OnGlobalEvent); EventHandler.ExecuteEvent("GlobalEventName"); ``` ### 7.2 内置事件节点 - **`HasReceivedEvent`** (Conditional): 当收到指定事件时返回 Success(支持 Conditional Abort) - **`OnReceivedEvent`** (EventNode): 收到事件时启动一个独立分支(类似中断) - **`SendEvent`** (Action): 向自身或其他 BehaviorTree 发送事件 ### 7.3 自定义事件系统 (Cielonos) 本项目使用 `BehaviorSubcontroller.EventMemory` 实现了带**有效窗口期**的上下文事件系统: ```csharp // HasReceivedContextEvent 的核心逻辑: // 1. 通过 behaviorSc.EventMemory 读取事件 // 2. 用 validWindow (秒) 判断事件是否在有效期内 // 3. OnUpdate 中消费事件 (Remove) // 4. OnReevaluateUpdate 中只检查不消费 ``` --- ## 8. 常用特性 (Attributes) ```csharp // 节点分类(在创建节点菜单中的路径) [Category("Cielonos")] [Category("Cielonos/Events")] [Category("Movement Pack")] // 节点描述(选中节点后右下角显示) [Description("此节点的功能描述")] // 节点图标(自定义 PNG 或 GUID) [NodeIcon("Assets/Sprites/Icon/Play.png")] [NodeIcon("GUID_Light", "GUID_Dark")] // 明暗两套图标 // Tooltip(Inspector 中悬浮提示) [Tooltip("参数说明")] // 隐藏在过滤窗口中 [HideInFilterWindow] // 强制要求 SharedVariable 必须为 Shared 模式 [RequireShared] // 隐藏字段不显示在 Inspector [HideInInspector] // 允许同类型多实例 [AllowMultipleTypes] ``` --- ## 9. 本项目自定义基类 ### 9.1 AutomataActionBase (Action 基类) ```csharp [Category("Cielonos")] public abstract class AutomataActionBase : Action { protected Automata self; protected BehaviorSubcontroller behaviorSc; protected BehaviorTree mainBehaviorTree; public override void OnAwake() { self = gameObject.GetComponent(); behaviorSc = self.behaviorSc; mainBehaviorTree = behaviorSc.mainBehaviorTree; } } ``` ### 9.2 AutomataConditionalBase (Conditional 基类) ```csharp [Category("Cielonos")] public abstract class AutomataConditionalBase : Conditional { protected Automata self; protected BehaviorSubcontroller behaviorSc; protected BehaviorTree mainBehaviorTree; public override void OnAwake() { self = gameObject.GetComponent(); behaviorSc = self.behaviorSc; mainBehaviorTree = behaviorSc.mainBehaviorTree; } } ``` **使用这些基类后,子类可直接访问 `self`, `behaviorSc`, `mainBehaviorTree`。** --- ## 10. 本项目已有自定义节点清单 ### 10.1 Action 节点 | 节点名 | 基类 | 功能 | |--------|------|------| | `PlayFuncAnim` | `AutomataActionBase` | 播放功能动画,检测打断,支持循环动画/转向目标/根吸附 | | `SetBreakthroughResistance` | `AutomataActionBase` | 设置可打断状态(白/橙/红光) | | `SetRootMotionMultipliers` | `Action` | 设置根运动各方向的倍率,支持自动计算 | | `GetPlayer` | `AutomataActionBase` | 获取玩家对象到 SharedVariable | | `GetGlobalVariable` | `AutomataActionBase` | 获取全局属性值 | | `ResetTimer` | `AutomataActionBase` | 重置计时器 | ### 10.2 Conditional 节点 | 节点名 | 基类 | 功能 | |--------|------|------| | `HasReceivedContextEvent` | `AutomataConditionalBase` | 检查上下文事件(带有效窗口期),完美支持 Conditional Abort | | `CheckAttribute` | `Conditional` | 检查数值属性(大于/小于/等于) | | `CheckStatus` | `Conditional` | 检查状态效果(单个/任意/全部) | | `CheckTimer` | `AutomataConditionalBase` | 检查计时器是否完成 | | `CheckString` | `Conditional` | 字符串检查(完全匹配/包含/正则) | | `IsPlayingFuncAnim` | `AutomataConditionalBase` | 检查是否正在播放指定功能动画 | ### 10.3 自定义 Editor | 编辑器类 | 目标 | 功能 | |---------|------|------| | `SetBreakthroughResistanceControl` | `SetBreakthroughResistance` | 条件显示高级设置列表 | --- ## 11. 自定义 Inspector 编辑器 在 BD Pro 中自定义节点的 Inspector 面板: ```csharp using UnityEngine.UIElements; using Opsive.Shared.Editor.UIElements; using Opsive.Shared.Editor.UIElements.Controls; using Opsive.Shared.Editor.UIElements.Controls.Types; [ControlType(typeof(目标Task类型))] public class MyTaskControl : TypeControlBase { public override bool UseLabel => false; // 不使用外层 Label protected override VisualElement GetControl(TypeControlInput input) { var task = input.Value as MyTask; var container = new VisualElement(); // 使用 FieldInspectorView.AddField 自动生成标准控件 FieldInspectorView.AddField( input.UnityObject, // UnityObject 引用 task, // 目标实例 "fieldName", // 字段名 container, // 父容器 (object newValue) => // 值变更回调 { task.fieldName = (FieldType)newValue; input.OnChangeEvent(task); // 通知 BD 数据已修改 } ); return container; } } ``` --- ## 12. Movement Pack Add-On 用于 NavMesh 导航的 Action 节点,所有移动类节点继承自 `MovementBase`。 ### 12.1 MovementBase 架构 ```csharp public abstract class MovementBase : Action { protected Pathfinder m_Pathfinder; // 寻路器抽象(默认 NavMeshAgentPathfinder) // 核心方法 protected virtual bool SetDestination(Vector3 destination); protected bool HasPath(); public bool HasArrived(); protected bool SamplePosition(ref Vector3 position); // 生命周期 public override void OnAwake() → m_Pathfinder.Initialize(gameObject); public override void OnStart() → m_Pathfinder.OnStart(); public override void OnEnd() → m_Pathfinder.Stop(); + OnEnd(); } ``` ### 12.2 常用移动节点 | 节点 | 功能 | |------|------| | `Seek` | 移向目标 GameObject 或 Position,到达返回 Success | | `Flee` | 远离目标 | | `Pursue` | 追逐(预测目标位置) | | `Patrol` | 巡逻路径 | | `Wander` | 随机漫游 | | `Follow` | 跟随目标 | | `Evade` | 逃避 | | `Cover` | 寻找掩体 | | `RotateTowards` | 转向目标 | | `MoveTowards` | 直线移向目标 | --- ## 13. Senses Pack Add-On 用于感知系统的节点。 ### 13.1 感知传感器类型 - `Visibility` — 视觉检测 - `Distance` — 距离检测 - `Sound` — 声音检测 - `Luminance` — 光照检测 - `Temperature` — 温度检测 - `Surface` — 地面类型检测 - `Tracer` — 痕迹追踪 ### 13.2 常用感知节点 - `CanDetectObject` — 检测是否能感知到物体 - `WithinRange` — 检测是否在范围内 - `GetSensorAmount` — 获取传感器数值 --- ## 14. 最佳实践与性能避坑 ### 14.1 OnAwake vs OnStart | 方法 | 调用频率 | 用途 | |------|---------|------| | `OnAwake()` | 行为树初始化时**仅一次** | 缓存组件引用(`GetComponent`) | | `OnStart()` | 节点**每次激活**时 | 初始化运行时状态 | **严禁在 `OnUpdate()` 中调用 `GetComponent`!** ### 14.2 Conditional Abort 安全编写 ```csharp public class SafeConditional : AutomataConditionalBase { public override TaskStatus OnUpdate() { // 正常评估 + 消费事件/状态 if (CheckAndConsume()) return TaskStatus.Success; return TaskStatus.Failure; } public override TaskStatus OnReevaluateUpdate() { // 仅检查,不消费! if (CheckOnly()) return TaskStatus.Success; return TaskStatus.Failure; } } ``` ### 14.3 节点命名规范 - Action 节点: 动词开头 (`PlayFuncAnim`, `SetBreakthroughResistance`, `GetPlayer`) - Conditional 节点: `Is*`, `Has*`, `Check*`, `Can*` (`IsPlayingFuncAnim`, `HasReceivedContextEvent`, `CheckAttribute`) ### 14.4 物理回调 如需接收物理回调,必须覆写对应属性: ```csharp protected override bool ReceiveTriggerEnterCallback => true; protected override void OnTriggerEnter(Collider other) { // 处理触发器进入 } ``` ### 14.5 协程支持 Task 支持在节点内启动协程: ```csharp protected void StartCoroutine(string methodName); protected Coroutine StartCoroutine(IEnumerator routine); protected void StopCoroutine(string methodName); protected void StopAllCoroutines(); ``` --- ## 15. 新节点编写 Checklist - [ ] 选择正确的基类 (`Action`/`Conditional`/`AutomataActionBase`/`AutomataConditionalBase`) - [ ] 添加 `[Category("Cielonos")]` 和 `[Description("...")]` - [ ] 在 `OnAwake()` 中缓存所有组件引用 - [ ] 在 `OnStart()` 中初始化每次运行的状态 - [ ] `OnUpdate()` 返回正确的 `TaskStatus` - [ ] 如果是 Conditional 且需要支持 Abort,考虑覆写 `OnReevaluateUpdate()` - [ ] 使用 `SharedVariable` 实现节点间数据共享 - [ ] 可选:实现 `Reset()` 提供编辑器默认值 - [ ] 可选:实现 `OnEnd()` 进行清理 - [ ] 确保引用了正确的 Assembly Definition (`Opsive.BehaviorDesigner.Runtime`)