# Laya 引擎集成 本教程将引导你在 Laya 引擎项目中集成和使用行为树系统。 ## 前置要求 - LayaAir 3.x 或更高版本 - 基本的 TypeScript 知识 - 已完成[快速开始](./getting-started.md)教程 ## 安装 在你的 Laya 项目根目录下: ```bash npm install @esengine/ecs-framework @esengine/behavior-tree ``` ## 项目结构 建议的项目结构: ``` src/ ├── ai/ │ ├── EnemyAI.ts │ └── BossAI.ts ├── systems/ │ └── AISystem.ts └── Main.ts resources/ └── behaviors/ ├── enemy.btree.json └── boss.btree.json ``` ## 初始化 ### 在Main.ts中初始化 ```typescript 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组件 ```typescript import { Core, Entity } from '@esengine/ecs-framework'; import { BehaviorTreeAssetSerializer, BehaviorTreeAssetLoader, BehaviorTreeStarter, BlackboardComponent } from '@esengine/behavior-tree'; export class EnemyAI extends Laya.Script { behaviorTreePath: string = "resources/behaviors/enemy.btree"; private aiEntity: Entity; onEnable() { this.loadBehaviorTree(); } private async loadBehaviorTree() { // 获取Core管理的场景 const scene = Core.scene; if (!scene) { console.error('场景未初始化'); return; } // 加载JSON资产 const jsonData = await Laya.loader.load(this.behaviorTreePath, Laya.Loader.JSON); // 转换为JSON字符串 const jsonString = typeof jsonData === 'string' ? jsonData : JSON.stringify(jsonData); // 反序列化 const asset = BehaviorTreeAssetSerializer.deserialize(jsonString); // 实例化 this.aiEntity = BehaviorTreeAssetLoader.instantiate(asset, scene, { namePrefix: (this.owner as Laya.Sprite).name }); // 设置黑板变量 const blackboard = this.aiEntity.getComponent(BlackboardComponent); blackboard?.setValue('layaSprite', this.owner); blackboard?.setValue('position', { x: (this.owner as Laya.Sprite).x, y: (this.owner as Laya.Sprite).y }); // 启动AI BehaviorTreeStarter.start(this.aiEntity); } onDisable() { // 停止AI if (this.aiEntity) { BehaviorTreeStarter.stop(this.aiEntity); } } } ``` ## 与Laya节点交互 在BehaviorTreeBuilder的action方法中,可以直接操作Laya节点。下面的完整示例展示了如何实现。 ## 完整示例 创建一个完整的敌人AI系统: ```typescript import { BehaviorTreeBuilder, BehaviorTreeStarter, BlackboardValueType, TaskStatus } from '@esengine/behavior-tree'; import { Core, Entity } from '@esengine/ecs-framework'; export class SimpleEnemyAI extends Laya.Script { public player: Laya.Sprite; public patrolPoints: Array<{x: number, y: number}> = []; private aiEntity: Entity; onEnable() { this.buildAI(); } private buildAI() { const scene = Core.scene; if (!scene) { console.error('场景未初始化'); return; } const sprite = this.owner as Laya.Sprite; this.aiEntity = BehaviorTreeBuilder.create(scene, 'EnemyAI') .blackboard() .defineVariable('sprite', BlackboardValueType.Object, sprite) .defineVariable('health', BlackboardValueType.Number, 100) .defineVariable('player', BlackboardValueType.Object, this.player) .defineVariable('patrolIndex', BlackboardValueType.Number, 0) .endBlackboard() .selector() // 攻击玩家 .sequence() .condition((e, bb) => { const player = bb?.getValue('player'); if (!player) return false; const dx = player.x - sprite.x; const dy = player.y - sprite.y; const distance = Math.sqrt(dx * dx + dy * dy); return distance < 200; // 检测范围 }, 'CheckPlayerInRange') .action('Attack', (e, bb) => { console.log('攻击玩家'); // 攻击逻辑 return TaskStatus.Success; }) .end() // 巡逻 .sequence() .action('Patrol', (e, bb, dt) => { const index = bb?.getValue('patrolIndex') || 0; const point = this.patrolPoints[index]; const dx = point.x - sprite.x; const dy = point.y - sprite.y; const distance = Math.sqrt(dx * dx + dy * dy); if (distance < 10) { // 到达路点 const nextIndex = (index + 1) % this.patrolPoints.length; bb?.setValue('patrolIndex', nextIndex); return TaskStatus.Success; } // 移动 const speed = 50; sprite.x += (dx / distance) * speed * dt; sprite.y += (dy / distance) * speed * dt; return TaskStatus.Running; }) .wait(2.0) .end() .end() .build(); BehaviorTreeStarter.start(this.aiEntity); } onDisable() { // 停止AI if (this.aiEntity) { BehaviorTreeStarter.stop(this.aiEntity); } } } ``` ## 性能优化 ### 使用对象池 ```typescript class AIPool { private pool: Entity[] = []; get(asset: any, scene: Scene): Entity { return this.pool.pop() || BehaviorTreeAssetLoader.instantiate(asset, scene); } release(entity: Entity) { BehaviorTreeStarter.stop(entity); this.pool.push(entity); } } ``` ### 降低更新频率 ```typescript private updateInterval: number = 0.1; // 每0.1秒更新 private timer: number = 0; onUpdate() { this.timer += Laya.timer.delta / 1000; if (this.timer >= this.updateInterval) { this.scene?.update(); this.timer = 0; } } ``` ## 常见问题 ### 资源加载失败? 确保: 1. 资源路径正确 2. 资源已添加到项目中 3. 使用 `Laya.loader.load()` 加载 ### AI不执行? 检查: 1. `onUpdate()` 是否被调用 2. `Scene.update()` 是否执行 3. 行为树是否已启动 ## 下一步 - 查看[高级用法](./advanced-usage.md) - 学习[最佳实践](./best-practices.md)