2025-10-28 11:45:35 +08:00
|
|
|
|
# 核心概念
|
|
|
|
|
|
|
|
|
|
|
|
本文介绍行为树系统的核心概念和工作原理。
|
|
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
## 什么是行为树?
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
行为树(Behavior Tree)是一种用于控制AI和自动化系统的决策结构。它通过树状层次结构组织任务,从根节点开始逐层执行,直到找到合适的行为。
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
|
|
|
|
|
### 与状态机的对比
|
|
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
传统状态机:
|
2025-10-28 11:45:35 +08:00
|
|
|
|
- 基于状态和转换
|
|
|
|
|
|
- 状态之间的转换复杂
|
|
|
|
|
|
- 难以扩展和维护
|
|
|
|
|
|
- 不便于复用
|
|
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
行为树:
|
2025-10-28 11:45:35 +08:00
|
|
|
|
- 基于任务和层次结构
|
|
|
|
|
|
- 模块化、易于复用
|
|
|
|
|
|
- 可视化编辑
|
|
|
|
|
|
- 灵活的决策逻辑
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 树结构
|
|
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
行为树由节点组成,形成树状结构:
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
Root (根节点)
|
|
|
|
|
|
├── Selector (选择器)
|
|
|
|
|
|
│ ├── Sequence (序列)
|
|
|
|
|
|
│ │ ├── Condition (条件)
|
|
|
|
|
|
│ │ └── Action (动作)
|
|
|
|
|
|
│ └── Action (动作)
|
|
|
|
|
|
└── Sequence (序列)
|
|
|
|
|
|
├── Action (动作)
|
|
|
|
|
|
└── Wait (等待)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
每个节点都有:
|
|
|
|
|
|
- 父节点(除了根节点)
|
2025-10-28 11:45:35 +08:00
|
|
|
|
- 零个或多个子节点
|
|
|
|
|
|
- 执行状态
|
|
|
|
|
|
- 返回结果
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 节点类型
|
|
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
### 复合节点(Composite)
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
复合节点有多个子节点,按特定规则执行它们。
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
#### Selector(选择器)
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
按顺序尝试执行子节点,直到某个子节点成功。
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
|
|
|
|
|
```typescript
|
2025-10-31 17:27:38 +08:00
|
|
|
|
const tree = BehaviorTreeBuilder.create('FindFood')
|
|
|
|
|
|
.selector('FindFoodSelector')
|
|
|
|
|
|
.log('尝试吃附近的食物', 'EatNearby')
|
|
|
|
|
|
.log('搜索食物', 'SearchFood')
|
|
|
|
|
|
.log('放弃', 'GiveUp')
|
|
|
|
|
|
.end()
|
|
|
|
|
|
.build();
|
2025-10-28 11:45:35 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
执行逻辑:
|
2025-10-28 11:45:35 +08:00
|
|
|
|
1. 尝试第一个子节点
|
2025-10-31 17:27:38 +08:00
|
|
|
|
2. 如果返回Success,选择器成功
|
|
|
|
|
|
3. 如果返回Failure,尝试下一个子节点
|
|
|
|
|
|
4. 如果返回Running,选择器返回Running
|
|
|
|
|
|
5. 所有子节点都失败时,选择器失败
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
|
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
#### Sequence(序列)
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
按顺序执行所有子节点,直到某个子节点失败。
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
|
|
|
|
|
```typescript
|
2025-10-31 17:27:38 +08:00
|
|
|
|
const tree = BehaviorTreeBuilder.create('Attack')
|
|
|
|
|
|
.sequence('AttackSequence')
|
|
|
|
|
|
.blackboardExists('target') // 检查是否有目标
|
|
|
|
|
|
.log('瞄准', 'Aim')
|
|
|
|
|
|
.log('开火', 'Fire')
|
|
|
|
|
|
.end()
|
|
|
|
|
|
.build();
|
2025-10-28 11:45:35 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
执行逻辑:
|
2025-10-28 11:45:35 +08:00
|
|
|
|
1. 依次执行子节点
|
2025-10-31 17:27:38 +08:00
|
|
|
|
2. 如果子节点返回Failure,序列失败
|
|
|
|
|
|
3. 如果子节点返回Running,序列返回Running
|
|
|
|
|
|
4. 如果子节点返回Success,继续下一个子节点
|
|
|
|
|
|
5. 所有子节点都成功时,序列成功
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
|
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
#### Parallel(并行)
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
|
|
|
|
|
同时执行多个子节点。
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
2025-10-31 17:27:38 +08:00
|
|
|
|
const tree = BehaviorTreeBuilder.create('PlayEffects')
|
|
|
|
|
|
.parallel('Effects', {
|
|
|
|
|
|
successPolicy: 'all', // 所有任务都要成功
|
|
|
|
|
|
failurePolicy: 'one' // 任一失败则失败
|
|
|
|
|
|
})
|
|
|
|
|
|
.log('播放动画', 'PlayAnimation')
|
|
|
|
|
|
.log('播放音效', 'PlaySound')
|
|
|
|
|
|
.log('生成粒子', 'SpawnEffect')
|
|
|
|
|
|
.end()
|
|
|
|
|
|
.build();
|
2025-10-28 11:45:35 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
策略类型:
|
|
|
|
|
|
- `successPolicy: 'all'`: 所有子节点都成功才成功
|
|
|
|
|
|
- `successPolicy: 'one'`: 任意一个子节点成功就成功
|
|
|
|
|
|
- `failurePolicy: 'all'`: 所有子节点都失败才失败
|
|
|
|
|
|
- `failurePolicy: 'one'`: 任意一个子节点失败就失败
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
|
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
### 装饰器节点(Decorator)
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
装饰器节点只有一个子节点,用于修改子节点的行为或结果。
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
#### Inverter(反转)
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
反转子节点的结果:
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
|
|
|
|
|
```typescript
|
2025-10-31 17:27:38 +08:00
|
|
|
|
const tree = BehaviorTreeBuilder.create('CheckSafe')
|
|
|
|
|
|
.inverter('NotHasEnemy')
|
|
|
|
|
|
.blackboardExists('enemy')
|
|
|
|
|
|
.end()
|
|
|
|
|
|
.build();
|
2025-10-28 11:45:35 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
#### Repeater(重复)
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
重复执行子节点:
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
|
|
|
|
|
```typescript
|
2025-10-31 17:27:38 +08:00
|
|
|
|
const tree = BehaviorTreeBuilder.create('Jump3Times')
|
|
|
|
|
|
.repeater(3, 'RepeatJump')
|
|
|
|
|
|
.log('跳跃', 'Jump')
|
|
|
|
|
|
.end()
|
|
|
|
|
|
.build();
|
2025-10-28 11:45:35 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
#### Cooldown(冷却)
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
限制子节点的执行频率:
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
|
|
|
|
|
```typescript
|
2025-10-31 17:27:38 +08:00
|
|
|
|
const tree = BehaviorTreeBuilder.create('UseSkill')
|
|
|
|
|
|
.cooldown(5.0, 'SkillCooldown')
|
|
|
|
|
|
.log('使用特殊技能', 'UseSpecialAbility')
|
|
|
|
|
|
.end()
|
|
|
|
|
|
.build();
|
2025-10-28 11:45:35 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
#### Timeout(超时)
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
限制子节点的执行时间:
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
|
|
|
|
|
```typescript
|
2025-10-31 17:27:38 +08:00
|
|
|
|
const tree = BehaviorTreeBuilder.create('TimedTask')
|
|
|
|
|
|
.timeout(10.0, 'TaskTimeout')
|
|
|
|
|
|
.log('长时间运行的任务', 'ComplexTask')
|
|
|
|
|
|
.end()
|
|
|
|
|
|
.build();
|
2025-10-28 11:45:35 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
### 叶节点(Leaf)
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
叶节点没有子节点,执行具体的任务。
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
#### Action(动作)
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
执行具体操作。内置动作节点包括:
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
|
|
|
|
|
```typescript
|
2025-10-31 17:27:38 +08:00
|
|
|
|
const tree = BehaviorTreeBuilder.create('Actions')
|
|
|
|
|
|
.sequence()
|
|
|
|
|
|
.wait(2.0) // 等待2秒
|
|
|
|
|
|
.log('Hello', 'LogAction') // 输出日志
|
|
|
|
|
|
.setBlackboardValue('score', 100) // 设置黑板值
|
|
|
|
|
|
.modifyBlackboardValue('score', 'add', 10) // 修改黑板值
|
|
|
|
|
|
.end()
|
|
|
|
|
|
.build();
|
2025-10-28 11:45:35 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
要实现自定义动作,需要创建自定义执行器,参见[自定义节点执行器](./custom-actions.md)。
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
#### Condition(条件)
|
|
|
|
|
|
|
|
|
|
|
|
检查条件。内置条件节点包括:
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
|
|
|
|
|
```typescript
|
2025-10-31 17:27:38 +08:00
|
|
|
|
const tree = BehaviorTreeBuilder.create('Conditions')
|
|
|
|
|
|
.selector()
|
|
|
|
|
|
.blackboardExists('player') // 检查变量是否存在
|
|
|
|
|
|
.blackboardCompare('health', 50, 'greater') // 比较变量值
|
|
|
|
|
|
.randomProbability(0.5) // 50%概率
|
|
|
|
|
|
.end()
|
|
|
|
|
|
.build();
|
2025-10-28 11:45:35 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
#### Wait(等待)
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
等待指定时间:
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
|
|
|
|
|
```typescript
|
2025-10-31 17:27:38 +08:00
|
|
|
|
const tree = BehaviorTreeBuilder.create('WaitExample')
|
|
|
|
|
|
.wait(2.0, 'Wait2Seconds')
|
|
|
|
|
|
.build();
|
2025-10-28 11:45:35 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 任务状态
|
|
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
每个节点执行后返回以下状态之一:
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
### Success(成功)
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
|
|
|
|
|
任务成功完成。
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
2025-10-31 17:27:38 +08:00
|
|
|
|
// 内置节点会根据逻辑自动返回Success
|
|
|
|
|
|
.log('任务完成') // 总是返回Success
|
|
|
|
|
|
.blackboardCompare('score', 100, 'greater') // 条件满足时返回Success
|
2025-10-28 11:45:35 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
### Failure(失败)
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
|
|
|
|
|
任务执行失败。
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
2025-10-31 17:27:38 +08:00
|
|
|
|
.blackboardCompare('score', 100, 'greater') // 条件不满足返回Failure
|
|
|
|
|
|
.blackboardExists('nonExistent') // 变量不存在返回Failure
|
2025-10-28 11:45:35 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
### Running(运行中)
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
任务需要多帧完成,仍在执行中。
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
|
|
|
|
|
```typescript
|
2025-10-31 17:27:38 +08:00
|
|
|
|
.wait(3.0) // 等待过程中返回Running,3秒后返回Success
|
2025-10-28 11:45:35 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
### Invalid(无效)
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
节点未初始化或已重置。通常不需要手动处理此状态。
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 黑板系统
|
|
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
黑板(Blackboard)是行为树的数据存储系统,用于在节点之间共享数据。
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
|
|
|
|
|
### 本地黑板
|
|
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
每个行为树实例都有自己的本地黑板:
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
|
|
|
|
|
```typescript
|
2025-10-31 17:27:38 +08:00
|
|
|
|
const tree = BehaviorTreeBuilder.create('EnemyAI')
|
|
|
|
|
|
.defineBlackboardVariable('health', 100)
|
|
|
|
|
|
.defineBlackboardVariable('target', null)
|
|
|
|
|
|
.defineBlackboardVariable('state', 'idle')
|
|
|
|
|
|
// ...
|
2025-10-28 11:45:35 +08:00
|
|
|
|
.build();
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 支持的数据类型
|
|
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
黑板支持以下数据类型:
|
|
|
|
|
|
- String:字符串
|
|
|
|
|
|
- Number:数字
|
|
|
|
|
|
- Boolean:布尔值
|
|
|
|
|
|
- Vector2:二维向量
|
|
|
|
|
|
- Vector3:三维向量
|
|
|
|
|
|
- Object:对象引用
|
|
|
|
|
|
- Array:数组
|
|
|
|
|
|
|
|
|
|
|
|
示例:
|
|
|
|
|
|
|
2025-10-28 11:45:35 +08:00
|
|
|
|
```typescript
|
2025-10-31 17:27:38 +08:00
|
|
|
|
const tree = BehaviorTreeBuilder.create('Variables')
|
|
|
|
|
|
.defineBlackboardVariable('name', 'Enemy') // 字符串
|
|
|
|
|
|
.defineBlackboardVariable('count', 0) // 数字
|
|
|
|
|
|
.defineBlackboardVariable('isActive', true) // 布尔值
|
|
|
|
|
|
.defineBlackboardVariable('position', { x: 0, y: 0 }) // 对象(也可用于Vector2)
|
|
|
|
|
|
.defineBlackboardVariable('velocity', { x: 0, y: 0, z: 0 }) // 对象(也可用于Vector3)
|
|
|
|
|
|
.defineBlackboardVariable('items', []) // 数组
|
|
|
|
|
|
.build();
|
2025-10-28 11:45:35 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 读写变量
|
|
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
通过`BehaviorTreeRuntimeComponent`访问黑板:
|
|
|
|
|
|
|
2025-10-28 11:45:35 +08:00
|
|
|
|
```typescript
|
2025-10-31 17:27:38 +08:00
|
|
|
|
const runtime = entity.getComponent(BehaviorTreeRuntimeComponent);
|
|
|
|
|
|
|
|
|
|
|
|
// 读取变量
|
|
|
|
|
|
const health = runtime?.getBlackboardValue('health');
|
|
|
|
|
|
const target = runtime?.getBlackboardValue('target');
|
|
|
|
|
|
|
|
|
|
|
|
// 写入变量
|
|
|
|
|
|
runtime?.setBlackboardValue('health', 50);
|
|
|
|
|
|
runtime?.setBlackboardValue('lastAttackTime', Date.now());
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
// 获取所有变量
|
|
|
|
|
|
const allVars = runtime?.getAllBlackboardVariables();
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
也可以使用内置节点操作黑板:
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
const tree = BehaviorTreeBuilder.create('BlackboardOps')
|
|
|
|
|
|
.sequence()
|
|
|
|
|
|
.setBlackboardValue('score', 100) // 设置值
|
|
|
|
|
|
.modifyBlackboardValue('score', 'add', 10) // 增加10
|
|
|
|
|
|
.blackboardCompare('score', 110, 'equals') // 检查是否等于110
|
|
|
|
|
|
.end()
|
|
|
|
|
|
.build();
|
2025-10-28 11:45:35 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 全局黑板
|
|
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
所有行为树实例共享的黑板,通过`GlobalBlackboardService`访问:
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
|
|
|
|
|
```typescript
|
2025-10-31 17:27:38 +08:00
|
|
|
|
import { GlobalBlackboardService } from '@esengine/behavior-tree';
|
2025-12-08 21:26:35 +08:00
|
|
|
|
import { Core } from '@esengine/ecs-framework';
|
2025-10-31 17:27:38 +08:00
|
|
|
|
|
|
|
|
|
|
const globalBlackboard = Core.services.resolve(GlobalBlackboardService);
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
|
|
|
|
|
// 设置全局变量
|
2025-10-31 17:27:38 +08:00
|
|
|
|
globalBlackboard.setValue('gameState', 'playing');
|
|
|
|
|
|
globalBlackboard.setValue('difficulty', 5);
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
// 读取全局变量
|
|
|
|
|
|
const gameState = globalBlackboard.getValue('gameState');
|
|
|
|
|
|
```
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
在自定义执行器中访问全局黑板:
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
import { GlobalBlackboardService } from '@esengine/behavior-tree';
|
2025-12-08 21:26:35 +08:00
|
|
|
|
import { Core } from '@esengine/ecs-framework';
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
export class CheckGameState implements INodeExecutor {
|
|
|
|
|
|
execute(context: NodeExecutionContext): TaskStatus {
|
|
|
|
|
|
const globalBlackboard = Core.services.resolve(GlobalBlackboardService);
|
|
|
|
|
|
const gameState = globalBlackboard.getValue('gameState');
|
|
|
|
|
|
|
|
|
|
|
|
if (gameState === 'paused') {
|
|
|
|
|
|
return TaskStatus.Failure;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return TaskStatus.Success;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-10-28 11:45:35 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 执行流程
|
|
|
|
|
|
|
|
|
|
|
|
### 初始化
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
2025-10-31 17:27:38 +08:00
|
|
|
|
// 1. 初始化Core和插件
|
2025-10-28 11:45:35 +08:00
|
|
|
|
Core.create();
|
2025-10-31 17:27:38 +08:00
|
|
|
|
const plugin = new BehaviorTreePlugin();
|
|
|
|
|
|
await Core.installPlugin(plugin);
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 创建场景
|
2025-10-28 11:45:35 +08:00
|
|
|
|
const scene = new Scene();
|
2025-10-31 17:27:38 +08:00
|
|
|
|
plugin.setupScene(scene);
|
2025-10-28 11:45:35 +08:00
|
|
|
|
Core.setScene(scene);
|
|
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
// 3. 构建行为树
|
|
|
|
|
|
const tree = BehaviorTreeBuilder.create('AI')
|
2025-10-28 11:45:35 +08:00
|
|
|
|
// ... 定义节点
|
|
|
|
|
|
.build();
|
|
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
// 4. 创建实体并启动
|
|
|
|
|
|
const entity = scene.createEntity('AIEntity');
|
|
|
|
|
|
BehaviorTreeStarter.start(entity, tree);
|
2025-10-28 11:45:35 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 更新循环
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
// 每帧更新
|
|
|
|
|
|
gameLoop(() => {
|
|
|
|
|
|
const deltaTime = getDeltaTime();
|
|
|
|
|
|
Core.update(deltaTime); // Core会自动更新场景和所有行为树
|
|
|
|
|
|
});
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 执行顺序
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
1. 从根节点开始
|
2025-10-31 17:27:38 +08:00
|
|
|
|
2. 根节点执行其逻辑(通常是Selector或Sequence)
|
2025-10-28 11:45:35 +08:00
|
|
|
|
3. 根节点的子节点按顺序执行
|
|
|
|
|
|
4. 每个子节点可能有自己的子节点
|
|
|
|
|
|
5. 叶节点执行具体操作并返回状态
|
|
|
|
|
|
6. 状态向上传播到父节点
|
|
|
|
|
|
7. 父节点根据策略决定如何处理子节点的状态
|
|
|
|
|
|
8. 最终根节点返回整体状态
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 执行示例
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
2025-10-31 17:27:38 +08:00
|
|
|
|
const tree = BehaviorTreeBuilder.create('Example')
|
2025-10-28 11:45:35 +08:00
|
|
|
|
.selector('Root') // 1. 执行选择器
|
|
|
|
|
|
.sequence('Branch1') // 2. 尝试第一个分支
|
2025-10-31 17:27:38 +08:00
|
|
|
|
.blackboardCompare('ready', true, 'equals', 'CheckReady') // 3. 条件失败
|
|
|
|
|
|
.end() // 4. 序列失败,选择器继续下一个分支
|
2025-10-28 11:45:35 +08:00
|
|
|
|
.sequence('Branch2') // 5. 尝试第二个分支
|
2025-10-31 17:27:38 +08:00
|
|
|
|
.blackboardCompare('active', true, 'equals', 'CheckActive') // 6. 条件成功
|
|
|
|
|
|
.log('执行动作', 'DoAction') // 7. 动作成功
|
|
|
|
|
|
.end() // 8. 序列成功,选择器成功
|
2025-10-28 11:45:35 +08:00
|
|
|
|
.end() // 9. 整个树成功
|
|
|
|
|
|
.build();
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
执行流程图:
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
Root(Selector)
|
|
|
|
|
|
→ Branch1(Sequence)
|
2025-10-31 17:27:38 +08:00
|
|
|
|
→ CheckReady: Failure
|
2025-10-28 11:45:35 +08:00
|
|
|
|
→ Branch1 fails
|
|
|
|
|
|
→ Branch2(Sequence)
|
2025-10-31 17:27:38 +08:00
|
|
|
|
→ CheckActive: Success
|
|
|
|
|
|
→ DoAction: Success
|
2025-10-28 11:45:35 +08:00
|
|
|
|
→ Branch2 succeeds
|
|
|
|
|
|
→ Root succeeds
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
## Runtime架构
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
本框架的行为树采用Runtime执行器架构:
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
### 核心组件
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
- **BehaviorTreeData**: 纯数据结构,描述行为树的结构和配置
|
|
|
|
|
|
- **BehaviorTreeRuntimeComponent**: 运行时组件,管理执行状态和黑板
|
|
|
|
|
|
- **BehaviorTreeExecutionSystem**: 执行系统,驱动行为树运行
|
|
|
|
|
|
- **INodeExecutor**: 节点执行器接口,定义节点的执行逻辑
|
|
|
|
|
|
- **NodeExecutionContext**: 执行上下文,包含执行所需的所有信息
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
### 架构特点
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
1. **数据与逻辑分离**: BehaviorTreeData是纯数据,执行逻辑在执行器中
|
|
|
|
|
|
2. **无状态执行器**: 执行器实例可以在多个节点间共享,状态存储在Runtime中
|
|
|
|
|
|
3. **类型安全**: 通过TypeScript类型系统保证类型安全
|
|
|
|
|
|
4. **高性能**: 避免不必要的对象创建,优化内存使用
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
### 数据流
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
|
|
|
|
|
```
|
2025-10-31 17:27:38 +08:00
|
|
|
|
BehaviorTreeBuilder
|
|
|
|
|
|
↓ (构建)
|
|
|
|
|
|
BehaviorTreeData
|
|
|
|
|
|
↓ (加载到)
|
|
|
|
|
|
BehaviorTreeAssetManager
|
|
|
|
|
|
↓ (读取)
|
|
|
|
|
|
BehaviorTreeExecutionSystem
|
|
|
|
|
|
↓ (执行)
|
|
|
|
|
|
INodeExecutor.execute(context)
|
|
|
|
|
|
↓ (返回)
|
|
|
|
|
|
TaskStatus
|
|
|
|
|
|
↓ (更新)
|
|
|
|
|
|
NodeRuntimeState
|
2025-10-28 11:45:35 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 下一步
|
|
|
|
|
|
|
2025-10-31 17:27:38 +08:00
|
|
|
|
现在你已经理解了行为树的核心概念,接下来可以:
|
2025-10-28 11:45:35 +08:00
|
|
|
|
|
|
|
|
|
|
- 查看[快速开始](./getting-started.md)创建第一个行为树
|
2025-10-31 17:27:38 +08:00
|
|
|
|
- 学习[自定义节点执行器](./custom-actions.md)创建自定义节点
|
2025-10-28 11:45:35 +08:00
|
|
|
|
- 探索[高级用法](./advanced-usage.md)了解更多功能
|
|
|
|
|
|
- 阅读[最佳实践](./best-practices.md)学习设计模式
|