This commit is contained in:
YHH
2025-06-23 16:08:40 +08:00
12 changed files with 1967 additions and 272 deletions

Binary file not shown.

View File

@@ -133,7 +133,65 @@ class GameLogicSystem extends ProcessingSystem {
const gameTime = Time.totalTime;
console.log(`游戏时间: ${gameTime.toFixed(1)}`);
}
private triggerVictory() {
console.log("游戏胜利!");
// 处理胜利逻辑
}
private triggerGameOver() {
console.log("游戏结束!");
// 处理游戏结束逻辑
}
}
```
**适用场景:**
- 全局游戏逻辑系统
- 胜负判断系统
- UI更新系统
- 不依赖特定实体的处理
## AI系统示例
下面是一个完整的AI系统示例展示EntitySystem的典型用法
```typescript
import { EntitySystem, Matcher, Entity } from '@esengine/ecs-framework';
enum AIState {
IDLE,
PATROL,
CHASE,
ATTACK
}
class AISystem extends EntitySystem {
constructor() {
// 匹配所有有AI组件和位置组件的实体
super(Matcher.empty().all(AIComponent, PositionComponent));
}
// 处理每个匹配的实体
public processEntity(entity: Entity) {
const ai = entity.getComponent(AIComponent);
const position = entity.getComponent(PositionComponent);
switch (ai.state) {
case AIState.IDLE:
this.processIdle(entity, ai);
break;
case AIState.PATROL:
this.processPatrol(entity, ai, position);
break;
case AIState.CHASE:
this.processChase(entity, ai, position);
break;
case AIState.ATTACK:
this.processAttack(entity, ai);
break;
}
}
private processIdle(entity: Entity, ai: AIComponent) {
ai.idleTimer += Time.deltaTime;

View File

@@ -0,0 +1,293 @@
import { _decorator, Component, Node, Label } from 'cc';
import {
BehaviorTreeBuilder,
BehaviorTree,
Blackboard
} from '@esengine/ai';
const { ccclass, property } = _decorator;
/**
* 行为树示例用法
* 展示如何在Cocos Creator中加载并执行行为树
*/
@ccclass('BehaviorTreeExampleUsage')
export class BehaviorTreeExampleUsage extends Component {
@property(Label)
statusLabel: Label = null;
@property(Label)
logLabel: Label = null;
private behaviorTree: BehaviorTree<any> = null;
private blackboard: Blackboard = null;
private isRunning: boolean = false;
private logs: string[] = [];
private executionContext: any = null;
onLoad() {
this.setupBehaviorTree();
}
private setupBehaviorTree() {
try {
// 行为树配置通常从JSON文件加载
const behaviorTreeConfig = {
"nodes": [
{
"id": "root_1",
"type": "root",
"name": "AI智能体行为树",
"children": ["selector_main"]
},
{
"id": "selector_main",
"type": "selector",
"name": "主选择器",
"properties": {
"abortType": "LowerPriority"
},
"children": ["sequence_combat", "sequence_patrol", "sequence_idle"]
},
{
"id": "sequence_combat",
"type": "sequence",
"name": "战斗序列",
"children": ["condition_enemy", "action_attack"]
},
{
"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_patrol",
"type": "sequence",
"name": "巡逻序列",
"children": ["action_move", "wait_patrol"]
},
{
"id": "action_move",
"type": "set-blackboard-value",
"name": "移动巡逻",
"properties": {
"variableName": "lastAction",
"value": "巡逻中"
}
},
{
"id": "wait_patrol",
"type": "wait-action",
"name": "巡逻等待",
"properties": {
"waitTime": 2
}
},
{
"id": "sequence_idle",
"type": "sequence",
"name": "闲置序列",
"children": ["action_idle", "wait_idle"]
},
{
"id": "action_idle",
"type": "log-action",
"name": "闲置状态",
"properties": {
"message": "当前状态: 闲置中,生命值: {{health}}"
}
},
{
"id": "wait_idle",
"type": "wait-action",
"name": "闲置等待",
"properties": {
"waitTime": 1
}
}
],
"blackboard": [
{
"name": "health",
"type": "number",
"value": 100,
"description": "角色生命值"
},
{
"name": "energy",
"type": "number",
"value": 80,
"description": "角色能量值"
},
{
"name": "lastAction",
"type": "string",
"value": "待机",
"description": "最后执行的动作"
},
{
"name": "enemyDetected",
"type": "boolean",
"value": false,
"description": "是否检测到敌人"
}
]
};
// 创建执行上下文
this.executionContext = {
node: this.node,
component: this,
// 添加日志方法供行为树节点使用
log: (message: string, level: string = 'info') => {
this.addLog(`🤖 [${level.toUpperCase()}] ${message}`);
}
};
// 🎯 使用 @esengine/ai 的 BehaviorTreeBuilder API - 一行代码完成所有初始化!
const result = BehaviorTreeBuilder.fromBehaviorTreeConfig(behaviorTreeConfig, this.executionContext);
this.behaviorTree = result.tree;
this.blackboard = result.blackboard;
this.executionContext = result.context;
this.updateStatus('行为树加载完成');
this.addLog('✅ 行为树初始化成功');
this.addLog(`📊 节点总数: ${behaviorTreeConfig.nodes.length}`);
this.addLog(`📋 变量总数: ${behaviorTreeConfig.blackboard.length}`);
// 自动开始执行
this.startBehaviorTree();
} catch (error) {
console.error('行为树设置失败:', error);
this.updateStatus('设置失败: ' + error.message);
this.addLog('❌ 行为树设置失败: ' + error.message);
}
}
private startBehaviorTree() {
this.isRunning = true;
this.behaviorTree.reset();
this.updateStatus('执行中...');
this.addLog('🚀 开始执行行为树');
}
private stopBehaviorTree() {
this.isRunning = false;
this.updateStatus('已停止');
this.addLog('⏹️ 行为树执行已停止');
if (this.behaviorTree) {
this.behaviorTree.reset();
}
}
update(deltaTime: number) {
if (!this.isRunning || !this.behaviorTree) {
return;
}
try {
// 每帧执行行为树
this.behaviorTree.tick(deltaTime);
} catch (error) {
console.error('行为树执行出错:', error);
this.addLog('❌ 执行出错: ' + error.message);
this.stopBehaviorTree();
}
}
private updateStatus(status: string) {
if (this.statusLabel) {
this.statusLabel.string = status;
}
console.log('[BehaviorTree] 状态:', status);
}
private addLog(message: string) {
this.logs.push(`[${new Date().toLocaleTimeString()}] ${message}`);
// 只保留最新的20条日志
if (this.logs.length > 20) {
this.logs.shift();
}
if (this.logLabel) {
this.logLabel.string = this.logs.join('\n');
}
console.log('[BehaviorTree]', message);
}
// 手动控制方法可以绑定到UI按钮
onStartButtonClick() {
if (!this.isRunning) {
this.startBehaviorTree();
}
}
onStopButtonClick() {
if (this.isRunning) {
this.stopBehaviorTree();
}
}
// 修改黑板变量的示例方法
onModifyHealthClick() {
if (this.blackboard) {
const currentHealth = this.blackboard.getValue('health', 100);
const newHealth = Math.max(0, currentHealth - 10);
this.blackboard.setValue('health', newHealth);
this.addLog(`🩺 生命值变更: ${currentHealth} -> ${newHealth}`);
}
}
onModifyEnergyClick() {
if (this.blackboard) {
const currentEnergy = this.blackboard.getValue('energy', 80);
const newEnergy = Math.max(0, currentEnergy - 5);
this.blackboard.setValue('energy', newEnergy);
this.addLog(`⚡ 能量变更: ${currentEnergy} -> ${newEnergy}`);
}
}
onDestroy() {
this.stopBehaviorTree();
}
}
/**
* 使用说明:
*
* 1. 安装依赖:
* npm install @esengine/ai
*
* 2. 将此脚本挂载到场景中的节点上
*
* 3. 在属性检查器中设置:
* - statusLabel: 用于显示当前状态的Label组件
* - logLabel: 用于显示日志信息的Label组件
*
* 4. 运行场景,观察行为树的执行效果
*
* 5. 可以添加按钮并绑定控制方法:
* - onStartButtonClick(): 开始执行
* - onStopButtonClick(): 停止执行
* - onModifyHealthClick(): 修改生命值
* - onModifyEnergyClick(): 修改能量值
*/

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "9013db83-08a6-4613-8911-016bfdd65239",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -0,0 +1,314 @@
import { _decorator, Component, Node, Label, Button } from 'cc';
import {
BehaviorTreeBuilder,
BehaviorTree,
Blackboard,
BehaviorTreeJSONConfig
} from '@esengine/ai';
const { ccclass, property } = _decorator;
/**
* 完整行为树指南使用示例
*
* 此示例展示了高级行为树特性:
* 1. 条件装饰器 (Conditional Decorator)
* 2. 重复器 (Repeater)
* 3. 反转器 (Inverter)
* 4. 黑板变量引用 {{variable}}
* 5. 复杂的组合行为逻辑
* 6. 自定义条件和动作
*
* 包含三种行为模式:
* - 巡逻模式:重复执行巡逻序列
* - 战斗模式:检测敌人并攻击/防御
* - 闲置模式:状态报告和等待
*/
@ccclass('BehaviorTreeGuideUsage')
export class BehaviorTreeGuideUsage extends Component {
@property(Label)
statusLabel: Label = null;
@property(Label)
logLabel: Label = null;
@property(Button)
controlButton: Button = null;
@property(Button)
switchStateButton: Button = null;
private behaviorTree: BehaviorTree<any> = null;
private blackboard: Blackboard = null;
private isRunning: boolean = false;
private logs: string[] = [];
private currentStateIndex: number = 0;
private states = ['patrol', 'combat', 'idle'];
onLoad() {
this.setupUI();
this.initializeBehaviorTree();
}
private setupUI() {
if (this.controlButton) {
this.controlButton.node.on('click', this.toggleExecution, this);
}
if (this.switchStateButton) {
this.switchStateButton.node.on('click', this.switchState, this);
}
this.updateStatus('初始化完整行为树指南...');
this.addLog('🎓 行为树指南示例已加载');
}
private initializeBehaviorTree() {
try {
// 这里应该从JSON文件加载完整的行为树配置
// 为了演示,我们使用简化版本
const config: BehaviorTreeJSONConfig = {
nodes: [
{
id: "root_1",
type: "root",
name: "行为树指南根",
children: ["selector_main"]
},
{
id: "selector_main",
type: "selector",
name: "主选择器",
properties: { abortType: "LowerPriority" },
children: ["sequence_combat", "sequence_patrol", "sequence_idle"]
},
{
id: "sequence_combat",
type: "sequence",
name: "战斗序列",
children: ["condition_enemy", "action_attack"]
},
{
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_patrol",
type: "sequence",
name: "巡逻序列",
children: ["action_patrol", "wait_patrol"]
},
{
id: "action_patrol",
type: "set-blackboard-value",
name: "执行巡逻",
properties: {
variableName: "lastAction",
value: "{{state}}_执行中"
}
},
{
id: "wait_patrol",
type: "wait-action",
name: "巡逻等待",
properties: { waitTime: 1 }
},
{
id: "sequence_idle",
type: "sequence",
name: "闲置序列",
children: ["action_idle", "wait_idle"]
},
{
id: "action_idle",
type: "log-action",
name: "状态报告",
properties: {
message: "状态报告 - 当前: {{state}}, 上次动作: {{lastAction}}"
}
},
{
id: "wait_idle",
type: "wait-action",
name: "闲置等待",
properties: { waitTime: 2 }
}
],
blackboard: [
{
name: "state",
type: "string",
value: "patrol",
description: "当前状态"
},
{
name: "lastAction",
type: "string",
value: "",
description: "最后执行的动作"
},
{
name: "health",
type: "number",
value: 100,
description: "生命值"
},
{
name: "energy",
type: "number",
value: 50,
description: "能量值"
}
]
};
// 创建执行上下文
const executionContext = {
node: this.node,
component: this,
log: (message: string, level: string = 'info') => {
this.addLog(`🤖 [${level.toUpperCase()}] ${message}`);
}
};
// 🎯 使用 BehaviorTreeBuilder 一键创建
const result = BehaviorTreeBuilder.fromBehaviorTreeConfig(config, executionContext);
this.behaviorTree = result.tree;
this.blackboard = result.blackboard;
this.updateStatus('完整行为树指南已准备就绪');
this.addLog('✅ 行为树创建成功(高级特性版本)');
this.addLog(`📊 包含 ${config.nodes.length} 个节点`);
this.addLog(`📋 包含 ${config.blackboard.length} 个黑板变量`);
// 显示初始状态
this.logBlackboardStatus();
} catch (error) {
console.error('初始化行为树失败:', error);
this.updateStatus('初始化失败: ' + error.message);
this.addLog('❌ 行为树初始化失败: ' + error.message);
}
}
private toggleExecution() {
if (!this.behaviorTree) {
this.addLog('❌ 行为树未准备好');
return;
}
this.isRunning = !this.isRunning;
if (this.isRunning) {
this.behaviorTree.reset();
this.updateStatus('执行中...');
this.addLog('🚀 开始执行完整行为树指南');
if (this.controlButton) {
this.controlButton.getComponentInChildren(Label).string = '停止';
}
} else {
this.updateStatus('已停止');
this.addLog('⏹️ 行为树执行已停止');
if (this.controlButton) {
this.controlButton.getComponentInChildren(Label).string = '开始';
}
}
}
private switchState() {
if (!this.blackboard) return;
this.currentStateIndex = (this.currentStateIndex + 1) % this.states.length;
const newState = this.states[this.currentStateIndex];
this.blackboard.setValue('state', newState);
this.addLog(`🔄 切换状态到: ${newState}`);
this.logBlackboardStatus();
}
update(deltaTime: number) {
if (!this.isRunning || !this.behaviorTree) return;
try {
this.behaviorTree.tick(deltaTime);
} catch (error) {
console.error('行为树执行出错:', error);
this.addLog('❌ 执行出错: ' + error.message);
this.isRunning = false;
}
}
private logBlackboardStatus() {
if (!this.blackboard) return;
const variables = ['state', 'lastAction', 'health', 'energy'];
const status = variables.map(name => {
const value = this.blackboard.getValue(name, 'undefined');
return `${name}:${value}`;
}).join(', ');
this.addLog(`📊 黑板状态: ${status}`);
}
private updateStatus(status: string) {
if (this.statusLabel) {
this.statusLabel.string = status;
}
}
private addLog(message: string) {
this.logs.push(`[${new Date().toLocaleTimeString()}] ${message}`);
// 只保留最新的20条日志
if (this.logs.length > 20) {
this.logs.shift();
}
if (this.logLabel) {
this.logLabel.string = this.logs.join('\n');
}
console.log('[BehaviorTreeGuide]', message);
}
onDestroy() {
this.isRunning = false;
}
}
/*
* 完整指南特色功能说明:
*
* 1. 高级节点类型:
* - Repeater: 无限重复巡逻行为
* - Conditional Decorator: 带条件的装饰器
* - Inverter: 反转子节点结果
*
* 2. 黑板变量引用:
* - {{state}}: 动态引用当前状态
* - {{health}}: 显示生命值
* - {{lastAction}}: 跟踪最后动作
*
* 3. 复杂行为逻辑:
* - 巡逻:重复执行,状态检查
* - 战斗:敌人检测,攻击防御
* - 闲置:状态报告,定时等待
*
* 4. 交互功能:
* - 状态切换按钮
* - 开始/停止控制
* - 实时日志显示
* - 黑板变量监控
*/

View File

@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "0403f4d3-2985-4452-928e-d0535cea8155",
"files": [],
"subMetas": {},
"userData": {}
}

View File

@@ -620,132 +620,132 @@
"id": "root_1-selector_main",
"sourceId": "root_1",
"targetId": "selector_main",
"path": "M 1349.21875 128 C 1349.21875 158 1359.21875 152 1359.21875 182",
"path": "M 1350.000006198883 125.66623544692993 C 1350.000006198883 155.66623544692993 1360.000006198883 155.04567861557007 1360.000006198883 185.04567861557007",
"active": false
},
{
"id": "selector_main-repeater_patrol",
"sourceId": "selector_main",
"targetId": "repeater_patrol",
"path": "M 1359.21875 278 C 1359.21875 320 590 320 590 362",
"path": "M 1360.000006198883 274.9542908668518 C 1360.000006198883 320.00000619888306 590.000006198883 320.00000619888306 590.000006198883 365.0457215309143",
"active": false
},
{
"id": "selector_main-selector_combat",
"sourceId": "selector_main",
"targetId": "selector_combat",
"path": "M 1359.21875 278 C 1359.21875 320 1250 320 1250 362",
"path": "M 1360.000006198883 274.9542908668518 C 1360.000006198883 320.00000619888306 1250.000006198883 320.00000619888306 1250.000006198883 365.0457215309143",
"active": false
},
{
"id": "selector_main-sequence_idle",
"sourceId": "selector_main",
"targetId": "sequence_idle",
"path": "M 1359.21875 278 C 1359.21875 320 2019.21875 320 2019.21875 362",
"path": "M 1360.000006198883 274.9542908668518 C 1360.000006198883 320.00000619888306 2020.0001282691956 320.00000619888306 2020.0001282691956 365.0457215309143",
"active": false
},
{
"id": "repeater_patrol-sequence_patrol",
"sourceId": "repeater_patrol",
"targetId": "sequence_patrol",
"path": "M 590 458 C 590 520 590 520 590 582",
"path": "M 590.000006198883 454.9542908668518 C 590.000006198883 519.9999756813049 590.000006198883 519.9999756813049 590.000006198883 585.045660495758",
"active": false
},
{
"id": "sequence_patrol-decorator_patrol_check",
"sourceId": "sequence_patrol",
"targetId": "decorator_patrol_check",
"path": "M 590 678 C 590 720 510 720 510 762",
"path": "M 590.000006198883 674.9542908668518 C 590.000006198883 721.4701294898987 510.00000619888306 721.4701294898987 510.00000619888306 767.9859681129456",
"active": false
},
{
"id": "sequence_patrol-action_patrol",
"sourceId": "sequence_patrol",
"targetId": "action_patrol",
"path": "M 590 678 C 590 720 700 720 700 762",
"path": "M 590.000006198883 674.9542908668518 C 590.000006198883 720.0783143043518 700.000006198883 720.0783143043518 700.000006198883 765.2023377418518",
"active": false
},
{
"id": "decorator_patrol_check-log_patrolling",
"sourceId": "decorator_patrol_check",
"targetId": "log_patrolling",
"path": "M 510 942.078125 C 510 972.078125 480 972 480 1002",
"path": "M 510.00000619888306 934.6140809059143 C 510.00000619888306 969.908209323883 480.00000619888306 969.908209323883 480.00000619888306 1005.2023377418518",
"active": false
},
{
"id": "selector_combat-sequence_attack",
"sourceId": "selector_combat",
"targetId": "sequence_attack",
"path": "M 1250 458 C 1250 500 1030 500 1030 542",
"path": "M 1250.000006198883 454.9542908668518 C 1250.000006198883 499.99997568130493 1030.000006198883 499.99997568130493 1030.000006198883 545.045660495758",
"active": false
},
{
"id": "selector_combat-sequence_defend",
"sourceId": "selector_combat",
"targetId": "sequence_defend",
"path": "M 1250 458 C 1250 500 1470 500 1470 542",
"path": "M 1250.000006198883 454.9542908668518 C 1250.000006198883 499.99997568130493 1470.000006198883 499.99997568130493 1470.000006198883 545.045660495758",
"active": false
},
{
"id": "sequence_attack-inverter_enemy",
"sourceId": "sequence_attack",
"targetId": "inverter_enemy",
"path": "M 1030 638 C 1030 680 920 680 920 722",
"path": "M 1030.000006198883 634.9542908668518 C 1030.000006198883 679.644049167633 920.000006198883 679.644049167633 920.000006198883 724.3338074684143",
"active": false
},
{
"id": "sequence_attack-action_attack",
"sourceId": "sequence_attack",
"targetId": "action_attack",
"path": "M 1030 638 C 1030 680 1140 680 1140 722",
"path": "M 1030.000006198883 634.9542908668518 C 1030.000006198883 680.0783143043518 1140.000006198883 680.0783143043518 1140.000006198883 725.2023377418518",
"active": false
},
{
"id": "inverter_enemy-condition_enemy",
"sourceId": "inverter_enemy",
"targetId": "condition_enemy",
"path": "M 920 798 C 920 840 920 840 920 882",
"path": "M 920.000006198883 795.6662049293518 C 920.000006198883 840.355963230133 920.000006198883 840.355963230133 920.000006198883 885.0457215309143",
"active": false
},
{
"id": "sequence_defend-wait_defend",
"sourceId": "sequence_defend",
"targetId": "wait_defend",
"path": "M 1470 638 C 1470 680 1360 680 1360 722",
"path": "M 1470.000006198883 634.9542908668518 C 1470.000006198883 680.000006198883 1360.000006198883 680.000006198883 1360.000006198883 725.0457215309143",
"active": false
},
{
"id": "sequence_defend-action_defend",
"sourceId": "sequence_defend",
"targetId": "action_defend",
"path": "M 1470 638 C 1470 680 1580 680 1580 722",
"path": "M 1470.000006198883 634.9542908668518 C 1470.000006198883 680.0783143043518 1580.000006198883 680.0783143043518 1580.000006198883 725.2023377418518",
"active": false
},
{
"id": "sequence_idle-action_idle",
"sourceId": "sequence_idle",
"targetId": "action_idle",
"path": "M 2019.21875 458 C 2019.21875 500 1800 500 1800 542",
"path": "M 2020.0001282691956 454.9542908668518 C 2020.0001282691956 500.0782837867737 1800.0001282691956 500.0782837867737 1800.0001282691956 545.2022767066956",
"active": false
},
{
"id": "sequence_idle-log_status",
"sourceId": "sequence_idle",
"targetId": "log_status",
"path": "M 2019.21875 458 C 2019.21875 500 2019.21875 500 2019.21875 542",
"path": "M 2020.0001282691956 454.9542908668518 C 2020.0001282691956 500.0782837867737 2020.0001282691956 500.0782837867737 2020.0001282691956 545.2022767066956",
"active": false
},
{
"id": "sequence_idle-wait_idle",
"sourceId": "sequence_idle",
"targetId": "wait_idle",
"path": "M 2019.21875 458 C 2019.21875 500 2238.4375 500 2238.4375 542",
"path": "M 2020.0001282691956 454.9542908668518 C 2020.0001282691956 499.99997568130493 2240.0001282691956 499.99997568130493 2240.0001282691956 545.045660495758",
"active": false
}
],
"metadata": {
"name": "behavior-tree-examples-guide",
"created": "2025-06-19T04:28:44.589Z",
"name": "assets/resources/behavior-tree-examples-guide",
"created": "2025-06-22T11:04:28.781Z",
"version": "1.0"
},
"blackboard": [

File diff suppressed because it is too large Load Diff

View File

@@ -2,9 +2,9 @@ import { _decorator, Component, Node, Label, Button, resources, JsonAsset } from
import {
BehaviorTreeBuilder,
BehaviorTree,
Blackboard,
BehaviorTreeJSONConfig
Blackboard
} from '@esengine/ai';
import type { BehaviorTreeJSONConfig } from '@esengine/ai';
const { ccclass, property } = _decorator;