example update
This commit is contained in:
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -7,3 +7,6 @@
|
|||||||
[submodule "extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension"]
|
[submodule "extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension"]
|
||||||
path = extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension
|
path = extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension
|
||||||
url = https://github.com/esengine/cocos-ecs-extension.git
|
url = https://github.com/esengine/cocos-ecs-extension.git
|
||||||
|
[submodule "extensions/cocos/cocos-ecs/extensions/behaviour-tree"]
|
||||||
|
path = extensions/cocos/cocos-ecs/extensions/behaviour-tree
|
||||||
|
url = https://github.com/esengine/behaviour-tree.git
|
||||||
|
|||||||
@@ -744,7 +744,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"name": "behavior-tree-examples-guide.bt",
|
"name": "behavior-tree-examples-guide",
|
||||||
"created": "2025-06-19T04:28:44.589Z",
|
"created": "2025-06-19T04:28:44.589Z",
|
||||||
"version": "1.0"
|
"version": "1.0"
|
||||||
},
|
},
|
||||||
|
|||||||
283
extensions/cocos/cocos-ecs/assets/resources/test.bt.json
Normal file
283
extensions/cocos/cocos-ecs/assets/resources/test.bt.json
Normal file
@@ -0,0 +1,283 @@
|
|||||||
|
{
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": "root_1",
|
||||||
|
"type": "root",
|
||||||
|
"name": "行为树指南根",
|
||||||
|
"children": [
|
||||||
|
"selector_main"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "selector_main",
|
||||||
|
"type": "selector",
|
||||||
|
"name": "主选择器",
|
||||||
|
"properties": {
|
||||||
|
"abortType": "LowerPriority"
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
"repeater_patrol",
|
||||||
|
"selector_combat",
|
||||||
|
"sequence_idle"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "repeater_patrol",
|
||||||
|
"type": "repeater",
|
||||||
|
"name": "巡逻重复器",
|
||||||
|
"properties": {
|
||||||
|
"count": -1,
|
||||||
|
"continueOnFailure": true,
|
||||||
|
"delayBetween": false
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
"sequence_patrol"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "sequence_patrol",
|
||||||
|
"type": "sequence",
|
||||||
|
"name": "巡逻序列",
|
||||||
|
"properties": {
|
||||||
|
"abortType": "None"
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
"decorator_patrol_check",
|
||||||
|
"action_patrol"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "decorator_patrol_check",
|
||||||
|
"type": "conditional-decorator",
|
||||||
|
"name": "巡逻条件检查",
|
||||||
|
"properties": {
|
||||||
|
"conditionType": "custom",
|
||||||
|
"executeWhenTrue": true,
|
||||||
|
"executeWhenFalse": false,
|
||||||
|
"checkInterval": 1
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
"log_patrolling"
|
||||||
|
],
|
||||||
|
"condition": {
|
||||||
|
"type": "condition-custom",
|
||||||
|
"properties": {
|
||||||
|
"conditionCode": {
|
||||||
|
"name": "条件代码",
|
||||||
|
"type": "code",
|
||||||
|
"value": "(context) => {\n // 检查是否处于巡逻状态\n return context.blackboard && context.blackboard.getValue('state') === 'patrol';\n}",
|
||||||
|
"description": "条件判断函数代码",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"conditionName": {
|
||||||
|
"name": "条件名称",
|
||||||
|
"type": "string",
|
||||||
|
"value": "巡逻状态检查",
|
||||||
|
"description": "用于调试的条件名称",
|
||||||
|
"required": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "log_patrolling",
|
||||||
|
"type": "log-action",
|
||||||
|
"name": "记录巡逻",
|
||||||
|
"properties": {
|
||||||
|
"message": "正在执行巡逻任务,当前状态: {{state}}",
|
||||||
|
"logLevel": "info"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "action_patrol",
|
||||||
|
"type": "set-blackboard-value",
|
||||||
|
"name": "执行巡逻",
|
||||||
|
"properties": {
|
||||||
|
"variableName": "lastAction",
|
||||||
|
"value": "{{state}}_执行中",
|
||||||
|
"force": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "selector_combat",
|
||||||
|
"type": "selector",
|
||||||
|
"name": "战斗选择器",
|
||||||
|
"properties": {
|
||||||
|
"abortType": "None"
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
"sequence_attack",
|
||||||
|
"sequence_defend"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "sequence_attack",
|
||||||
|
"type": "sequence",
|
||||||
|
"name": "攻击序列",
|
||||||
|
"properties": {
|
||||||
|
"abortType": "Self"
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
"inverter_enemy",
|
||||||
|
"action_attack"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "inverter_enemy",
|
||||||
|
"type": "inverter",
|
||||||
|
"name": "敌人检查反转",
|
||||||
|
"children": [
|
||||||
|
"condition_enemy"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "condition_enemy",
|
||||||
|
"type": "condition-random",
|
||||||
|
"name": "随机敌人出现",
|
||||||
|
"properties": {
|
||||||
|
"successProbability": 0.3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "action_attack",
|
||||||
|
"type": "log-action",
|
||||||
|
"name": "攻击动作",
|
||||||
|
"properties": {
|
||||||
|
"message": "发动攻击!生命值: {{health}}, 能量: {{energy}}",
|
||||||
|
"logLevel": "warn"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "sequence_defend",
|
||||||
|
"type": "sequence",
|
||||||
|
"name": "防御序列",
|
||||||
|
"properties": {
|
||||||
|
"abortType": "None"
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
"wait_defend",
|
||||||
|
"action_defend"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "wait_defend",
|
||||||
|
"type": "wait-action",
|
||||||
|
"name": "防御准备",
|
||||||
|
"properties": {
|
||||||
|
"waitTime": 0.5,
|
||||||
|
"useExternalTime": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "action_defend",
|
||||||
|
"type": "execute-action",
|
||||||
|
"name": "执行防御",
|
||||||
|
"properties": {
|
||||||
|
"actionCode": "(context) => {\n // 防御逻辑\n console.log('开始防御姿态');\n if(context.blackboard) {\n context.blackboard.setValue('defendActive', true);\n context.blackboard.setValue('lastAction', '防御中');\n }\n return 'success';\n}",
|
||||||
|
"actionName": "防御动作_生命值{{health}}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "sequence_idle",
|
||||||
|
"type": "sequence",
|
||||||
|
"name": "闲置序列",
|
||||||
|
"properties": {
|
||||||
|
"abortType": "None"
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
"action_idle",
|
||||||
|
"log_status",
|
||||||
|
"wait_idle"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "action_idle",
|
||||||
|
"type": "set-blackboard-value",
|
||||||
|
"name": "设置闲置",
|
||||||
|
"properties": {
|
||||||
|
"variableName": "state",
|
||||||
|
"value": "idle",
|
||||||
|
"force": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "log_status",
|
||||||
|
"type": "log-action",
|
||||||
|
"name": "状态报告",
|
||||||
|
"properties": {
|
||||||
|
"message": "状态报告 - 当前: {{state}}, 上次动作: {{lastAction}}, 防御中: {{defendActive}}",
|
||||||
|
"logLevel": "debug"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "wait_idle",
|
||||||
|
"type": "wait-action",
|
||||||
|
"name": "闲置等待",
|
||||||
|
"properties": {
|
||||||
|
"waitTime": 3,
|
||||||
|
"useExternalTime": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"blackboard": [
|
||||||
|
{
|
||||||
|
"name": "state",
|
||||||
|
"type": "string",
|
||||||
|
"value": "patrol",
|
||||||
|
"description": "当前状态",
|
||||||
|
"group": "核心状态",
|
||||||
|
"constraints": {
|
||||||
|
"allowedValues": [
|
||||||
|
"idle",
|
||||||
|
"patrol",
|
||||||
|
"combat",
|
||||||
|
"defend"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "lastAction",
|
||||||
|
"type": "string",
|
||||||
|
"value": "",
|
||||||
|
"description": "最后执行的动作",
|
||||||
|
"group": "核心状态"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "defendActive",
|
||||||
|
"type": "boolean",
|
||||||
|
"value": false,
|
||||||
|
"description": "是否正在防御",
|
||||||
|
"group": "战斗状态"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "health",
|
||||||
|
"type": "number",
|
||||||
|
"value": 100,
|
||||||
|
"description": "生命值",
|
||||||
|
"group": "属性",
|
||||||
|
"constraints": {
|
||||||
|
"min": 0,
|
||||||
|
"max": 100,
|
||||||
|
"step": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "energy",
|
||||||
|
"type": "number",
|
||||||
|
"value": 50,
|
||||||
|
"description": "能量值",
|
||||||
|
"group": "属性",
|
||||||
|
"constraints": {
|
||||||
|
"min": 0,
|
||||||
|
"max": 100,
|
||||||
|
"step": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"name": "behavior-tree",
|
||||||
|
"created": "2025-06-20T07:17:15.437Z",
|
||||||
|
"version": "1.0",
|
||||||
|
"exportType": "clean"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"ver": "2.0.1",
|
||||||
|
"importer": "json",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "4d7244e6-4be6-46a0-9e38-6915004177e7",
|
||||||
|
"files": [
|
||||||
|
".json"
|
||||||
|
],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "4.0.24",
|
||||||
|
"importer": "typescript",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "3c3bf485-e1ab-44a4-8758-6594edc4c575",
|
||||||
|
"files": [],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"ver": "4.0.24",
|
||||||
|
"importer": "typescript",
|
||||||
|
"imported": true,
|
||||||
|
"uuid": "0c14be41-df1d-4bdc-88fd-b2e504ecdee6",
|
||||||
|
"files": [],
|
||||||
|
"subMetas": {},
|
||||||
|
"userData": {}
|
||||||
|
}
|
||||||
Submodule extensions/cocos/cocos-ecs/extensions/behaviour-tree added at 25598129b2
Submodule extensions/cocos/cocos-ecs/extensions/cocos-ecs-extension updated: 54233b9773...b6a2ee1e2e
20
extensions/cocos/cocos-ecs/package-lock.json
generated
20
extensions/cocos/cocos-ecs/package-lock.json
generated
@@ -6,23 +6,25 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "cocos-ecs",
|
"name": "cocos-ecs",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@esengine/ai": "^2.0.1",
|
"@esengine/ai": "^2.0.6",
|
||||||
"@esengine/ecs-framework": "^2.1.19"
|
"@esengine/ecs-framework": "^2.1.22"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esengine/ai": {
|
"node_modules/@esengine/ai": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/@esengine/ai/-/ai-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esengine/ai/-/ai-2.0.6.tgz",
|
||||||
"integrity": "sha512-qGGYc4kYlSJzCkBDJa+p5OruOnDvnL2oJ/ciKSHsPJVdn1tIefPEkUofJyMVGo4my5ubGr2ky6igTLtLYmhzRg==",
|
"integrity": "sha512-IPP9jh6GL6MG5Hh5gL1OnkfC1J2Eu1V2KXYCov3kMj/4gUdmehm3Ga0sPSlyctBUwQkYVFr3A7G0QUY+Ts4y6Q==",
|
||||||
"license": "MIT",
|
"dependencies": {
|
||||||
|
"@esengine/ecs-framework": "^2.1.20"
|
||||||
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16.0.0"
|
"node": ">=16.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esengine/ecs-framework": {
|
"node_modules/@esengine/ecs-framework": {
|
||||||
"version": "2.1.20",
|
"version": "2.1.22",
|
||||||
"resolved": "https://registry.npmjs.org/@esengine/ecs-framework/-/ecs-framework-2.1.20.tgz",
|
"resolved": "https://registry.npmjs.org/@esengine/ecs-framework/-/ecs-framework-2.1.22.tgz",
|
||||||
"integrity": "sha512-L6PJ1uoXIttzjYt66shZPdmJ4lU38SyqRnEcVJexcR+V6iyQ/U7H8yjvQjeTIlBb0u7JDBEC6a8B4Cr6VlsF9Q==",
|
"integrity": "sha512-U5zQJJC7eQeiIUMV1Y784rOJoVK9x8ZLDI65+8Bbt3PcBWzn+qspLe4FBlJU4XBZcMbmEo5vJltvRNkN7iVPZA==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16.0.0"
|
"node": ">=16.0.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"version": "3.8.6"
|
"version": "3.8.6"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@esengine/ai": "^2.0.1",
|
"@esengine/ecs-framework": "^2.1.22",
|
||||||
"@esengine/ecs-framework": "^2.1.19"
|
"@esengine/ai": "^2.0.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user