快速开始
本教程将引导你在5分钟内创建第一个行为树。
npm install @esengine/behavior-tree第一个行为树
Section titled “第一个行为树”让我们创建一个简单的AI行为树,实现”巡逻-发现敌人-攻击”的逻辑。
步骤1: 导入依赖
Section titled “步骤1: 导入依赖”import { Core, Scene, Entity } from '@esengine/ecs-framework';import { BehaviorTreeBuilder, BehaviorTreeStarter, BehaviorTreePlugin} from '@esengine/behavior-tree';步骤2: 初始化Core并安装插件
Section titled “步骤2: 初始化Core并安装插件”Core.create();const plugin = new BehaviorTreePlugin();await Core.installPlugin(plugin);步骤3: 创建场景并设置行为树系统
Section titled “步骤3: 创建场景并设置行为树系统”const scene = new Scene();plugin.setupScene(scene);Core.setScene(scene);步骤4: 构建行为树数据
Section titled “步骤4: 构建行为树数据”const guardAITree = BehaviorTreeBuilder.create('GuardAI') // 定义黑板变量 .defineBlackboardVariable('health', 100) .defineBlackboardVariable('hasEnemy', false) .defineBlackboardVariable('patrolPoint', 0)
// 根选择器 .selector('RootSelector') // 分支1: 如果发现敌人且生命值高,则攻击 .selector('CombatBranch') .blackboardExists('hasEnemy', 'CheckEnemy') .blackboardCompare('health', 30, 'greater', 'CheckHealth') .log('守卫正在攻击敌人', 'Attack') .end()
// 分支2: 如果生命值低,则逃跑 .selector('FleeBranch') .blackboardCompare('health', 30, 'lessOrEqual', 'CheckLowHealth') .log('守卫生命值过低,正在逃跑', 'Flee') .end()
// 分支3: 默认巡逻 .selector('PatrolBranch') .modifyBlackboardValue('patrolPoint', 'add', 1, 'IncrementPatrol') .log('守卫正在巡逻', 'Patrol') .wait(2.0, 'WaitAtPoint') .end() .end() .build();步骤5: 创建实体并启动行为树
Section titled “步骤5: 创建实体并启动行为树”// 创建守卫实体const guardEntity = scene.createEntity('Guard');
// 启动行为树BehaviorTreeStarter.start(guardEntity, guardAITree);步骤6: 运行游戏循环
Section titled “步骤6: 运行游戏循环”// 模拟游戏循环setInterval(() => { Core.update(0.1); // 传入deltaTime(秒)}, 100); // 每100ms更新一次步骤7: 模拟游戏事件
Section titled “步骤7: 模拟游戏事件”// 5秒后模拟发现敌人setTimeout(() => { const runtime = guardEntity.getComponent(BehaviorTreeRuntimeComponent); runtime?.setBlackboardValue('hasEnemy', true); console.log('发现敌人!');}, 5000);
// 10秒后模拟受伤setTimeout(() => { const runtime = guardEntity.getComponent(BehaviorTreeRuntimeComponent); runtime?.setBlackboardValue('health', 20); console.log('守卫受伤!');}, 10000);import { Core, Scene } from '@esengine/ecs-framework';import { BehaviorTreeBuilder, BehaviorTreeStarter, BehaviorTreePlugin, BehaviorTreeRuntimeComponent} from '@esengine/behavior-tree';
async function main() { // 1. 创建核心并安装插件 Core.create(); const plugin = new BehaviorTreePlugin(); await Core.installPlugin(plugin);
// 2. 创建场景 const scene = new Scene(); plugin.setupScene(scene); Core.setScene(scene);
// 3. 构建行为树数据 const guardAITree = BehaviorTreeBuilder.create('GuardAI') .defineBlackboardVariable('health', 100) .defineBlackboardVariable('hasEnemy', false) .defineBlackboardVariable('patrolPoint', 0) .selector('RootSelector') .selector('CombatBranch') .blackboardExists('hasEnemy') .blackboardCompare('health', 30, 'greater') .log('守卫正在攻击敌人') .end() .selector('FleeBranch') .blackboardCompare('health', 30, 'lessOrEqual') .log('守卫生命值过低,正在逃跑') .end() .selector('PatrolBranch') .modifyBlackboardValue('patrolPoint', 'add', 1) .log('守卫正在巡逻') .wait(2.0) .end() .end() .build();
// 4. 创建守卫实体并启动行为树 const guardEntity = scene.createEntity('Guard'); BehaviorTreeStarter.start(guardEntity, guardAITree);
// 5. 运行游戏循环 setInterval(() => { Core.update(0.1); }, 100);
// 6. 模拟游戏事件 setTimeout(() => { const runtime = guardEntity.getComponent(BehaviorTreeRuntimeComponent); runtime?.setBlackboardValue('hasEnemy', true); console.log('发现敌人!'); }, 5000);
setTimeout(() => { const runtime = guardEntity.getComponent(BehaviorTreeRuntimeComponent); runtime?.setBlackboardValue('health', 20); console.log('守卫受伤!'); }, 10000);}
main();运行程序后,你会看到类似的输出:
守卫正在巡逻守卫正在巡逻守卫正在巡逻发现敌人!守卫正在攻击敌人守卫正在攻击敌人守卫受伤!守卫生命值过低,正在逃跑.defineBlackboardVariable('health', 100).defineBlackboardVariable('hasEnemy', false).defineBlackboardVariable('patrolPoint', 0)黑板用于在节点之间共享数据。这里定义了三个变量:
health: 守卫的生命值hasEnemy: 是否发现敌人patrolPoint: 当前巡逻点编号
.selector('RootSelector') // 分支1 // 分支2 // 分支3.end()选择器按顺序尝试执行子节点,直到某个子节点返回成功。类似于编程中的 if-else if-else。
.blackboardExists('hasEnemy') // 检查变量是否存在.blackboardCompare('health', 30, 'greater') // 比较变量值条件节点用于检查黑板变量的值。
.log('守卫正在攻击敌人') // 输出日志.wait(2.0) // 等待2秒.modifyBlackboardValue('patrolPoint', 'add', 1) // 修改黑板值动作节点执行具体的操作。
Runtime组件
Section titled “Runtime组件”const runtime = guardEntity.getComponent(BehaviorTreeRuntimeComponent);runtime?.setBlackboardValue('hasEnemy', true);runtime?.getBlackboardValue('health');通过BehaviorTreeRuntimeComponent访问和修改黑板变量。
常见任务状态
Section titled “常见任务状态”行为树的每个节点返回以下状态之一:
- Success: 任务成功完成
- Failure: 任务执行失败
- Running: 任务正在执行,需要在后续帧继续
- Invalid: 无效状态(未初始化或已重置)
sequence()- 序列节点,按顺序执行所有子节点selector()- 选择器节点,按顺序尝试子节点直到成功parallel()- 并行节点,同时执行多个子节点parallelSelector()- 并行选择器randomSequence()- 随机序列randomSelector()- 随机选择器
inverter()- 反转子节点结果repeater(count)- 重复执行子节点alwaysSucceed()- 总是返回成功alwaysFail()- 总是返回失败untilSuccess()- 重复直到成功untilFail()- 重复直到失败conditional(key, value, operator)- 条件装饰器cooldown(time)- 冷却装饰器timeout(time)- 超时装饰器
wait(duration)- 等待指定时间log(message)- 输出日志setBlackboardValue(key, value)- 设置黑板值modifyBlackboardValue(key, operation, value)- 修改黑板值executeAction(actionName)- 执行自定义动作
blackboardExists(key)- 检查变量是否存在blackboardCompare(key, value, operator)- 比较黑板值randomProbability(probability)- 随机概率executeCondition(conditionName)- 执行自定义条件
BehaviorTreeStarter.start(entity, treeData);BehaviorTreeStarter.stop(entity);BehaviorTreeStarter.pause(entity);// ... 一段时间后BehaviorTreeStarter.resume(entity);BehaviorTreeStarter.restart(entity);现在你已经创建了第一个行为树,接下来可以:
- 学习核心概念深入理解行为树原理
- 学习资产管理了解如何加载和复用行为树、使用子树
- 查看自定义节点执行器学习如何创建自定义节点
- 根据你的场景查看集成教程:Cocos Creator 或 Node.js
- 查看高级用法了解更多功能
为什么行为树不执行?
Section titled “为什么行为树不执行?”确保:
- 已经安装了
BehaviorTreePlugin - 调用了
plugin.setupScene(scene) - 调用了
BehaviorTreeStarter.start(entity, treeData) - 在游戏循环中调用了
Core.update(deltaTime)
如何访问黑板变量?
Section titled “如何访问黑板变量?”const runtime = entity.getComponent(BehaviorTreeRuntimeComponent);
// 读取const health = runtime?.getBlackboardValue('health');
// 写入runtime?.setBlackboardValue('health', 50);
// 获取所有变量const allVars = runtime?.getAllBlackboardVariables();如何调试行为树?
Section titled “如何调试行为树?”使用日志节点:
.log('到达这个节点', 'DebugLog')或者在代码中监控黑板:
const runtime = entity.getComponent(BehaviorTreeRuntimeComponent);console.log('黑板变量:', runtime?.getAllBlackboardVariables());console.log('活动节点:', Array.from(runtime?.activeNodeIds || []));如何使用自定义逻辑?
Section titled “如何使用自定义逻辑?”内置的executeAction和executeCondition节点只是占位符。要实现真正的自定义逻辑,你需要创建自定义执行器:
参见自定义节点执行器学习如何创建。