example update

This commit is contained in:
YHH
2025-06-20 18:21:50 +08:00
parent 48fa547c8f
commit d3fe79cf39
13 changed files with 1611 additions and 217 deletions

View File

@@ -0,0 +1,270 @@
import { _decorator, Component, Node, Label, Button, resources, JsonAsset } from 'cc';
import {
BehaviorTreeBuilder,
BehaviorTree,
Blackboard,
BehaviorTreeJSONConfig
} from '@esengine/ai';
const { ccclass, property } = _decorator;
/**
* 行为树测试示例
* 使用 @esengine/ai 包的 BehaviorTreeBuilder API 自动加载和初始化行为树
*
* 特性:
* - 自动从JSON配置文件加载行为树
* - 自动初始化黑板变量
* - 支持行为树的启动、停止、暂停、恢复
* - 实时显示黑板变量状态
*/
@ccclass('BehaviorTreeExample')
export class BehaviorTreeExample extends Component {
@property(Label)
statusLabel: Label = null;
@property(Label)
logLabel: Label = null;
@property(Button)
startButton: Button = null;
@property(Button)
stopButton: Button = null;
@property(Button)
pauseButton: Button = null;
@property(Button)
resumeButton: Button = null;
private behaviorTree: BehaviorTree<any> = null;
private blackboard: Blackboard = null;
private isRunning: boolean = false;
private isPaused: boolean = false;
private logs: string[] = [];
private executionContext: any = null;
private updateTimer: number = 0;
onLoad() {
this.setupUI();
this.loadBehaviorTree();
}
private setupUI() {
if (this.startButton) {
this.startButton.node.on('click', this.startBehaviorTree, this);
}
if (this.stopButton) {
this.stopButton.node.on('click', this.stopBehaviorTree, this);
}
if (this.pauseButton) {
this.pauseButton.node.on('click', this.pauseBehaviorTree, this);
}
if (this.resumeButton) {
this.resumeButton.node.on('click', this.resumeBehaviorTree, this);
}
this.updateStatus('初始化中...');
this.updateLog('行为树测试组件已加载');
}
private async loadBehaviorTree() {
try {
this.updateStatus('加载行为树配置...');
// 从resources目录加载test.bt.json文件
resources.load('test.bt', JsonAsset, (err, jsonAsset: JsonAsset) => {
if (err) {
console.error('加载行为树配置失败:', err);
this.updateStatus('加载失败: ' + err.message);
this.updateLog('❌ 加载test.bt.json失败: ' + err.message);
return;
}
try {
const config = jsonAsset.json as BehaviorTreeJSONConfig;
this.setupBehaviorTree(config);
} catch (error) {
console.error('解析行为树配置失败:', error);
this.updateStatus('解析失败: ' + error.message);
this.updateLog('❌ 解析行为树配置失败: ' + error.message);
}
});
} catch (error) {
console.error('行为树加载过程出错:', error);
this.updateStatus('加载出错: ' + error.message);
this.updateLog('❌ 行为树加载过程出错: ' + error.message);
}
}
private setupBehaviorTree(config: BehaviorTreeJSONConfig) {
try {
// 创建执行上下文
this.executionContext = {
node: this.node,
component: this,
// 添加日志方法供行为树节点使用
log: (message: string, level: string = 'info') => {
this.updateLog(`🤖 [${level.toUpperCase()}] ${message}`);
}
};
// 🎯 使用 @esengine/ai 的 BehaviorTreeBuilder API - 一行代码完成所有初始化!
const result = BehaviorTreeBuilder.fromBehaviorTreeConfig(config, this.executionContext);
this.behaviorTree = result.tree;
this.blackboard = result.blackboard;
this.executionContext = result.context;
this.updateStatus('行为树加载完成,准备执行');
this.updateLog('✅ 行为树创建成功(使用 @esengine/ai 包)');
this.updateLog(`📊 节点总数: ${config.nodes ? config.nodes.length : 0}`);
this.updateLog(`📋 变量总数: ${config.blackboard ? config.blackboard.length : 0}`);
// 显示黑板变量初始状态
this.logBlackboardStatus();
// 启用开始按钮
if (this.startButton) {
this.startButton.interactable = true;
}
} catch (error) {
console.error('设置行为树失败:', error);
this.updateStatus('设置失败: ' + error.message);
this.updateLog('❌ 行为树设置失败: ' + error.message);
}
}
private startBehaviorTree() {
if (!this.behaviorTree) {
this.updateLog('❌ 行为树未准备好');
return;
}
this.isRunning = true;
this.isPaused = false;
// 重置行为树状态
this.behaviorTree.reset();
this.updateStatus('执行中...');
this.updateLog('🚀 开始执行行为树(自动初始化黑板)');
// 更新按钮状态
if (this.startButton) this.startButton.interactable = false;
if (this.stopButton) this.stopButton.interactable = true;
if (this.pauseButton) this.pauseButton.interactable = true;
if (this.resumeButton) this.resumeButton.interactable = false;
}
private stopBehaviorTree() {
this.isRunning = false;
this.isPaused = false;
this.updateStatus('已停止');
this.updateLog('⏹️ 行为树执行已停止');
// 重置行为树状态
if (this.behaviorTree) {
this.behaviorTree.reset();
}
// 更新按钮状态
if (this.startButton) this.startButton.interactable = true;
if (this.stopButton) this.stopButton.interactable = false;
if (this.pauseButton) this.pauseButton.interactable = false;
if (this.resumeButton) this.resumeButton.interactable = false;
}
private pauseBehaviorTree() {
this.isPaused = true;
this.updateStatus('已暂停');
this.updateLog('⏸️ 行为树执行已暂停');
// 更新按钮状态
if (this.pauseButton) this.pauseButton.interactable = false;
if (this.resumeButton) this.resumeButton.interactable = true;
}
private resumeBehaviorTree() {
this.isPaused = false;
this.updateStatus('执行中...');
this.updateLog('▶️ 行为树执行已恢复');
// 更新按钮状态
if (this.pauseButton) this.pauseButton.interactable = true;
if (this.resumeButton) this.resumeButton.interactable = false;
}
update(deltaTime: number) {
if (!this.isRunning || this.isPaused || !this.behaviorTree) {
return;
}
this.updateTimer += deltaTime;
// 每帧执行行为树
try {
this.behaviorTree.tick(deltaTime);
// 每2秒输出一次状态信息
if (this.updateTimer >= 2.0) {
this.updateTimer = 0;
this.logBlackboardStatus();
// 检查行为树是否处于活动状态
const isActive = this.behaviorTree.isActive();
if (!isActive) {
this.updateLog('✅ 行为树执行完成');
// 注意:这里只是演示,实际上行为树会持续运行
// 如果需要检查完成状态,需要通过黑板变量或其他逻辑判断
}
}
} catch (error) {
console.error('行为树执行出错:', error);
this.updateLog('❌ 执行出错: ' + error.message);
this.stopBehaviorTree();
}
}
private logBlackboardStatus() {
if (!this.blackboard) return;
// 获取所有黑板变量的当前值
const variables = ['state', 'lastAction', 'defendActive', 'health', 'energy'];
const status = variables.map(name => {
const value = this.blackboard.getValue(name, 'undefined');
return `${name}:${value}`;
}).join(', ');
this.updateLog(`📊 状态: ${status}`);
}
private updateStatus(status: string) {
if (this.statusLabel) {
this.statusLabel.string = status;
}
console.log('[BehaviorTree] 状态:', status);
}
private updateLog(message: string) {
this.logs.push(`[${new Date().toLocaleTimeString()}] ${message}`);
// 只保留最新的25条日志
if (this.logs.length > 25) {
this.logs.shift();
}
if (this.logLabel) {
this.logLabel.string = this.logs.join('\n');
}
console.log('[BehaviorTree]', message);
}
onDestroy() {
this.stopBehaviorTree();
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "3c3bf485-e1ab-44a4-8758-6594edc4c575",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,202 @@
import { _decorator, Component, Node, Label, Button, Canvas, UITransform, Widget, Layout, Sprite, Color } from 'cc';
import { BehaviorTreeExample } from './BehaviorTreeExample';
const { ccclass, property } = _decorator;
/**
* 行为树测试场景
* 自动创建UI界面用于测试行为树
*/
@ccclass('BehaviorTreeTestScene')
export class BehaviorTreeTestScene extends Component {
onLoad() {
this.createTestUI();
}
private createTestUI() {
// 创建Canvas
const canvasNode = new Node('BehaviorTreeTestCanvas');
canvasNode.addComponent(Canvas);
canvasNode.addComponent(UITransform);
canvasNode.parent = this.node;
// 创建背景
const backgroundNode = new Node('Background');
const backgroundTransform = backgroundNode.addComponent(UITransform);
backgroundTransform.setContentSize(1920, 1080);
const backgroundSprite = backgroundNode.addComponent(Sprite);
backgroundSprite.color = new Color(40, 40, 40, 255);
backgroundNode.parent = canvasNode;
// 设置Widget组件使背景充满整个画布
const backgroundWidget = backgroundNode.addComponent(Widget);
backgroundWidget.isAlignTop = true;
backgroundWidget.isAlignBottom = true;
backgroundWidget.isAlignLeft = true;
backgroundWidget.isAlignRight = true;
// 创建主容器
const mainContainer = new Node('MainContainer');
const mainTransform = mainContainer.addComponent(UITransform);
mainTransform.setContentSize(1600, 900);
mainContainer.parent = canvasNode;
// 设置主容器居中
const mainWidget = mainContainer.addComponent(Widget);
mainWidget.isAlignHorizontalCenter = true;
mainWidget.isAlignVerticalCenter = true;
// 设置主容器的Layout
const mainLayout = mainContainer.addComponent(Layout);
mainLayout.type = Layout.Type.VERTICAL;
mainLayout.spacingY = 20;
mainLayout.paddingTop = 50;
mainLayout.paddingBottom = 50;
mainLayout.paddingLeft = 50;
mainLayout.paddingRight = 50;
// 创建标题
const titleNode = this.createText('简化API行为树测试器', 48, Color.WHITE);
titleNode.parent = mainContainer;
// 创建状态显示区域
const statusContainer = new Node('StatusContainer');
const statusTransform = statusContainer.addComponent(UITransform);
statusTransform.setContentSize(1500, 80);
statusContainer.parent = mainContainer;
const statusLayout = statusContainer.addComponent(Layout);
statusLayout.type = Layout.Type.HORIZONTAL;
statusLayout.spacingX = 20;
// 状态标签
const statusLabelNode = this.createText('状态: 初始化中...', 24, Color.YELLOW);
statusLabelNode.parent = statusContainer;
// 创建控制按钮区域
const buttonContainer = new Node('ButtonContainer');
const buttonTransform = buttonContainer.addComponent(UITransform);
buttonTransform.setContentSize(1500, 80);
buttonContainer.parent = mainContainer;
const buttonLayout = buttonContainer.addComponent(Layout);
buttonLayout.type = Layout.Type.HORIZONTAL;
buttonLayout.spacingX = 20;
// 创建控制按钮
const startButton = this.createButton('开始执行', Color.GREEN);
const stopButton = this.createButton('停止', Color.RED);
const pauseButton = this.createButton('暂停', Color.YELLOW);
const resumeButton = this.createButton('恢复', Color.BLUE);
startButton.parent = buttonContainer;
stopButton.parent = buttonContainer;
pauseButton.parent = buttonContainer;
resumeButton.parent = buttonContainer;
// 创建说明区域
const infoContainer = new Node('InfoContainer');
const infoTransform = infoContainer.addComponent(UITransform);
infoTransform.setContentSize(1500, 60);
infoContainer.parent = mainContainer;
const infoNode = this.createText('使用新的简化API - 一行代码完成所有初始化!', 18, Color.CYAN);
infoNode.parent = infoContainer;
// 创建日志显示区域
const logContainer = new Node('LogContainer');
const logTransform = logContainer.addComponent(UITransform);
logTransform.setContentSize(1500, 600);
logContainer.parent = mainContainer;
// 日志背景
const logBackground = new Node('LogBackground');
const logBgTransform = logBackground.addComponent(UITransform);
logBgTransform.setContentSize(1500, 600);
const logBgSprite = logBackground.addComponent(Sprite);
logBgSprite.color = new Color(20, 20, 20, 255);
logBackground.parent = logContainer;
// 日志文本
const logLabelNode = this.createText('等待行为树配置加载...', 16, Color.CYAN);
const logLabel = logLabelNode.getComponent(Label);
logLabel.overflow = Label.Overflow.RESIZE_HEIGHT;
logLabel.verticalAlign = Label.VerticalAlign.TOP;
logLabel.horizontalAlign = Label.HorizontalAlign.LEFT;
const logTransformComp = logLabelNode.getComponent(UITransform);
logTransformComp.setContentSize(1450, 550);
logLabelNode.parent = logContainer;
// 设置日志文本位置
const logWidget = logLabelNode.addComponent(Widget);
logWidget.isAlignTop = true;
logWidget.isAlignLeft = true;
logWidget.top = 25;
logWidget.left = 25;
// 添加BehaviorTreeExample组件
const behaviorTreeExample = this.node.addComponent(BehaviorTreeExample);
behaviorTreeExample.statusLabel = statusLabelNode.getComponent(Label);
behaviorTreeExample.logLabel = logLabel;
behaviorTreeExample.startButton = startButton.getComponent(Button);
behaviorTreeExample.stopButton = stopButton.getComponent(Button);
behaviorTreeExample.pauseButton = pauseButton.getComponent(Button);
behaviorTreeExample.resumeButton = resumeButton.getComponent(Button);
// 初始化按钮状态
stopButton.getComponent(Button).interactable = false;
pauseButton.getComponent(Button).interactable = false;
resumeButton.getComponent(Button).interactable = false;
startButton.getComponent(Button).interactable = false; // 等待行为树配置加载完成
console.log('行为树测试UI创建完成');
}
private createText(text: string, fontSize: number, color: Color): Node {
const textNode = new Node('Text');
const textTransform = textNode.addComponent(UITransform);
const label = textNode.addComponent(Label);
label.string = text;
label.fontSize = fontSize;
label.color = color;
label.lineHeight = fontSize + 4;
// 根据文本内容调整大小
const estimatedWidth = Math.max(200, text.length * fontSize * 0.6);
textTransform.setContentSize(estimatedWidth, fontSize + 10);
return textNode;
}
private createButton(text: string, color: Color): Node {
const buttonNode = new Node('Button');
const buttonTransform = buttonNode.addComponent(UITransform);
buttonTransform.setContentSize(180, 60);
// 按钮背景
const buttonSprite = buttonNode.addComponent(Sprite);
buttonSprite.color = color;
// 按钮组件
const button = buttonNode.addComponent(Button);
button.target = buttonNode;
// 按钮文本
const labelNode = new Node('Label');
const labelTransform = labelNode.addComponent(UITransform);
labelTransform.setContentSize(170, 50);
const label = labelNode.addComponent(Label);
label.string = text;
label.fontSize = 20;
label.color = Color.WHITE;
label.horizontalAlign = Label.HorizontalAlign.CENTER;
label.verticalAlign = Label.VerticalAlign.CENTER;
labelNode.parent = buttonNode;
return buttonNode;
}
}

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "0c14be41-df1d-4bdc-88fd-b2e504ecdee6",
"files": [],
"subMetas": {},
"userData": {}
}