Laya 引擎集成
本教程将引导你在 Laya 引擎项目中集成和使用行为树系统。
- LayaAir 3.x 或更高版本
- 基本的 TypeScript 知识
- 已完成快速开始教程
在你的 Laya 项目根目录下:
npm install @esengine/ecs-framework @esengine/behavior-tree建议的项目结构:
src/├── ai/│ ├── EnemyAI.ts│ └── BossAI.ts├── systems/│ └── AISystem.ts└── Main.tsresources/└── behaviors/ ├── enemy.btree.json └── boss.btree.json在Main.ts中初始化
Section titled “在Main.ts中初始化”import { Core, Scene } from '@esengine/ecs-framework';import { BehaviorTreePlugin } from '@esengine/behavior-tree';
export class Main { constructor() { Laya.init(1280, 720).then(() => { this.initECS(); this.startGame(); }); }
private async initECS() { // 初始化 ECS Core.create();
// 安装行为树插件 const btPlugin = new BehaviorTreePlugin(); await Core.installPlugin(btPlugin);
// 创建并设置场景 const scene = new Scene(); btPlugin.setupScene(scene); Core.setScene(scene);
// 启动更新循环 Laya.timer.frameLoop(1, this, this.update); }
private update() { // Core.update会自动更新场景 Core.update(Laya.timer.delta / 1000); }
private startGame() { // 加载场景 }}
new Main();创建AI组件
Section titled “创建AI组件”import { Core, Entity } from '@esengine/ecs-framework';import { BehaviorTreeBuilder, BehaviorTreeStarter, BehaviorTreeRuntimeComponent} from '@esengine/behavior-tree';
export class EnemyAI extends Laya.Script { private aiEntity: Entity;
onEnable() { this.createBehaviorTree(); }
private createBehaviorTree() { // 获取Core管理的场景 const scene = Core.scene; if (!scene) { console.error('场景未初始化'); return; }
const sprite = this.owner as Laya.Sprite;
// 使用Builder API创建行为树 const tree = BehaviorTreeBuilder.create('EnemyAI') .defineBlackboardVariable('layaSprite', sprite) .defineBlackboardVariable('health', 100) .defineBlackboardVariable('position', { x: sprite.x, y: sprite.y }) .selector('MainBehavior') .sequence('Combat') .blackboardCompare('health', 30, 'greater') .log('攻击', 'Attack') .end() .log('巡逻', 'Patrol') .end() .build();
// 创建AI实体并启动 this.aiEntity = scene.createEntity(`AI_${sprite.name}`); BehaviorTreeStarter.start(this.aiEntity, tree); }
onDisable() { // 停止AI if (this.aiEntity) { BehaviorTreeStarter.stop(this.aiEntity); } }}与Laya节点交互
Section titled “与Laya节点交互”要实现与Laya节点的交互,需要创建自定义执行器。下面展示一个完整示例。
创建一个使用自定义执行器的敌人AI系统:
import { BehaviorTreeBuilder, BehaviorTreeStarter, INodeExecutor, NodeExecutionContext, NodeExecutorMetadata, BehaviorTreeRuntimeComponent} from '@esengine/behavior-tree';import { TaskStatus, NodeType } from '@esengine/behavior-tree';import { Core, Entity } from '@esengine/ecs-framework';
// 自定义移动执行器@NodeExecutorMetadata({ implementationType: 'MoveToTarget', nodeType: NodeType.Action, displayName: '移动到目标', category: 'Laya', configSchema: { speed: { type: 'number', default: 50, supportBinding: true } }})export class MoveToTargetAction implements INodeExecutor { execute(context: NodeExecutionContext): TaskStatus { const sprite = context.runtime.getBlackboardValue('layaSprite'); const targetPos = context.runtime.getBlackboardValue('targetPosition'); const speed = context.nodeData.config.speed;
if (!sprite || !targetPos) { return TaskStatus.Failure; }
const dx = targetPos.x - sprite.x; const dy = targetPos.y - sprite.y; const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 10) { return TaskStatus.Success; }
sprite.x += (dx / distance) * speed * context.deltaTime; sprite.y += (dy / distance) * speed * context.deltaTime;
return TaskStatus.Running; }}
export class SimpleEnemyAI extends Laya.Script { public player: Laya.Sprite;
private aiEntity: Entity;
onEnable() { this.buildAI(); }
private buildAI() { const scene = Core.scene; if (!scene) { console.error('场景未初始化'); return; }
const sprite = this.owner as Laya.Sprite;
const tree = BehaviorTreeBuilder.create('EnemyAI') .defineBlackboardVariable('layaSprite', sprite) .defineBlackboardVariable('health', 100) .defineBlackboardVariable('player', this.player) .defineBlackboardVariable('targetPosition', { x: 0, y: 0 }) .selector('MainBehavior') .sequence('Attack') .blackboardExists('player') .log('攻击玩家', 'DoAttack') .end() .log('巡逻', 'Patrol') .end() .build();
this.aiEntity = scene.createEntity(`AI_${sprite.name}`); BehaviorTreeStarter.start(this.aiEntity, tree);
// 可以在帧更新中修改黑板 Laya.timer.frameLoop(1, this, () => { const runtime = this.aiEntity?.getComponent(BehaviorTreeRuntimeComponent); if (runtime && this.player) { runtime.setBlackboardValue('targetPosition', { x: this.player.x, y: this.player.y }); } }); }
onDisable() { if (this.aiEntity) { BehaviorTreeStarter.stop(this.aiEntity); } Laya.timer.clearAll(this); }}使用冷却装饰器
Section titled “使用冷却装饰器”对于不需要每帧更新的AI,使用冷却装饰器:
const tree = BehaviorTreeBuilder.create('ThrottledAI') .cooldown(0.2, 'ThrottleRoot') // 每0.2秒执行一次 .selector('MainBehavior') // AI逻辑... .end() .end() .build();限制同时运行的AI数量
Section titled “限制同时运行的AI数量”class AIManager { private activeAIs: Entity[] = []; private maxAIs: number = 20;
addAI(entity: Entity, tree: BehaviorTreeData) { if (this.activeAIs.length >= this.maxAIs) { const furthest = this.activeAIs.shift(); if (furthest) { BehaviorTreeStarter.stop(furthest); } }
BehaviorTreeStarter.start(entity, tree); this.activeAIs.push(entity); }}资源加载失败?
Section titled “资源加载失败?”确保:
- 资源路径正确
- 资源已添加到项目中
- 使用
Laya.loader.load()加载
AI不执行?
Section titled “AI不执行?”检查:
onUpdate()是否被调用Scene.update()是否执行- 行为树是否已启动