653 lines
20 KiB
Markdown
653 lines
20 KiB
Markdown
# 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<TSystem, TComponent>` 等 |
|
||
| 编写难度 | 低,类似 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<T>
|
||
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<float> m_Speed = 5f; // 带默认值
|
||
[SerializeField] SharedVariable<GameObject> m_Target; // 引用类型
|
||
[SerializeField] SharedVariable<Vector3> m_Position;
|
||
[SerializeField] SharedVariable<string> m_EventName;
|
||
[SerializeField] SharedVariable<bool> 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<object>(behaviorTree, "EventName", arg1);
|
||
EventHandler.ExecuteEvent<object, object>(behaviorTree, "EventName", arg1, arg2);
|
||
|
||
// 注册事件
|
||
EventHandler.RegisterEvent(behaviorTree, "EventName", OnEventReceived);
|
||
EventHandler.RegisterEvent<object>(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<Automata>();
|
||
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<Automata>();
|
||
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<T>` 实现节点间数据共享
|
||
- [ ] 可选:实现 `Reset()` 提供编辑器默认值
|
||
- [ ] 可选:实现 `OnEnd()` 进行清理
|
||
- [ ] 确保引用了正确的 Assembly Definition (`Opsive.BehaviorDesigner.Runtime`)
|