2025-06-24 23:51:59 +08:00
|
|
|
|
import { _decorator, Component, resources, JsonAsset, Vec3 } from 'cc';
|
|
|
|
|
|
import { BehaviorTree, BehaviorTreeBuilder, Blackboard, BehaviorTreeJSONConfig, ExecutionContext, EventRegistry } from '@esengine/ai';
|
2025-06-24 19:34:37 +08:00
|
|
|
|
import { UnitController } from './UnitController';
|
2025-06-24 23:51:59 +08:00
|
|
|
|
import { RTSBehaviorHandler } from './RTSBehaviorHandler';
|
2025-06-24 19:34:37 +08:00
|
|
|
|
|
|
|
|
|
|
const { ccclass, property } = _decorator;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2025-06-24 23:51:59 +08:00
|
|
|
|
* 游戏执行上下文接口
|
|
|
|
|
|
* 继承框架的ExecutionContext,添加游戏特定的属性
|
2025-06-24 19:34:37 +08:00
|
|
|
|
*/
|
2025-06-24 23:51:59 +08:00
|
|
|
|
interface GameExecutionContext extends ExecutionContext {
|
2025-06-24 19:34:37 +08:00
|
|
|
|
unitController: UnitController;
|
|
|
|
|
|
gameObject: any;
|
2025-06-24 23:51:59 +08:00
|
|
|
|
eventRegistry?: EventRegistry;
|
|
|
|
|
|
// 确保继承索引签名
|
|
|
|
|
|
[key: string]: unknown;
|
2025-06-24 19:34:37 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 行为树管理器 - 使用@esengine/ai包管理行为树
|
|
|
|
|
|
*/
|
|
|
|
|
|
@ccclass('BehaviorTreeManager')
|
|
|
|
|
|
export class BehaviorTreeManager extends Component {
|
|
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
|
debugMode: boolean = true;
|
|
|
|
|
|
|
|
|
|
|
|
@property
|
2025-06-24 23:51:59 +08:00
|
|
|
|
tickInterval: number = 0.1; // 行为树更新间隔(秒)- 10fps更新频率,平衡性能和响应性
|
2025-06-24 19:34:37 +08:00
|
|
|
|
|
|
|
|
|
|
private behaviorTree: BehaviorTree<GameExecutionContext> | null = null;
|
|
|
|
|
|
private blackboard: Blackboard | null = null;
|
|
|
|
|
|
private context: GameExecutionContext | null = null;
|
2025-06-24 23:51:59 +08:00
|
|
|
|
private eventRegistry: EventRegistry | null = null;
|
2025-06-24 19:34:37 +08:00
|
|
|
|
private isLoaded: boolean = false;
|
|
|
|
|
|
private isRunning: boolean = false;
|
|
|
|
|
|
private lastTickTime: number = 0;
|
|
|
|
|
|
private unitController: UnitController | null = null;
|
|
|
|
|
|
private currentBehaviorTreeName: string = '';
|
2025-06-24 23:51:59 +08:00
|
|
|
|
private behaviorHandler: RTSBehaviorHandler | null = null;
|
2025-06-24 19:34:37 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 初始化行为树
|
|
|
|
|
|
*/
|
|
|
|
|
|
async initializeBehaviorTree(behaviorTreeName: string, unitController: UnitController) {
|
|
|
|
|
|
this.currentBehaviorTreeName = behaviorTreeName;
|
|
|
|
|
|
this.unitController = unitController;
|
|
|
|
|
|
|
2025-06-24 23:51:59 +08:00
|
|
|
|
// 获取RTSBehaviorHandler组件
|
|
|
|
|
|
this.behaviorHandler = this.getComponent(RTSBehaviorHandler);
|
|
|
|
|
|
if (!this.behaviorHandler) {
|
|
|
|
|
|
console.error(`BehaviorTreeManager: 未找到RTSBehaviorHandler组件 - ${this.node.name}`);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-24 19:34:37 +08:00
|
|
|
|
try {
|
|
|
|
|
|
await this.loadBehaviorTree(behaviorTreeName);
|
|
|
|
|
|
this.setupBlackboard();
|
|
|
|
|
|
this.isLoaded = true;
|
|
|
|
|
|
this.isRunning = true;
|
2025-06-24 23:51:59 +08:00
|
|
|
|
console.log(`✅ 行为树初始化成功: ${behaviorTreeName} for ${this.unitController.node.name}`);
|
|
|
|
|
|
console.log(` - 行为树: ${this.behaviorTree ? '已创建' : '未创建'}`);
|
|
|
|
|
|
console.log(` - 黑板变量: ${this.blackboard ? '已创建' : '未创建'}`);
|
|
|
|
|
|
console.log(` - 运行状态: ${this.isRunning ? '运行中' : '已停止'}`);
|
2025-06-24 19:34:37 +08:00
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error(`行为树初始化失败: ${behaviorTreeName}`, error);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 加载行为树文件
|
|
|
|
|
|
*/
|
|
|
|
|
|
private async loadBehaviorTree(behaviorTreeName: string): Promise<void> {
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
|
const jsonPath = `${behaviorTreeName}.bt`;
|
2025-06-24 23:51:59 +08:00
|
|
|
|
console.log(`🔍 尝试加载行为树文件: ${jsonPath}`);
|
2025-06-24 19:34:37 +08:00
|
|
|
|
|
|
|
|
|
|
resources.load(jsonPath, JsonAsset, (err, asset) => {
|
|
|
|
|
|
if (err) {
|
|
|
|
|
|
console.error(`加载行为树文件失败: ${jsonPath}`, err);
|
|
|
|
|
|
reject(err);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const behaviorTreeData = asset.json as BehaviorTreeJSONConfig;
|
|
|
|
|
|
|
|
|
|
|
|
// 创建执行上下文
|
|
|
|
|
|
this.blackboard = new Blackboard();
|
2025-06-24 23:51:59 +08:00
|
|
|
|
this.eventRegistry = this.createEventRegistry();
|
2025-06-24 19:34:37 +08:00
|
|
|
|
this.context = {
|
|
|
|
|
|
blackboard: this.blackboard,
|
|
|
|
|
|
unitController: this.unitController!,
|
2025-06-24 23:51:59 +08:00
|
|
|
|
gameObject: this.node,
|
|
|
|
|
|
eventRegistry: this.eventRegistry
|
|
|
|
|
|
} as GameExecutionContext;
|
2025-06-24 19:34:37 +08:00
|
|
|
|
|
|
|
|
|
|
// 从JSON数据创建行为树
|
2025-06-24 23:51:59 +08:00
|
|
|
|
const buildResult = BehaviorTreeBuilder.fromBehaviorTreeConfig<GameExecutionContext>(behaviorTreeData, this.context);
|
|
|
|
|
|
this.behaviorTree = buildResult.tree;
|
2025-06-24 19:34:37 +08:00
|
|
|
|
this.blackboard = buildResult.blackboard;
|
|
|
|
|
|
|
|
|
|
|
|
resolve();
|
|
|
|
|
|
} catch (parseError) {
|
|
|
|
|
|
console.error(`创建行为树失败: ${jsonPath}`, parseError);
|
|
|
|
|
|
reject(parseError);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-24 23:51:59 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 创建事件注册表
|
|
|
|
|
|
*/
|
|
|
|
|
|
private createEventRegistry(): EventRegistry {
|
|
|
|
|
|
const registry = new EventRegistry();
|
|
|
|
|
|
|
|
|
|
|
|
// 注册简化的矿工行为事件处理器
|
|
|
|
|
|
const eventHandlers = {
|
|
|
|
|
|
// 矿工核心行为
|
|
|
|
|
|
'find-and-mine-ore': (context: any, params?: any) => this.callBehaviorHandler('onFindAndMineOre', params),
|
|
|
|
|
|
'store-ore': (context: any, params?: any) => this.callBehaviorHandler('onStoreOre', params),
|
|
|
|
|
|
'idle-behavior': (context: any, params?: any) => this.callBehaviorHandler('onIdleBehavior', params)
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 将事件处理器注册到EventRegistry
|
|
|
|
|
|
Object.entries(eventHandlers).forEach(([eventName, handler]) => {
|
|
|
|
|
|
registry.registerAction(eventName, handler);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return registry;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 调用行为处理器的方法
|
|
|
|
|
|
*/
|
|
|
|
|
|
private callBehaviorHandler(methodName: string, params: any = {}): string {
|
|
|
|
|
|
if (!this.behaviorHandler) {
|
|
|
|
|
|
console.error(`BehaviorTreeManager: RTSBehaviorHandler未初始化 - ${this.node.name}`);
|
|
|
|
|
|
return 'failure';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 直接调用RTSBehaviorHandler的方法
|
|
|
|
|
|
const method = (this.behaviorHandler as any)[methodName];
|
|
|
|
|
|
if (typeof method === 'function') {
|
|
|
|
|
|
console.log(`🎯 调用行为处理器: ${methodName} (${this.node.name})`);
|
|
|
|
|
|
const result = method.call(this.behaviorHandler, params);
|
|
|
|
|
|
console.log(`📤 行为处理器返回: ${methodName} -> "${result}" (${this.node.name})`);
|
|
|
|
|
|
return result || 'success'; // 确保有返回值
|
|
|
|
|
|
} else {
|
|
|
|
|
|
console.error(`BehaviorTreeManager: 方法不存在: ${methodName}`);
|
|
|
|
|
|
return 'failure';
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error(`BehaviorTreeManager: 调用方法失败: ${methodName}`, error);
|
|
|
|
|
|
return 'failure';
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-06-24 19:34:37 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 设置黑板基础信息
|
|
|
|
|
|
*/
|
|
|
|
|
|
private setupBlackboard() {
|
|
|
|
|
|
if (!this.unitController || !this.blackboard) return;
|
|
|
|
|
|
|
2025-06-24 23:51:59 +08:00
|
|
|
|
// 设置矿工基础信息
|
2025-06-24 19:34:37 +08:00
|
|
|
|
this.blackboard.setValue('unitType', this.unitController.unitType);
|
|
|
|
|
|
this.blackboard.setValue('currentHealth', this.unitController.currentHealth);
|
2025-06-24 23:51:59 +08:00
|
|
|
|
this.blackboard.setValue('maxHealth', this.unitController.maxHealth);
|
|
|
|
|
|
this.blackboard.setValue('currentCommand', 'mine');
|
|
|
|
|
|
this.blackboard.setValue('hasOre', false);
|
2025-06-24 19:34:37 +08:00
|
|
|
|
this.blackboard.setValue('hasTarget', false);
|
2025-06-24 23:51:59 +08:00
|
|
|
|
this.blackboard.setValue('targetPosition', null);
|
|
|
|
|
|
this.blackboard.setValue('isMoving', false);
|
2025-06-24 19:34:37 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 更新黑板值
|
|
|
|
|
|
*/
|
|
|
|
|
|
updateBlackboardValue(key: string, value: any) {
|
|
|
|
|
|
if (this.blackboard) {
|
|
|
|
|
|
this.blackboard.setValue(key, value);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取黑板值
|
|
|
|
|
|
*/
|
|
|
|
|
|
getBlackboardValue(key: string): any {
|
|
|
|
|
|
return this.blackboard?.getValue(key);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取黑板
|
|
|
|
|
|
*/
|
|
|
|
|
|
getBlackboard(): Blackboard | null {
|
|
|
|
|
|
return this.blackboard;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取行为树
|
|
|
|
|
|
*/
|
|
|
|
|
|
getBehaviorTree(): BehaviorTree<GameExecutionContext> | null {
|
|
|
|
|
|
return this.behaviorTree;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 更新行为树
|
|
|
|
|
|
*/
|
|
|
|
|
|
update(deltaTime: number) {
|
|
|
|
|
|
if (!this.isLoaded || !this.isRunning || !this.behaviorTree || !this.blackboard) return;
|
|
|
|
|
|
|
|
|
|
|
|
// 控制更新频率
|
|
|
|
|
|
this.lastTickTime += deltaTime;
|
|
|
|
|
|
if (this.lastTickTime < this.tickInterval) return;
|
|
|
|
|
|
|
|
|
|
|
|
this.lastTickTime = 0;
|
|
|
|
|
|
|
2025-06-24 23:51:59 +08:00
|
|
|
|
// 更新矿工状态信息
|
2025-06-24 19:34:37 +08:00
|
|
|
|
if (this.unitController) {
|
|
|
|
|
|
this.blackboard.setValue('currentHealth', this.unitController.currentHealth);
|
|
|
|
|
|
this.blackboard.setValue('currentCommand', this.unitController.currentCommand);
|
2025-06-24 23:51:59 +08:00
|
|
|
|
this.blackboard.setValue('hasTarget', this.unitController.targetPosition && !this.unitController.targetPosition.equals(Vec3.ZERO));
|
2025-06-24 19:34:37 +08:00
|
|
|
|
this.blackboard.setValue('targetPosition', this.unitController.targetPosition);
|
2025-06-24 23:51:59 +08:00
|
|
|
|
this.blackboard.setValue('isMoving', this.unitController.targetPosition && !this.unitController.targetPosition.equals(Vec3.ZERO));
|
2025-06-24 19:34:37 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 执行行为树
|
|
|
|
|
|
try {
|
|
|
|
|
|
this.behaviorTree.tick(deltaTime);
|
|
|
|
|
|
} catch (error) {
|
2025-06-24 23:51:59 +08:00
|
|
|
|
console.error(`行为树执行错误: ${this.node.name}`, error);
|
2025-06-24 19:34:37 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 暂停行为树
|
|
|
|
|
|
*/
|
|
|
|
|
|
pause() {
|
|
|
|
|
|
this.isRunning = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 恢复行为树
|
|
|
|
|
|
*/
|
|
|
|
|
|
resume() {
|
|
|
|
|
|
if (this.isLoaded) {
|
|
|
|
|
|
this.isRunning = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 停止行为树
|
|
|
|
|
|
*/
|
|
|
|
|
|
stop() {
|
|
|
|
|
|
this.isRunning = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 重新加载行为树
|
|
|
|
|
|
*/
|
|
|
|
|
|
async reloadBehaviorTree() {
|
|
|
|
|
|
if (this.currentBehaviorTreeName && this.unitController) {
|
|
|
|
|
|
this.stop();
|
|
|
|
|
|
await this.initializeBehaviorTree(this.currentBehaviorTreeName, this.unitController);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 重置行为树
|
|
|
|
|
|
*/
|
|
|
|
|
|
reset() {
|
|
|
|
|
|
if (this.behaviorTree) {
|
|
|
|
|
|
this.behaviorTree.reset();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
onDestroy() {
|
|
|
|
|
|
this.stop();
|
|
|
|
|
|
this.behaviorTree = null;
|
|
|
|
|
|
this.blackboard = null;
|
|
|
|
|
|
this.context = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|