Compare commits
7 Commits
@esengine/
...
@esengine/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87f71e2251 | ||
|
|
b9ea8d14cf | ||
|
|
10d0fb1d5c | ||
|
|
71e111415f | ||
|
|
0de45279e6 | ||
|
|
cc6f12d470 | ||
|
|
902c0a1074 |
@@ -182,6 +182,70 @@ export class IsHealthLow implements INodeExecutor {
|
||||
}
|
||||
```
|
||||
|
||||
## Using Custom Executors in BehaviorTreeBuilder
|
||||
|
||||
After defining a custom executor with `@NodeExecutorMetadata`, use the `.action()` method in the builder:
|
||||
|
||||
```typescript
|
||||
import { BehaviorTreeBuilder, BehaviorTreeStarter } from '@esengine/behavior-tree';
|
||||
|
||||
// Use custom executor in behavior tree
|
||||
const tree = BehaviorTreeBuilder.create('CombatAI')
|
||||
.defineBlackboardVariable('health', 100)
|
||||
.defineBlackboardVariable('target', null)
|
||||
.selector('Root')
|
||||
.sequence('AttackSequence')
|
||||
// Use custom action - matches implementationType in decorator
|
||||
.action('AttackAction', 'Attack', { damage: 25 })
|
||||
.action('MoveToTarget', 'Chase')
|
||||
.end()
|
||||
.action('WaitAction', 'Idle', { duration: 1000 })
|
||||
.end()
|
||||
.build();
|
||||
|
||||
// Start the behavior tree
|
||||
const entity = scene.createEntity('Enemy');
|
||||
BehaviorTreeStarter.start(entity, tree);
|
||||
```
|
||||
|
||||
### Builder Methods for Custom Nodes
|
||||
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| `.action(type, name?, config?)` | Add custom action node |
|
||||
| `.condition(type, name?, config?)` | Add custom condition node |
|
||||
| `.executeAction(name)` | Use blackboard function `action_{name}` |
|
||||
| `.executeCondition(name)` | Use blackboard function `condition_{name}` |
|
||||
|
||||
### Complete Example
|
||||
|
||||
```typescript
|
||||
// 1. Define custom executor
|
||||
@NodeExecutorMetadata({
|
||||
implementationType: 'AttackAction',
|
||||
nodeType: NodeType.Action,
|
||||
displayName: 'Attack',
|
||||
category: 'Combat',
|
||||
configSchema: {
|
||||
damage: { type: 'number', default: 10, supportBinding: true }
|
||||
}
|
||||
})
|
||||
class AttackAction implements INodeExecutor {
|
||||
execute(context: NodeExecutionContext): TaskStatus {
|
||||
const damage = BindingHelper.getValue<number>(context, 'damage', 10);
|
||||
console.log(`Attacking with ${damage} damage!`);
|
||||
return TaskStatus.Success;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Build and use
|
||||
const tree = BehaviorTreeBuilder.create('AI')
|
||||
.selector('Root')
|
||||
.action('AttackAction', 'Attack', { damage: 50 })
|
||||
.end()
|
||||
.build();
|
||||
```
|
||||
|
||||
## Registering Custom Executors
|
||||
|
||||
Executors are auto-registered via the decorator. To manually register:
|
||||
|
||||
@@ -606,6 +606,107 @@ export class RetryDecorator implements INodeExecutor {
|
||||
}
|
||||
```
|
||||
|
||||
## 在代码中使用自定义执行器
|
||||
|
||||
定义了自定义执行器后,可以通过 `BehaviorTreeBuilder` 的 `.action()` 和 `.condition()` 方法在代码中使用:
|
||||
|
||||
### 使用 action() 方法
|
||||
|
||||
```typescript
|
||||
import { BehaviorTreeBuilder, BehaviorTreeStarter } from '@esengine/behavior-tree';
|
||||
|
||||
// 使用自定义执行器构建行为树
|
||||
const tree = BehaviorTreeBuilder.create('CombatAI')
|
||||
.defineBlackboardVariable('health', 100)
|
||||
.defineBlackboardVariable('target', null)
|
||||
.selector('Root')
|
||||
.sequence('AttackSequence')
|
||||
// 使用自定义动作 - implementationType 匹配装饰器中的定义
|
||||
.action('AttackAction', 'Attack', { damage: 25 })
|
||||
.action('MoveToPosition', 'Chase', { speed: 10 })
|
||||
.end()
|
||||
.action('DelayAction', 'Idle', { duration: 1.0 })
|
||||
.end()
|
||||
.build();
|
||||
|
||||
// 启动行为树
|
||||
const entity = scene.createEntity('Enemy');
|
||||
BehaviorTreeStarter.start(entity, tree);
|
||||
```
|
||||
|
||||
### 使用 condition() 方法
|
||||
|
||||
```typescript
|
||||
const tree = BehaviorTreeBuilder.create('AI')
|
||||
.selector('Root')
|
||||
.sequence('AttackBranch')
|
||||
// 使用自定义条件
|
||||
.condition('CheckHealth', 'IsHealthy', { threshold: 50, operator: 'greater' })
|
||||
.action('AttackAction', 'Attack')
|
||||
.end()
|
||||
.end()
|
||||
.build();
|
||||
```
|
||||
|
||||
### Builder 方法对照表
|
||||
|
||||
| 方法 | 说明 | 使用场景 |
|
||||
|------|------|----------|
|
||||
| `.action(type, name?, config?)` | 使用自定义动作执行器 | 自定义 Action 类 |
|
||||
| `.condition(type, name?, config?)` | 使用自定义条件执行器 | 自定义 Condition 类 |
|
||||
| `.executeAction(name)` | 调用黑板函数 `action_{name}` | 简单逻辑、快速原型 |
|
||||
| `.executeCondition(name)` | 调用黑板函数 `condition_{name}` | 简单条件判断 |
|
||||
|
||||
### 完整示例
|
||||
|
||||
```typescript
|
||||
import {
|
||||
BehaviorTreeBuilder,
|
||||
BehaviorTreeStarter,
|
||||
NodeExecutorMetadata,
|
||||
INodeExecutor,
|
||||
NodeExecutionContext,
|
||||
TaskStatus,
|
||||
NodeType,
|
||||
BindingHelper
|
||||
} from '@esengine/behavior-tree';
|
||||
|
||||
// 1. 定义自定义执行器
|
||||
@NodeExecutorMetadata({
|
||||
implementationType: 'AttackAction',
|
||||
nodeType: NodeType.Action,
|
||||
displayName: '攻击',
|
||||
category: 'Combat',
|
||||
configSchema: {
|
||||
damage: { type: 'number', default: 10, supportBinding: true }
|
||||
}
|
||||
})
|
||||
class AttackAction implements INodeExecutor {
|
||||
execute(context: NodeExecutionContext): TaskStatus {
|
||||
const damage = BindingHelper.getValue<number>(context, 'damage', 10);
|
||||
console.log(`执行攻击,造成 ${damage} 点伤害!`);
|
||||
return TaskStatus.Success;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 构建行为树
|
||||
const enemyAI = BehaviorTreeBuilder.create('EnemyAI')
|
||||
.defineBlackboardVariable('health', 100)
|
||||
.defineBlackboardVariable('target', null)
|
||||
.selector('MainBehavior')
|
||||
.sequence('AttackBranch')
|
||||
.condition('CheckHealth', 'HasEnoughHealth', { threshold: 20, operator: 'greater' })
|
||||
.action('AttackAction', 'Attack', { damage: 50 })
|
||||
.end()
|
||||
.log('逃跑', 'Flee')
|
||||
.end()
|
||||
.build();
|
||||
|
||||
// 3. 启动行为树
|
||||
const entity = scene.createEntity('Enemy');
|
||||
BehaviorTreeStarter.start(entity, enemyAI);
|
||||
```
|
||||
|
||||
## 注册执行器
|
||||
|
||||
### 自动注册
|
||||
|
||||
@@ -1,5 +1,54 @@
|
||||
# @esengine/behavior-tree
|
||||
|
||||
## 4.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#408](https://github.com/esengine/esengine/pull/408) [`b9ea8d1`](https://github.com/esengine/esengine/commit/b9ea8d14cf38e1480f638c229f9ee150b65f0c60) Thanks [@esengine](https://github.com/esengine)! - feat: add action() and condition() methods to BehaviorTreeBuilder
|
||||
|
||||
Added new methods to support custom executor types directly in the builder:
|
||||
- `action(implementationType, name?, config?)` - Use custom action executors registered via `@NodeExecutorMetadata`
|
||||
- `condition(implementationType, name?, config?)` - Use custom condition executors
|
||||
|
||||
This provides a cleaner API for using custom node executors compared to the existing `executeAction()` which only supports blackboard functions.
|
||||
|
||||
Example:
|
||||
|
||||
```typescript
|
||||
// Define custom executor
|
||||
@NodeExecutorMetadata({
|
||||
implementationType: 'AttackAction',
|
||||
nodeType: NodeType.Action,
|
||||
displayName: 'Attack',
|
||||
category: 'Combat'
|
||||
})
|
||||
class AttackAction implements INodeExecutor {
|
||||
execute(context: NodeExecutionContext): TaskStatus {
|
||||
return TaskStatus.Success;
|
||||
}
|
||||
}
|
||||
|
||||
// Use in builder
|
||||
const tree = BehaviorTreeBuilder.create('AI')
|
||||
.selector('Root')
|
||||
.action('AttackAction', 'Attack', { damage: 50 })
|
||||
.end()
|
||||
.build();
|
||||
```
|
||||
|
||||
## 4.1.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#406](https://github.com/esengine/esengine/pull/406) [`0de4527`](https://github.com/esengine/esengine/commit/0de45279e612c04ae9be7fbd65ce496e4797a43c) Thanks [@esengine](https://github.com/esengine)! - fix(behavior-tree): export NodeExecutorMetadata as value instead of type
|
||||
|
||||
Fixed the export of `NodeExecutorMetadata` decorator in `execution/index.ts`.
|
||||
Previously it was exported as `export type { NodeExecutorMetadata }` which only
|
||||
exported the type signature, not the actual function. This caused runtime errors
|
||||
in Cocos Creator: "TypeError: (intermediate value) is not a function".
|
||||
|
||||
Changed to `export { NodeExecutorMetadata }` to properly export the decorator function.
|
||||
|
||||
## 4.1.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@esengine/behavior-tree",
|
||||
"version": "4.1.1",
|
||||
"version": "4.2.0",
|
||||
"description": "ECS-based AI behavior tree system - works with any ECS framework (ESEngine, Cocos, Laya, etc.)",
|
||||
"main": "dist/index.js",
|
||||
"module": "dist/index.js",
|
||||
@@ -29,7 +29,8 @@
|
||||
"clean": "rimraf dist tsconfig.tsbuildinfo",
|
||||
"build": "tsup",
|
||||
"build:watch": "tsup --watch",
|
||||
"type-check": "tsc --noEmit"
|
||||
"type-check": "tsc --noEmit",
|
||||
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
|
||||
},
|
||||
"author": "yhh",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -181,12 +181,73 @@ export class BehaviorTreeBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加执行动作
|
||||
* 添加执行动作(通过黑板函数)
|
||||
*
|
||||
* @zh 使用黑板中的 action_{actionName} 函数执行动作
|
||||
* @en Execute action using action_{actionName} function from blackboard
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* BehaviorTreeBuilder.create("AI")
|
||||
* .defineBlackboardVariable("action_Attack", (entity) => TaskStatus.Success)
|
||||
* .selector("Root")
|
||||
* .executeAction("Attack")
|
||||
* .end()
|
||||
* .build();
|
||||
* ```
|
||||
*/
|
||||
executeAction(actionName: string, name?: string): BehaviorTreeBuilder {
|
||||
return this.addActionNode('ExecuteAction', name || 'ExecuteAction', { actionName });
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加自定义动作节点
|
||||
*
|
||||
* @zh 直接使用注册的执行器类型(通过 @NodeExecutorMetadata 装饰器注册的类)
|
||||
* @en Use a registered executor type directly (class registered via @NodeExecutorMetadata decorator)
|
||||
*
|
||||
* @param implementationType - 执行器类型名称(@NodeExecutorMetadata 中的 implementationType)
|
||||
* @param name - 节点显示名称
|
||||
* @param config - 节点配置参数
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // 1. 定义自定义执行器
|
||||
* @NodeExecutorMetadata({
|
||||
* implementationType: 'AttackAction',
|
||||
* nodeType: NodeType.Action,
|
||||
* displayName: '攻击动作',
|
||||
* category: 'Action'
|
||||
* })
|
||||
* class AttackAction implements INodeExecutor {
|
||||
* execute(context: NodeExecutionContext): TaskStatus {
|
||||
* console.log("执行攻击!");
|
||||
* return TaskStatus.Success;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* // 2. 在行为树中使用
|
||||
* BehaviorTreeBuilder.create("AI")
|
||||
* .selector("Root")
|
||||
* .action("AttackAction", "Attack")
|
||||
* .end()
|
||||
* .build();
|
||||
* ```
|
||||
*/
|
||||
action(implementationType: string, name?: string, config?: Record<string, any>): BehaviorTreeBuilder {
|
||||
return this.addActionNode(implementationType, name || implementationType, config || {});
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加自定义条件节点
|
||||
*
|
||||
* @zh 直接使用注册的条件执行器类型
|
||||
* @en Use a registered condition executor type directly
|
||||
*/
|
||||
condition(implementationType: string, name?: string, config?: Record<string, any>): BehaviorTreeBuilder {
|
||||
return this.addConditionNode(implementationType, name || implementationType, config || {});
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加黑板比较条件
|
||||
*/
|
||||
|
||||
@@ -5,7 +5,7 @@ export { BehaviorTreeAssetManager } from './BehaviorTreeAssetManager';
|
||||
export type { INodeExecutor, NodeExecutionContext } from './NodeExecutor';
|
||||
export { NodeExecutorRegistry, BindingHelper } from './NodeExecutor';
|
||||
export { BehaviorTreeExecutionSystem } from './BehaviorTreeExecutionSystem';
|
||||
export type { NodeMetadata, ConfigFieldDefinition, NodeExecutorMetadata } from './NodeMetadata';
|
||||
export { NodeMetadataRegistry } from './NodeMetadata';
|
||||
export type { NodeMetadata, ConfigFieldDefinition } from './NodeMetadata';
|
||||
export { NodeMetadataRegistry, NodeExecutorMetadata } from './NodeMetadata';
|
||||
|
||||
export * from './Executors';
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @esengine/network
|
||||
|
||||
## 5.0.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`902c0a1`](https://github.com/esengine/esengine/commit/902c0a10749f80bd8f499b44154646379d359704)]:
|
||||
- @esengine/rpc@1.1.3
|
||||
|
||||
## 5.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@esengine/network",
|
||||
"version": "5.0.2",
|
||||
"version": "5.0.3",
|
||||
"description": "Network synchronization for multiplayer games",
|
||||
"esengine": {
|
||||
"plugin": true,
|
||||
|
||||
@@ -1,5 +1,49 @@
|
||||
# @esengine/rpc
|
||||
|
||||
## 1.1.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#404](https://github.com/esengine/esengine/pull/404) [`902c0a1`](https://github.com/esengine/esengine/commit/902c0a10749f80bd8f499b44154646379d359704) Thanks [@esengine](https://github.com/esengine)! - feat(server): add HTTP file-based routing support / 添加 HTTP 文件路由支持
|
||||
|
||||
New feature that allows organizing HTTP routes in separate files, similar to API and message handlers.
|
||||
新功能:支持将 HTTP 路由组织在独立文件中,类似于 API 和消息处理器的文件路由方式。
|
||||
|
||||
```typescript
|
||||
// src/http/login.ts
|
||||
import { defineHttp } from '@esengine/server';
|
||||
|
||||
export default defineHttp<{ username: string; password: string }>({
|
||||
method: 'POST',
|
||||
handler(req, res) {
|
||||
const { username, password } = req.body;
|
||||
res.json({ token: '...', userId: '...' });
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Server configuration / 服务器配置:
|
||||
|
||||
```typescript
|
||||
const server = await createServer({
|
||||
port: 8080,
|
||||
httpDir: 'src/http', // HTTP routes directory / HTTP 路由目录
|
||||
httpPrefix: '/api', // Route prefix / 路由前缀
|
||||
cors: true
|
||||
});
|
||||
```
|
||||
|
||||
File naming convention / 文件命名规则:
|
||||
- `login.ts` → POST /api/login
|
||||
- `users/profile.ts` → POST /api/users/profile
|
||||
- `users/[id].ts` → POST /api/users/:id (dynamic routes / 动态路由)
|
||||
- Set `method: 'GET'` in defineHttp for GET requests / 在 defineHttp 中设置 `method: 'GET'` 以处理 GET 请求
|
||||
|
||||
Also includes / 还包括:
|
||||
- `defineHttp<TBody>()` helper for type-safe route definitions / 类型安全的路由定义辅助函数
|
||||
- Support for merging file routes with inline `http` config / 支持文件路由与内联 `http` 配置合并
|
||||
- RPC server supports attaching to existing HTTP server via `server` option / RPC 服务器支持通过 `server` 选项附加到现有 HTTP 服务器
|
||||
|
||||
## 1.1.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@esengine/rpc",
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.3",
|
||||
"description": "Elegant type-safe RPC library for ESEngine",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
|
||||
@@ -1,5 +1,54 @@
|
||||
# @esengine/server
|
||||
|
||||
## 4.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#404](https://github.com/esengine/esengine/pull/404) [`902c0a1`](https://github.com/esengine/esengine/commit/902c0a10749f80bd8f499b44154646379d359704) Thanks [@esengine](https://github.com/esengine)! - feat(server): add HTTP file-based routing support / 添加 HTTP 文件路由支持
|
||||
|
||||
New feature that allows organizing HTTP routes in separate files, similar to API and message handlers.
|
||||
新功能:支持将 HTTP 路由组织在独立文件中,类似于 API 和消息处理器的文件路由方式。
|
||||
|
||||
```typescript
|
||||
// src/http/login.ts
|
||||
import { defineHttp } from '@esengine/server';
|
||||
|
||||
export default defineHttp<{ username: string; password: string }>({
|
||||
method: 'POST',
|
||||
handler(req, res) {
|
||||
const { username, password } = req.body;
|
||||
res.json({ token: '...', userId: '...' });
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Server configuration / 服务器配置:
|
||||
|
||||
```typescript
|
||||
const server = await createServer({
|
||||
port: 8080,
|
||||
httpDir: 'src/http', // HTTP routes directory / HTTP 路由目录
|
||||
httpPrefix: '/api', // Route prefix / 路由前缀
|
||||
cors: true
|
||||
});
|
||||
```
|
||||
|
||||
File naming convention / 文件命名规则:
|
||||
- `login.ts` → POST /api/login
|
||||
- `users/profile.ts` → POST /api/users/profile
|
||||
- `users/[id].ts` → POST /api/users/:id (dynamic routes / 动态路由)
|
||||
- Set `method: 'GET'` in defineHttp for GET requests / 在 defineHttp 中设置 `method: 'GET'` 以处理 GET 请求
|
||||
|
||||
Also includes / 还包括:
|
||||
- `defineHttp<TBody>()` helper for type-safe route definitions / 类型安全的路由定义辅助函数
|
||||
- Support for merging file routes with inline `http` config / 支持文件路由与内联 `http` 配置合并
|
||||
- RPC server supports attaching to existing HTTP server via `server` option / RPC 服务器支持通过 `server` 选项附加到现有 HTTP 服务器
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`902c0a1`](https://github.com/esengine/esengine/commit/902c0a10749f80bd8f499b44154646379d359704)]:
|
||||
- @esengine/rpc@1.1.3
|
||||
|
||||
## 4.1.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@esengine/server",
|
||||
"version": "4.1.0",
|
||||
"version": "4.2.0",
|
||||
"description": "Game server framework for ESEngine with file-based routing",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @esengine/transaction
|
||||
|
||||
## 2.0.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`902c0a1`](https://github.com/esengine/esengine/commit/902c0a10749f80bd8f499b44154646379d359704)]:
|
||||
- @esengine/server@4.2.0
|
||||
|
||||
## 2.0.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@esengine/transaction",
|
||||
"version": "2.0.6",
|
||||
"version": "2.0.7",
|
||||
"description": "Game transaction system with distributed support | 游戏事务系统,支持分布式事务",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
|
||||
@@ -6,7 +6,7 @@ export default defineConfig({
|
||||
dts: true,
|
||||
sourcemap: true,
|
||||
clean: true,
|
||||
external: ["../pkg/rapier_wasm2d.js"],
|
||||
external: [/\.\.\/pkg\/rapier_wasm2d/],
|
||||
loader: {
|
||||
".wasm": "base64",
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user