mirror of
https://github.com/gongxh0901/kunpocc-behaviortree.git
synced 2025-12-26 16:48:56 +00:00
修改节点装饰器
# Conflicts: # package.json
This commit is contained in:
@@ -19,7 +19,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf dist",
|
||||
"copy": "cp -r dist/* ./../kunpo-esc/demo/node_modules/kunpocc-behaviortree/dist/",
|
||||
"copy": "cp -r dist/* ./../kunpo-ecs/demo/node_modules/kunpocc-behaviortree/dist/",
|
||||
"build": "npm run clean && rollup -c rollup.config.mjs && npm run copy"
|
||||
},
|
||||
"files": [
|
||||
|
||||
@@ -32,16 +32,16 @@ export namespace BT {
|
||||
* 参数描述接口
|
||||
*/
|
||||
export interface ParameterInfo {
|
||||
/** 参数类型 */
|
||||
type: ParamType;
|
||||
/** 参数名称 */
|
||||
name: string;
|
||||
/** 参数类型 */
|
||||
type: ParamType;
|
||||
/** 参数描述 */
|
||||
description?: string;
|
||||
/** 默认值 */
|
||||
defaultValue?: any;
|
||||
/** 是否必需 */
|
||||
required?: boolean;
|
||||
/** 步进 针对数字类型的变更的最小单位 */
|
||||
step?: number,
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,6 +50,8 @@ export namespace BT {
|
||||
export interface NodeMetadata {
|
||||
/** 节点名称 */
|
||||
name: string;
|
||||
/** 节点类名 */
|
||||
className: string;
|
||||
/** 节点分组 */
|
||||
group: string;
|
||||
/** 节点类型 */
|
||||
@@ -65,19 +67,50 @@ export namespace BT {
|
||||
/**
|
||||
* 节点元数据存储
|
||||
*/
|
||||
const NODE_METADATA_MAP = new Map<string, NodeMetadata>();
|
||||
const NODE_METADATA_MAP = new Map<any, NodeMetadata>();
|
||||
|
||||
/**
|
||||
* 节点参数存储
|
||||
*/
|
||||
const NODE_PARAMETERS_MAP = new Map<any, ParameterInfo[]>();
|
||||
|
||||
/**
|
||||
* 节点属性装饰器
|
||||
*/
|
||||
export function prop(paramInfo: Omit<ParameterInfo, "name">) {
|
||||
return function (target: any, propertyKey: string) {
|
||||
const ctor = target.constructor;
|
||||
if (!NODE_PARAMETERS_MAP.has(ctor)) {
|
||||
NODE_PARAMETERS_MAP.set(ctor, []);
|
||||
}
|
||||
const parameters = NODE_PARAMETERS_MAP.get(ctor)!;
|
||||
parameters.push({
|
||||
...paramInfo,
|
||||
name: propertyKey
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 行为节点装饰器
|
||||
* @param name 节点的类名 编辑器导出数据中的节点名字
|
||||
* @param info.group 节点在编辑器中的分组
|
||||
* @param info.name 节点在编辑器中的中文名
|
||||
* @param info.desc 节点描述信息
|
||||
*/
|
||||
export function ActionNode(metadata: Omit<NodeMetadata, "type" | "maxChildren">) {
|
||||
export function ActionNode(name: string, info?: { group?: string, name?: string, desc?: string }) {
|
||||
return function <T extends new (...args: any[]) => any>(constructor: T) {
|
||||
const parameters = NODE_PARAMETERS_MAP.get(constructor) || [];
|
||||
const fullMetadata: NodeMetadata = {
|
||||
...metadata,
|
||||
className: name,
|
||||
group: info?.group || '未分组',
|
||||
name: info?.name || '',
|
||||
description: info?.desc || '',
|
||||
type: Type.Action,
|
||||
maxChildren: 0,
|
||||
parameters
|
||||
};
|
||||
NODE_METADATA_MAP.set(constructor.name, fullMetadata);
|
||||
NODE_METADATA_MAP.set(constructor, fullMetadata);
|
||||
return constructor;
|
||||
};
|
||||
}
|
||||
@@ -85,14 +118,19 @@ export namespace BT {
|
||||
/**
|
||||
* 条件节点装饰器
|
||||
*/
|
||||
export function ConditionNode(metadata: Omit<NodeMetadata, "type" | "maxChildren">) {
|
||||
export function ConditionNode(name: string, info?: { group?: string, name?: string, desc?: string }) {
|
||||
return function <T extends new (...args: any[]) => any>(constructor: T) {
|
||||
const parameters = NODE_PARAMETERS_MAP.get(constructor) || [];
|
||||
const fullMetadata: NodeMetadata = {
|
||||
...metadata,
|
||||
className: name,
|
||||
group: info?.group || '未分组',
|
||||
name: info?.name || '',
|
||||
description: info?.desc || '',
|
||||
type: Type.Condition,
|
||||
maxChildren: 0,
|
||||
parameters
|
||||
};
|
||||
NODE_METADATA_MAP.set(constructor.name, fullMetadata);
|
||||
NODE_METADATA_MAP.set(constructor, fullMetadata);
|
||||
return constructor;
|
||||
};
|
||||
}
|
||||
@@ -100,14 +138,19 @@ export namespace BT {
|
||||
/**
|
||||
* 组合节点装饰器
|
||||
*/
|
||||
export function CompositeNode(metadata: Omit<NodeMetadata, "type" | "maxChildren">) {
|
||||
export function CompositeNode(name: string, info?: { group?: string, name?: string, desc?: string }) {
|
||||
return function <T extends new (...args: any[]) => any>(constructor: T) {
|
||||
const parameters = NODE_PARAMETERS_MAP.get(constructor) || [];
|
||||
const fullMetadata: NodeMetadata = {
|
||||
...metadata,
|
||||
className: name,
|
||||
group: info?.group || '未分组',
|
||||
name: info?.name || '',
|
||||
description: info?.desc || '',
|
||||
type: Type.Composite,
|
||||
maxChildren: -1
|
||||
maxChildren: -1,
|
||||
parameters
|
||||
};
|
||||
NODE_METADATA_MAP.set(constructor.name, fullMetadata);
|
||||
NODE_METADATA_MAP.set(constructor, fullMetadata);
|
||||
return constructor;
|
||||
};
|
||||
}
|
||||
@@ -115,45 +158,29 @@ export namespace BT {
|
||||
/**
|
||||
* 装饰节点装饰器
|
||||
*/
|
||||
export function DecoratorNode(metadata: Omit<NodeMetadata, "type" | "maxChildren">) {
|
||||
export function DecoratorNode(name: string, info?: { group?: string, name?: string, desc?: string }) {
|
||||
return function <T extends new (...args: any[]) => any>(constructor: T) {
|
||||
const parameters = NODE_PARAMETERS_MAP.get(constructor) || [];
|
||||
const fullMetadata: NodeMetadata = {
|
||||
...metadata,
|
||||
className: name,
|
||||
group: info?.group || '未分组',
|
||||
name: info?.name || '',
|
||||
description: info?.desc || '',
|
||||
type: Type.Decorator,
|
||||
maxChildren: 1
|
||||
maxChildren: 1,
|
||||
parameters
|
||||
};
|
||||
NODE_METADATA_MAP.set(constructor.name, fullMetadata);
|
||||
NODE_METADATA_MAP.set(constructor, fullMetadata);
|
||||
return constructor;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取节点元数据
|
||||
*/
|
||||
export function getNodeMetadata(nodeName: string): NodeMetadata | undefined {
|
||||
return NODE_METADATA_MAP.get(nodeName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有节点元数据
|
||||
*/
|
||||
export function getAllNodeMetadata(): Map<string, NodeMetadata> {
|
||||
export function getAllNodeMetadata(): Map<any, NodeMetadata> {
|
||||
return new Map(NODE_METADATA_MAP);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断节点是否允许子节点
|
||||
*/
|
||||
export function canHaveChildren(metadata: NodeMetadata): boolean {
|
||||
return metadata.maxChildren !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断节点是否可以添加更多子节点
|
||||
*/
|
||||
export function canAddMoreChildren(metadata: NodeMetadata, currentChildCount: number): boolean {
|
||||
return metadata.maxChildren === -1 || currentChildCount < metadata.maxChildren;
|
||||
}
|
||||
}
|
||||
|
||||
let _global = globalThis || window || global;
|
||||
|
||||
@@ -42,14 +42,7 @@ export abstract class Composite extends BTNode {
|
||||
* 包含最大值和当前值的通用逻辑,适用于所有需要数值计数的修饰节点
|
||||
*/
|
||||
export abstract class NumericDecorator extends Decorator {
|
||||
protected readonly _max: number;
|
||||
protected _value: number = 0;
|
||||
|
||||
constructor(child: IBTNode, max: number = 1) {
|
||||
super(child);
|
||||
this._max = max;
|
||||
}
|
||||
|
||||
protected override open(): void {
|
||||
super.open();
|
||||
this._value = 0;
|
||||
|
||||
@@ -7,15 +7,13 @@ import { LeafNode } from "./AbstractNodes";
|
||||
* 次数内,返回RUNNING
|
||||
* 超次,返回SUCCESS
|
||||
*/
|
||||
@BT.ActionNode({
|
||||
@BT.ActionNode("WaitTicks", {
|
||||
name: "等待次数",
|
||||
group: "等待行为",
|
||||
description: "等待指定次数后返回成功",
|
||||
parameters: [
|
||||
{ name: "maxTicks", type: BT.ParamType.int, description: "最大等待次数", defaultValue: 0, required: true }
|
||||
]
|
||||
desc: "等待指定次数后返回成功",
|
||||
})
|
||||
export class WaitTicks extends LeafNode {
|
||||
@BT.prop({ type: BT.ParamType.int, description: "最大等待次数", defaultValue: 0, step: 1 })
|
||||
private _max: number;
|
||||
private _value: number;
|
||||
|
||||
@@ -42,15 +40,13 @@ export class WaitTicks extends LeafNode {
|
||||
* 时间等待节点 时间(秒)
|
||||
* 时间到后返回SUCCESS,否则返回RUNNING
|
||||
*/
|
||||
@BT.ActionNode({
|
||||
@BT.ActionNode("WaitTime", {
|
||||
name: "等待时间",
|
||||
group: "等待行为",
|
||||
description: "等待指定时间(秒)后返回成功",
|
||||
parameters: [
|
||||
{ name: "duration", type: BT.ParamType.float, description: "等待时间(秒)", defaultValue: 0, required: true }
|
||||
]
|
||||
desc: "等待指定时间(秒)后返回成功",
|
||||
})
|
||||
export class WaitTime extends LeafNode {
|
||||
@BT.prop({ type: BT.ParamType.float, description: "等待时间(秒)", defaultValue: 0, step: 0.01 })
|
||||
private _max: number;
|
||||
private _value: number = 0;
|
||||
constructor(duration: number = 0) {
|
||||
|
||||
@@ -11,11 +11,10 @@ import { WeightDecorator } from "./Decorator";
|
||||
*
|
||||
* 遇到 RUNNING 返回 RUNNING 下次从该节点开始
|
||||
*/
|
||||
@BT.CompositeNode({
|
||||
@BT.CompositeNode("MemSelector", {
|
||||
name: "记忆选择节点",
|
||||
group: "基础组合节点",
|
||||
description: "记住上次运行位置的选择节点,从记忆位置开始执行",
|
||||
parameters: []
|
||||
desc: "记住上次运行位置的选择节点,从记忆位置开始执行",
|
||||
})
|
||||
export class MemSelector extends MemoryComposite {
|
||||
public tick(): Status {
|
||||
@@ -42,11 +41,10 @@ export class MemSelector extends MemoryComposite {
|
||||
*
|
||||
* 遇到 RUNNING 返回 RUNNING 下次从该节点开始
|
||||
*/
|
||||
@BT.CompositeNode({
|
||||
@BT.CompositeNode("MemSequence", {
|
||||
name: "记忆顺序节点",
|
||||
group: "基础组合节点",
|
||||
description: "记住上次运行位置的序列节点,从记忆位置开始执行",
|
||||
parameters: []
|
||||
desc: "记住上次运行位置的序列节点,从记忆位置开始执行",
|
||||
})
|
||||
export class MemSequence extends MemoryComposite {
|
||||
public tick(): Status {
|
||||
@@ -71,11 +69,10 @@ export class MemSequence extends MemoryComposite {
|
||||
* 返回第一个不为 FAILURE 的子节点状态
|
||||
* 否则返回 FAILURE
|
||||
*/
|
||||
@BT.CompositeNode({
|
||||
@BT.CompositeNode("Selector", {
|
||||
name: "选择节点",
|
||||
group: "基础组合节点",
|
||||
description: "依次执行子节点,直到找到成功或运行中的节点",
|
||||
parameters: []
|
||||
desc: "依次执行子节点,直到找到成功或运行中的节点",
|
||||
})
|
||||
export class Selector extends Composite {
|
||||
public tick(): Status {
|
||||
@@ -95,11 +92,10 @@ export class Selector extends Composite {
|
||||
* 随机选择一个子节点执行
|
||||
* 返回子节点状态
|
||||
*/
|
||||
@BT.CompositeNode({
|
||||
@BT.CompositeNode("RandomSelector", {
|
||||
name: "随机选择节点",
|
||||
group: "基础组合节点",
|
||||
description: "随机选择一个子节点执行",
|
||||
parameters: [],
|
||||
desc: "随机选择一个子节点执行",
|
||||
})
|
||||
export class RandomSelector extends Composite {
|
||||
private _totalWeight: number = 0;
|
||||
@@ -153,11 +149,10 @@ export class RandomSelector extends Composite {
|
||||
* 遇到 SUCCESS 继续下一个
|
||||
* 否则返回子节点状态
|
||||
*/
|
||||
@BT.CompositeNode({
|
||||
@BT.CompositeNode("Sequence", {
|
||||
name: "顺序节点",
|
||||
group: "基础组合节点",
|
||||
description: "依次执行所有子节点,全部成功才返回成功",
|
||||
parameters: []
|
||||
desc: "依次执行所有子节点,全部成功才返回成功",
|
||||
})
|
||||
export class Sequence extends Composite {
|
||||
public tick(): Status {
|
||||
@@ -176,11 +171,10 @@ export class Sequence extends Composite {
|
||||
* 并行节点 从上到下执行 全部执行一遍
|
||||
* 返回优先级 FAILURE > RUNNING > SUCCESS
|
||||
*/
|
||||
@BT.CompositeNode({
|
||||
@BT.CompositeNode("Parallel", {
|
||||
name: "并行节点",
|
||||
group: "基础组合节点",
|
||||
description: "同时执行所有子节点,全部成功才返回成功",
|
||||
parameters: []
|
||||
desc: "同时执行所有子节点,全部成功才返回成功",
|
||||
})
|
||||
export class Parallel extends Composite {
|
||||
public tick(): Status {
|
||||
@@ -201,11 +195,10 @@ export class Parallel extends Composite {
|
||||
* 并行节点 从上到下执行 全部执行一遍
|
||||
* 返回优先级 SUCCESS > RUNNING > FAILURE
|
||||
*/
|
||||
@BT.CompositeNode({
|
||||
@BT.CompositeNode("ParallelAnySuccess", {
|
||||
name: "并行任意成功",
|
||||
group: "基础组合节点",
|
||||
description: "同时执行所有子节点,任意一个成功即返回成功",
|
||||
parameters: []
|
||||
desc: "同时执行所有子节点,任意一个成功即返回成功",
|
||||
})
|
||||
export class ParallelAnySuccess extends Composite {
|
||||
public tick(): Status {
|
||||
|
||||
@@ -15,11 +15,10 @@ import { IBTNode } from "./BTNode";
|
||||
* 第一个Child Node节点, 返回 FAILURE, 本Node向自己的Parent Node也返回 SUCCESS
|
||||
* 第一个Child Node节点, 返回 SUCCESS, 本Node向自己的Parent Node也返回 FAILURE
|
||||
*/
|
||||
@BT.DecoratorNode({
|
||||
@BT.DecoratorNode("Inverter", {
|
||||
name: "反转器",
|
||||
group: "基础装饰节点",
|
||||
description: "反转子节点的执行结果,成功变失败,失败变成功",
|
||||
parameters: []
|
||||
desc: "反转子节点的执行结果,成功变失败,失败变成功",
|
||||
})
|
||||
export class Inverter extends Decorator {
|
||||
public tick(): Status {
|
||||
@@ -41,22 +40,23 @@ export class Inverter extends Decorator {
|
||||
* 规定时间内, 根据Child Node的结果, 本节点向自己的父节点也返回相同的结果
|
||||
* 超时后, 直接返回 FAILURE
|
||||
*/
|
||||
@BT.DecoratorNode({
|
||||
@BT.DecoratorNode("LimitTime", {
|
||||
name: "时间限制器",
|
||||
group: "基础装饰节点",
|
||||
description: "限制子节点执行时间,超时返回失败",
|
||||
parameters: [
|
||||
{ name: "max", type: BT.ParamType.float, description: "最大时间(秒)", defaultValue: 1, required: true }
|
||||
]
|
||||
desc: "限制子节点执行时间,超时返回失败",
|
||||
})
|
||||
export class LimitTime extends NumericDecorator {
|
||||
@BT.prop({ type: BT.ParamType.float, description: "最大时间(秒)", defaultValue: 1 })
|
||||
protected _max: number = 1;
|
||||
|
||||
/**
|
||||
* 时间限制节点
|
||||
* @param child 子节点
|
||||
* @param max 最大时间 (秒) 默认1秒
|
||||
*/
|
||||
constructor(child: IBTNode, max: number = 1) {
|
||||
super(child, max * 1000);
|
||||
super(child);
|
||||
this._max = max * 1000;
|
||||
}
|
||||
|
||||
protected override open(): void {
|
||||
@@ -77,15 +77,19 @@ export class LimitTime extends NumericDecorator {
|
||||
* 必须且只能包含一个子节点
|
||||
* 次数超过后, 直接返回失败; 次数未超过, 返回子节点状态
|
||||
*/
|
||||
@BT.DecoratorNode({
|
||||
@BT.DecoratorNode("LimitTicks", {
|
||||
name: "次数限制器",
|
||||
group: "基础装饰节点",
|
||||
description: "限制子节点执行次数,超过次数返回失败",
|
||||
parameters: [
|
||||
{ name: "max", type: BT.ParamType.int, description: "最大次数", defaultValue: 1, required: true }
|
||||
]
|
||||
desc: "限制子节点执行次数,超过次数返回失败",
|
||||
})
|
||||
export class LimitTicks extends NumericDecorator {
|
||||
@BT.prop({ type: BT.ParamType.int, description: "最大次数", defaultValue: 1 })
|
||||
protected _max: number = 1;
|
||||
constructor(child: IBTNode, max: number = 1) {
|
||||
super(child);
|
||||
this._max = max;
|
||||
}
|
||||
|
||||
public tick(): Status {
|
||||
this._value++;
|
||||
if (this._value > this._max) {
|
||||
@@ -101,15 +105,18 @@ export class LimitTicks extends NumericDecorator {
|
||||
* 子节点是成功或失败,累加计数
|
||||
* 次数超过之后返回子节点状态,否则返回 RUNNING
|
||||
*/
|
||||
@BT.DecoratorNode({
|
||||
name: "重复器",
|
||||
@BT.DecoratorNode("Repeat", {
|
||||
name: "重复节点",
|
||||
group: "基础装饰节点",
|
||||
description: "重复执行子节点指定次数",
|
||||
parameters: [
|
||||
{ name: "max", type: BT.ParamType.int, description: "重复次数", defaultValue: 1, required: true }
|
||||
]
|
||||
desc: "重复执行子节点指定次数",
|
||||
})
|
||||
export class Repeat extends NumericDecorator {
|
||||
@BT.prop({ type: BT.ParamType.int, description: "重复次数", defaultValue: 1 })
|
||||
protected _max: number = 1;
|
||||
constructor(child: IBTNode, max: number = 1) {
|
||||
super(child);
|
||||
this._max = max;
|
||||
}
|
||||
public tick(): Status {
|
||||
// 执行子节点
|
||||
const status = this.children[0]!._execute();
|
||||
@@ -132,15 +139,18 @@ export class Repeat extends NumericDecorator {
|
||||
*
|
||||
* 子节点成功 计数+1
|
||||
*/
|
||||
@BT.DecoratorNode({
|
||||
@BT.DecoratorNode("RepeatUntilFailure", {
|
||||
name: "重复直到失败",
|
||||
group: "基础装饰节点",
|
||||
description: "重复执行子节点直到失败或达到最大次数",
|
||||
parameters: [
|
||||
{ name: "max", type: BT.ParamType.int, description: "最大重试次数", defaultValue: 1, required: true }
|
||||
]
|
||||
desc: "重复执行子节点直到失败或达到最大次数",
|
||||
})
|
||||
export class RepeatUntilFailure extends NumericDecorator {
|
||||
@BT.prop({ type: BT.ParamType.int, description: "最大重试次数", defaultValue: 1 })
|
||||
protected _max: number = 1;
|
||||
constructor(child: IBTNode, max: number = 1) {
|
||||
super(child);
|
||||
this._max = max;
|
||||
}
|
||||
public tick(): Status {
|
||||
const status = this.children[0]!._execute();
|
||||
if (status === Status.FAILURE) {
|
||||
@@ -164,15 +174,18 @@ export class RepeatUntilFailure extends NumericDecorator {
|
||||
*
|
||||
* 子节点失败, 计数+1
|
||||
*/
|
||||
@BT.DecoratorNode({
|
||||
@BT.DecoratorNode("RepeatUntilSuccess", {
|
||||
name: "重复直到成功",
|
||||
group: "基础装饰节点",
|
||||
description: "重复执行子节点直到成功或达到最大次数",
|
||||
parameters: [
|
||||
{ name: "max", type: BT.ParamType.int, description: "最大重试次数", defaultValue: 1, required: true }
|
||||
]
|
||||
desc: "重复执行子节点直到成功或达到最大次数",
|
||||
})
|
||||
export class RepeatUntilSuccess extends NumericDecorator {
|
||||
@BT.prop({ type: BT.ParamType.int, description: "最大重试次数", defaultValue: 1, step: 1 })
|
||||
protected _max: number = 1;
|
||||
constructor(child: IBTNode, max: number = 1) {
|
||||
super(child);
|
||||
this._max = max;
|
||||
}
|
||||
public tick(): Status {
|
||||
// 执行子节点
|
||||
const status = this.children[0]!._execute();
|
||||
@@ -193,15 +206,13 @@ export class RepeatUntilSuccess extends NumericDecorator {
|
||||
/**
|
||||
* 权重装饰节点
|
||||
*/
|
||||
@BT.DecoratorNode({
|
||||
@BT.DecoratorNode("WeightDecorator", {
|
||||
name: "权重装饰器",
|
||||
group: "基础装饰节点",
|
||||
description: "权重装饰节点",
|
||||
parameters: [
|
||||
{ name: "weight", type: BT.ParamType.float, description: "权重", defaultValue: 1, required: true }
|
||||
]
|
||||
desc: "权重装饰节点",
|
||||
})
|
||||
export class WeightDecorator extends Decorator {
|
||||
@BT.prop({ type: BT.ParamType.int, description: "权重", defaultValue: 1, step: 1 })
|
||||
private _weight: number;
|
||||
|
||||
constructor(child: IBTNode, weight?: number) {
|
||||
|
||||
Reference in New Issue
Block a user