Laya 引擎集成
本教程将引导你在 Laya 引擎项目中集成和使用行为树系统。
前置要求
- LayaAir 3.x 或更高版本
- 基本的 TypeScript 知识
- 已完成快速开始教程
安装
在你的 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;
}
}常见问题
资源加载失败?
确保:
- 资源路径正确
- 资源已添加到项目中
- 使用
Laya.loader.load()加载
AI不执行?
检查:
onUpdate()是否被调用Scene.update()是否执行- 行为树是否已启动