编辑器工作流
本教程介绍如何使用行为树编辑器创建AI,并在游戏中加载使用。
1. 启动编辑器2. 创建行为树并定义黑板变量3. 添加和配置节点4. 导出JSON文件5. 在游戏中加载并使用使用编辑器创建
Section titled “使用编辑器创建”cd packages/editor-appnpm run tauri:dev- 创建行为树:
文件→新建项目→ 创建行为树文件 - 定义黑板变量:在黑板面板中添加共享变量
- 添加节点:从节点面板拖拽到画布
- 连接节点:拖拽连接点建立父子关系
- 配置属性:选中节点后在属性面板编辑
- 导出:
文件→导出→JSON格式
示例:敌人AI的黑板变量
Section titled “示例:敌人AI的黑板变量”在编辑器黑板面板中定义:
health: Number = 100target: Object = nullmoveSpeed: Number = 5.0attackRange: Number = 2.0示例:行为树结构
Section titled “示例:行为树结构”Root: Selector├── Combat Sequence│ ├── CheckHasTarget (Condition)│ ├── CheckInAttackRange (Condition)│ └── ExecuteAttack (Action)├── Patrol Sequence│ ├── MoveToNextPatrolPoint (Action)│ └── Wait 2s└── Idle (Action)在游戏中使用
Section titled “在游戏中使用”使用Builder API创建
Section titled “使用Builder API创建”推荐使用Builder API在代码中创建行为树:
import { Core, Scene } from '@esengine/ecs-framework';import { BehaviorTreePlugin, BehaviorTreeBuilder, BehaviorTreeStarter, BehaviorTreeRuntimeComponent} from '@esengine/behavior-tree';
// 初始化Core.create();const plugin = new BehaviorTreePlugin();await Core.installPlugin(plugin);
const scene = new Scene();plugin.setupScene(scene);Core.setScene(scene);
// 使用Builder创建行为树const tree = BehaviorTreeBuilder.create('EnemyAI') .defineBlackboardVariable('health', 100) .defineBlackboardVariable('target', null) .defineBlackboardVariable('moveSpeed', 5.0) .selector('MainBehavior') .sequence('AttackBranch') .blackboardExists('target') .blackboardCompare('health', 30, 'greater') .log('攻击目标', 'Attack') .end() .log('巡逻', 'Patrol') .end() .build();
// 创建实体并启动行为树const entity = scene.createEntity('Enemy');BehaviorTreeStarter.start(entity, tree);
// 访问和修改黑板const runtime = entity.getComponent(BehaviorTreeRuntimeComponent);runtime?.setBlackboardValue('target', someTarget);
// 游戏循环setInterval(() => { Core.update(0.016); // 60 FPS}, 16);实现自定义执行器
Section titled “实现自定义执行器”要扩展行为树的功能,需要创建自定义执行器(详见自定义节点执行器):
import { INodeExecutor, NodeExecutionContext, BindingHelper, NodeExecutorMetadata} from '@esengine/behavior-tree';import { TaskStatus, NodeType } from '@esengine/behavior-tree';
@NodeExecutorMetadata({ implementationType: 'AttackAction', nodeType: NodeType.Action, displayName: '攻击目标', description: '对目标造成伤害', category: '战斗', configSchema: { damage: { type: 'number', default: 10, supportBinding: true } }})export class AttackAction implements INodeExecutor { execute(context: NodeExecutionContext): TaskStatus { const damage = BindingHelper.getValue<number>(context, 'damage', 10); const target = context.runtime.getBlackboardValue('target');
if (!target) { return TaskStatus.Failure; }
// 执行攻击逻辑 performAttack(context.entity, target, damage); return TaskStatus.Success; }
reset(context: NodeExecutionContext): void { // 清理状态 }}1. 使用日志节点
Section titled “1. 使用日志节点”在行为树中添加Log节点输出调试信息:
const tree = BehaviorTreeBuilder.create('DebugAI') .log('开始战斗序列', 'StartCombat') .sequence('Combat') .blackboardCompare('health', 0, 'greater') .log('执行攻击', 'Attack') .end() .build();2. 监控黑板状态
Section titled “2. 监控黑板状态”const runtime = entity.getComponent(BehaviorTreeRuntimeComponent);console.log('黑板变量:', runtime?.getAllBlackboardVariables());console.log('活动节点:', Array.from(runtime?.activeNodeIds || []));3. 在自定义执行器中调试
Section titled “3. 在自定义执行器中调试”export class DebugAction implements INodeExecutor { execute(context: NodeExecutionContext): TaskStatus { const { nodeData, runtime, state } = context;
console.group(`[${nodeData.name}]`); console.log('配置:', nodeData.config); console.log('状态:', state); console.log('黑板:', runtime.getAllBlackboardVariables()); console.groupEnd();
return TaskStatus.Success; }}import { Core, Scene } from '@esengine/ecs-framework';import { BehaviorTreePlugin, BehaviorTreeBuilder, BehaviorTreeStarter, BehaviorTreeRuntimeComponent} from '@esengine/behavior-tree';
// 初始化Core.create();const plugin = new BehaviorTreePlugin();await Core.installPlugin(plugin);
const scene = new Scene();plugin.setupScene(scene);Core.setScene(scene);
// 使用Builder API构建行为树const tree = BehaviorTreeBuilder.create('EnemyAI') .defineBlackboardVariable('health', 100) .defineBlackboardVariable('hasTarget', false) .selector('Root') .sequence('Combat') .blackboardCompare('hasTarget', true, 'equals') .log('攻击玩家', 'Attack') .end() .log('空闲', 'Idle') .end() .build();
// 创建实体并启动const entity = scene.createEntity('Enemy');BehaviorTreeStarter.start(entity, tree);
// 模拟发现目标setTimeout(() => { const runtime = entity.getComponent(BehaviorTreeRuntimeComponent); runtime?.setBlackboardValue('hasTarget', true);}, 2000);
// 游戏循环setInterval(() => { Core.update(0.016);}, 16);