Files
esengine/docs/guide/behavior-tree/getting-started.md

314 lines
8.5 KiB
Markdown
Raw Normal View History

# 快速开始
本教程将引导你在5分钟内创建第一个行为树。
## 安装
```bash
npm install @esengine/behavior-tree
```
## 第一个行为树
让我们创建一个简单的AI行为树实现"巡逻-发现敌人-攻击"的逻辑。
### 步骤1导入依赖
```typescript
import { Core, Scene } from '@esengine/ecs-framework';
import {
BehaviorTreeBuilder,
BehaviorTreeStarter,
BehaviorTreePlugin,
BlackboardValueType,
TaskStatus,
CompareOperator
} from '@esengine/behavior-tree';
```
### 步骤2安装插件
```typescript
Core.create();
const plugin = new BehaviorTreePlugin();
await Core.installPlugin(plugin);
```
### 步骤3创建场景并设置行为树系统
```typescript
const scene = new Scene();
plugin.setupScene(scene);
Core.setScene(scene);
```
### 步骤4构建行为树
```typescript
const guardAI = BehaviorTreeBuilder.create(scene, 'GuardAI')
// 定义黑板变量
.blackboard()
.defineVariable('health', BlackboardValueType.Number, 100)
.defineVariable('hasEnemy', BlackboardValueType.Boolean, false)
.defineVariable('patrolPoint', BlackboardValueType.Number, 0)
.endBlackboard()
// 根选择器
.selector('RootSelector')
// 分支1如果发现敌人且生命值高则攻击
.sequence('CombatBranch')
.checkBlackboardExists('hasEnemy', true, 'CheckEnemy')
.compareBlackboardValue('health', CompareOperator.Greater, 30, 'CheckHealth')
.action('Attack', (entity, blackboard) => {
console.log('守卫正在攻击敌人');
// 模拟攻击逻辑
const health = blackboard?.getValue<number>('health') || 100;
blackboard?.setValue('health', health - 10);
return TaskStatus.Success;
})
.end()
// 分支2如果生命值低则逃跑
.sequence('FleeBranch')
.compareBlackboardValue('health', CompareOperator.LessOrEqual, 30)
.action('Flee', (entity) => {
console.log('守卫生命值过低,正在逃跑');
return TaskStatus.Success;
})
.end()
// 分支3默认巡逻
.sequence('PatrolBranch')
.action('MoveToNextPoint', (entity, blackboard) => {
const point = blackboard?.getValue<number>('patrolPoint') || 0;
const nextPoint = (point + 1) % 4;
blackboard?.setValue('patrolPoint', nextPoint);
console.log(`守卫移动到巡逻点 ${nextPoint}`);
return TaskStatus.Success;
})
.wait(2.0, 'WaitAtPoint') // 在巡逻点等待2秒
.end()
.end()
.build();
```
### 步骤5启动行为树
```typescript
BehaviorTreeStarter.start(guardAI);
```
### 步骤6运行游戏循环
```typescript
// 模拟游戏循环
setInterval(() => {
Core.update(0.1); // 传入deltaTime(秒)
}, 100); // 每100ms更新一次
```
## 完整代码
```typescript
import { Core, Scene } from '@esengine/ecs-framework';
import {
BehaviorTreeBuilder,
BehaviorTreeStarter,
BehaviorTreePlugin,
BlackboardValueType,
TaskStatus,
CompareOperator
} from '@esengine/behavior-tree';
async function main() {
// 创建核心和场景
Core.create();
const plugin = new BehaviorTreePlugin();
await Core.installPlugin(plugin);
const scene = new Scene();
plugin.setupScene(scene);
Core.setScene(scene);
// 构建行为树
const guardAI = BehaviorTreeBuilder.create(scene, 'GuardAI')
.blackboard()
.defineVariable('health', BlackboardValueType.Number, 100)
.defineVariable('hasEnemy', BlackboardValueType.Boolean, false)
.defineVariable('patrolPoint', BlackboardValueType.Number, 0)
.endBlackboard()
.selector('RootSelector')
.sequence('CombatBranch')
.checkBlackboardExists('hasEnemy', true)
.compareBlackboardValue('health', CompareOperator.Greater, 30)
.action('Attack', (entity, blackboard) => {
console.log('守卫正在攻击敌人');
const health = blackboard?.getValue<number>('health') || 100;
blackboard?.setValue('health', health - 10);
return TaskStatus.Success;
})
.end()
.sequence('FleeBranch')
.compareBlackboardValue('health', CompareOperator.LessOrEqual, 30)
.action('Flee', () => {
console.log('守卫生命值过低,正在逃跑');
return TaskStatus.Success;
})
.end()
.sequence('PatrolBranch')
.action('MoveToNextPoint', (entity, blackboard) => {
const point = blackboard?.getValue<number>('patrolPoint') || 0;
const nextPoint = (point + 1) % 4;
blackboard?.setValue('patrolPoint', nextPoint);
console.log(`守卫移动到巡逻点 ${nextPoint}`);
return TaskStatus.Success;
})
.wait(2.0)
.end()
.end()
.build();
// 启动AI
BehaviorTreeStarter.start(guardAI);
// 运行游戏循环
setInterval(() => {
Core.update(0.1); // 传入deltaTime(秒)
}, 100);
// 5秒后模拟发现敌人
setTimeout(() => {
const blackboard = guardAI.getComponent(BlackboardComponent);
blackboard?.setValue('hasEnemy', true);
console.log('发现敌人!');
}, 5000);
}
main();
```
## 运行结果
运行程序后,你会看到类似的输出:
```
守卫移动到巡逻点 1
守卫移动到巡逻点 2
守卫移动到巡逻点 3
发现敌人!
守卫正在攻击敌人
守卫正在攻击敌人
守卫正在攻击敌人
...
守卫生命值过低,正在逃跑
```
## 理解代码
### 黑板变量
```typescript
.blackboard()
.defineVariable('health', BlackboardValueType.Number, 100)
.defineVariable('hasEnemy', BlackboardValueType.Boolean, false)
.defineVariable('patrolPoint', BlackboardValueType.Number, 0)
.endBlackboard()
```
黑板用于在节点之间共享数据。这里定义了三个变量:
- `health`:守卫的生命值
- `hasEnemy`:是否发现敌人
- `patrolPoint`:当前巡逻点编号
### 选择器节点
```typescript
.selector('RootSelector')
// 分支1
// 分支2
// 分支3
.end()
```
选择器按顺序尝试执行子节点,直到某个子节点返回成功。类似于编程中的 `if-else if-else`
### 序列节点
```typescript
.sequence('CombatBranch')
.checkBlackboardExists('hasEnemy', true)
.compareBlackboardValue('health', CompareOperator.Greater, 30)
.action('Attack', ...)
.end()
```
序列节点按顺序执行所有子节点,如果任何一个失败则整个序列失败。类似于编程中的 `&&` 运算符。
### 自定义动作
```typescript
.action('Attack', (entity, blackboard, deltaTime) => {
// 你的自定义逻辑
console.log('执行攻击');
return TaskStatus.Success;
})
```
动作节点执行具体的操作并返回状态:
- `TaskStatus.Success`:成功完成
- `TaskStatus.Failure`:执行失败
- `TaskStatus.Running`:正在执行(需要多帧完成)
## 常见任务状态
行为树的每个节点返回以下状态之一:
- **Success**:任务成功完成
- **Failure**:任务执行失败
- **Running**:任务正在执行,需要在后续帧继续
- **Invalid**:无效状态(未初始化或已重置)
## 下一步
现在你已经创建了第一个行为树,接下来可以:
1. 学习[核心概念](./core-concepts.md)深入理解行为树原理
2. 尝试[编辑器使用指南](./editor-guide.md)可视化创建行为树
3. 查看[高级用法](./advanced-usage.md)了解更多功能
## 常见问题
### 为什么行为树不执行?
确保:
1. 已经安装了 `BehaviorTreePlugin`
2. 调用了 `plugin.setupScene(scene)`
3. 调用了 `BehaviorTreeStarter.start(aiRoot)`
4. 场景的 `update()` 方法在游戏循环中被调用
### 如何调试行为树?
使用日志动作和控制台输出:
```typescript
.log('到达这个节点', 'info')
.action('MyAction', (entity, blackboard) => {
console.log('blackboard:', blackboard?.getAllVariables());
return TaskStatus.Success;
})
```
### 如何停止行为树?
```typescript
BehaviorTreeStarter.stop(aiRoot);
```
或暂停后恢复:
```typescript
BehaviorTreeStarter.pause(aiRoot);
// ... 一段时间后
BehaviorTreeStarter.resume(aiRoot);
```