Files
esengine/docs/guide/behavior-tree/cocos-integration.md

502 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Cocos Creator 集成
本教程将引导你在 Cocos Creator 项目中集成和使用行为树系统。
## 前置要求
- Cocos Creator 3.x 或更高版本
- 基本的 TypeScript 知识
- 已完成[快速开始](./getting-started.md)教程
## 安装
### 步骤1安装依赖
在你的 Cocos Creator 项目根目录下:
```bash
npm install @esengine/ecs-framework @esengine/behavior-tree
```
### 步骤2配置 tsconfig.json
确保 `tsconfig.json` 中包含以下配置:
```json
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"moduleResolution": "node"
}
}
```
## 项目结构
建议的项目结构:
```
assets/
├── scripts/
│ ├── ai/
│ │ ├── EnemyAIComponent.ts # AI 组件
│ │ └── PlayerDetector.ts # 检测器
│ ├── systems/
│ │ └── BehaviorTreeSystem.ts # 行为树系统
│ └── Main.ts # 主入口
├── resources/
│ └── behaviors/
│ ├── enemy-ai.btree.json # 行为树资产
│ └── patrol.btree.json # 子树资产
└── types/
└── enemy-ai.ts # 类型定义
```
## 初始化 ECS 和行为树
### 创建主入口组件
创建 `assets/scripts/Main.ts`
```typescript
import { _decorator, Component } from 'cc';
import { Core, Scene } from '@esengine/ecs-framework';
import { BehaviorTreePlugin } from '@esengine/behavior-tree';
const { ccclass } = _decorator;
@ccclass('Main')
export class Main extends Component {
async onLoad() {
// 初始化 ECS Core
Core.create();
// 安装行为树插件
const behaviorTreePlugin = new BehaviorTreePlugin();
await Core.installPlugin(behaviorTreePlugin);
// 创建并设置场景
const scene = new Scene();
behaviorTreePlugin.setupScene(scene);
Core.setScene(scene);
console.log('ECS 和行为树系统初始化完成');
}
update(deltaTime: number) {
// 更新 ECS会自动更新场景
Core.update(deltaTime);
}
onDestroy() {
// 清理资源
Core.destroy();
}
}
```
### 添加组件到场景
1. 在场景中创建一个空节点(命名为 `GameManager`
2. 添加 `Main` 组件到该节点
## 创建 AI 组件
创建 `assets/scripts/ai/EnemyAIComponent.ts`
```typescript
import { _decorator, Component, Node } from 'cc';
import { Core, Entity } from '@esengine/ecs-framework';
import {
BehaviorTreeAssetSerializer,
BehaviorTreeAssetLoader,
BehaviorTreeStarter,
BlackboardComponent
} from '@esengine/behavior-tree';
import { resources } from 'cc';
const { ccclass, property } = _decorator;
@ccclass('EnemyAIComponent')
export class EnemyAIComponent extends Component {
@property
behaviorTreeAsset: string = 'behaviors/enemy-ai.btree';
private aiEntity: Entity | null = null;
async start() {
// 加载行为树资产
await this.loadBehaviorTree();
}
private async loadBehaviorTree() {
try {
// 获取Core管理的场景
const scene = Core.scene;
if (!scene) {
console.error('场景未初始化');
return;
}
// 从 resources 加载JSON资产
resources.load(this.behaviorTreeAsset, (err, jsonAsset: any) => {
if (err) {
console.error('加载行为树失败:', err);
return;
}
// 获取JSON字符串
const jsonString = jsonAsset.json ? JSON.stringify(jsonAsset.json) : jsonAsset.text;
// 反序列化
const btAsset = BehaviorTreeAssetSerializer.deserialize(jsonString);
// 实例化
this.aiEntity = BehaviorTreeAssetLoader.instantiate(
btAsset,
scene,
{
namePrefix: this.node.name
}
);
// 设置黑板初始值
const blackboard = this.aiEntity.getComponent(BlackboardComponent);
if (blackboard) {
// 可以在这里设置引用到 Cocos 节点
blackboard.setValue('cocosNode', this.node);
blackboard.setValue('position', this.node.position.clone());
}
// 启动 AI
BehaviorTreeStarter.start(this.aiEntity);
console.log('敌人 AI 已启动');
});
} catch (error) {
console.error('初始化行为树失败:', error);
}
}
onDestroy() {
// 停止 AI
if (this.aiEntity) {
BehaviorTreeStarter.stop(this.aiEntity);
}
}
}
```
## 与 Cocos 节点交互
### 在编辑器ExecuteAction节点中编写代码
在行为树编辑器中,可以使用 `Execute Action` 节点,并编写代码:
```javascript
// 获取 Cocos 节点
const cocosNode = blackboard.getValue('cocosNode');
// 播放攻击动画
const animation = cocosNode.getComponent('Animation');
animation.play('attack');
return TaskStatus.Success;
```
## 完整示例:敌人 AI
### 行为树设计
使用编辑器创建 `enemy-ai.btree.json`
```
RootSelector
├── CombatSequence
│ ├── CheckPlayerInRange (Condition)
│ ├── CheckHealthGood (Condition)
│ └── AttackPlayer (Action)
├── FleeSequence
│ ├── CheckHealthLow (Condition)
│ └── RunAway (Action)
└── PatrolSequence
├── PickWaypoint (Action)
├── MoveToWaypoint (Action)
└── Wait (Action)
```
### 黑板变量
定义以下黑板变量:
- `cocosNode`Node - Cocos 节点引用
- `health`Number - 生命值
- `playerNode`Object - 玩家节点引用
- `detectionRange`Number - 检测范围
- `attackRange`Number - 攻击范围
- `currentWaypoint`Number - 当前路点索引
### 实现检测系统
创建 `assets/scripts/ai/PlayerDetector.ts`
```typescript
import { _decorator, Component, Node, Vec3 } from 'cc';
import { BlackboardComponent } from '@esengine/behavior-tree';
const { ccclass, property } = _decorator;
@ccclass('PlayerDetector')
export class PlayerDetector extends Component {
@property(Node)
player: Node = null;
@property
detectionRange: number = 10;
private blackboard: BlackboardComponent | null = null;
start() {
// 假设AI组件在同一节点上
const aiComponent = this.node.getComponent('EnemyAIComponent') as any;
if (aiComponent && aiComponent.aiEntity) {
this.blackboard = aiComponent.aiEntity.getComponent(BlackboardComponent);
}
}
update(deltaTime: number) {
if (!this.blackboard || !this.player) {
return;
}
// 计算距离
const distance = Vec3.distance(this.node.position, this.player.position);
// 更新黑板
this.blackboard.setValue('playerNode', this.player);
this.blackboard.setValue('playerInRange', distance <= this.detectionRange);
this.blackboard.setValue('distanceToPlayer', distance);
}
}
```
## 资源管理
### 预加载行为树资产
在游戏启动时预加载所有行为树资产:
```typescript
import { resources } from 'cc';
async function preloadBehaviorTrees() {
const assets = [
'behaviors/enemy-ai',
'behaviors/boss-ai',
'behaviors/patrol'
];
for (const path of assets) {
await new Promise((resolve, reject) => {
resources.preload(path, (err) => {
if (err) reject(err);
else resolve(null);
});
});
}
console.log('行为树资产预加载完成');
}
```
### 使用 AssetManager
对于动态加载,可以使用 Cocos 的 AssetManager
```typescript
import { assetManager } from 'cc';
assetManager.loadBundle('behaviors', (err, bundle) => {
if (err) {
console.error('加载 bundle 失败:', err);
return;
}
bundle.load('enemy-ai', (err, asset) => {
if (!err) {
// 使用资产
}
});
});
```
## 调试
### 可视化调试信息
创建调试组件显示 AI 状态:
```typescript
import { _decorator, Component, Label } from 'cc';
import { BlackboardComponent } from '@esengine/behavior-tree';
const { ccclass, property } = _decorator;
@ccclass('AIDebugger')
export class AIDebugger extends Component {
@property(Label)
debugLabel: Label = null;
private blackboard: BlackboardComponent | null = null;
start() {
const aiComponent = this.node.getComponent('EnemyAIComponent') as any;
if (aiComponent && aiComponent.aiEntity) {
this.blackboard = aiComponent.aiEntity.getComponent(BlackboardComponent);
}
}
update() {
if (!this.blackboard || !this.debugLabel) {
return;
}
const health = this.blackboard.getValue('health');
const state = this.blackboard.getValue('currentState');
this.debugLabel.string = `Health: ${health}\nState: ${state}`;
}
}
```
## 性能优化
### 1. 使用对象池
为 AI 实体使用对象池:
```typescript
class AIEntityPool {
private pool: Entity[] = [];
private scene: Scene;
constructor(scene: Scene) {
this.scene = scene;
}
acquire(behaviorTreeAsset: any): Entity {
if (this.pool.length > 0) {
const entity = this.pool.pop()!;
BehaviorTreeStarter.restart(entity);
return entity;
}
return BehaviorTreeAssetLoader.instantiate(behaviorTreeAsset, this.scene);
}
release(entity: Entity) {
BehaviorTreeStarter.stop(entity);
this.pool.push(entity);
}
}
```
### 2. 限制更新频率
对于远离相机的敌人,可以在行为树内部使用节流机制:
```typescript
// 在行为树的Action节点中实现节流
function throttledAction(entity, blackboard, deltaTime) {
let lastUpdate = blackboard?.getValue('lastUpdateTime') || 0;
const currentTime = Date.now();
// 根据距离决定更新间隔
const distance = getDistanceToCamera();
const updateInterval = distance < 10 ? 0 : 200; // 远处敌人200ms更新一次
if (currentTime - lastUpdate < updateInterval) {
return TaskStatus.Running;
}
blackboard?.setValue('lastUpdateTime', currentTime);
// 执行实际逻辑
performAILogic();
return TaskStatus.Success;
}
```
### 3. 使用二进制格式
在构建时将 JSON 转换为二进制格式以减小包体:
```bash
# 在构建脚本中
node scripts/convert-bt-to-binary.js
```
## 多平台发布
### Web 平台
在 Web 平台,确保资源路径正确:
```typescript
// 使用相对路径
const assetPath = 'behaviors/enemy-ai';
```
### 原生平台
原生平台可以使用二进制格式以获得更好的性能:
```typescript
// 检测平台
if (sys.isNative) {
// 加载二进制格式
assetPath = 'behaviors/enemy-ai.btree.bin';
} else {
// 加载 JSON 格式
assetPath = 'behaviors/enemy-ai.btree.json';
}
```
## 常见问题
### 行为树无法加载?
检查:
1. 资源路径是否正确(相对于 `resources` 目录)
2. 文件是否已添加到项目中
3. 检查控制台错误信息
### AI 不执行?
确保:
1. `Main` 组件的 `update` 方法被调用
2. `Scene.update()` 在每帧被调用
3. 行为树已通过 `BehaviorTreeStarter.start()` 启动
### 黑板变量不更新?
检查:
1. 变量名拼写是否正确
2. 是否在正确的时机更新变量
3. 使用 `BlackboardComponent.getValue()``setValue()` 方法
## 下一步
- 查看[高级用法](./advanced-usage.md)了解子树和异步加载
- 学习[最佳实践](./best-practices.md)优化你的 AI