行为树逻辑调整,删除非记忆的选择和顺序节点

This commit is contained in:
gongxh
2025-10-03 18:09:47 +08:00
parent 63d9855658
commit 9a3e7028d2
9 changed files with 167 additions and 243 deletions

View File

@@ -1,66 +0,0 @@
/**
* @Author: Gongxh
* @Date: 2025-09-01
* @Description: 抽象节点基类
*/
import { IBlackboard } from "../Blackboard";
import { BTNode, IBTNode } from "./BTNode";
/**
* 叶子节点 基类
* 没有子节点
*/
export abstract class LeafNode extends BTNode {
constructor() {
super([]);
}
}
/**
* 修饰节点 基类
* 有且仅有一个子节点
*/
export abstract class Decorator extends BTNode {
constructor(child: IBTNode) {
super([child]);
}
}
/**
* 组合节点 基类
* 多个子节点
*/
export abstract class Composite extends BTNode {
constructor(...children: IBTNode[]) {
super(children);
}
}
/**
* 数值型修饰节点 基类
* 包含最大值和当前值的通用逻辑,适用于所有需要数值计数的修饰节点
*/
export abstract class NumericDecorator extends Decorator {
protected _value: number = 0;
protected override open(): void {
super.open();
this._value = 0;
}
}
/**
* 记忆修饰节点基类
* 只有记忆节点才需要设置局部数据
*/
export abstract class MemoryComposite extends Composite {
public override _initialize(global: IBlackboard, branch: IBlackboard): void {
super._initialize(global, branch);
this._local = branch.createChild();
}
protected override open(): void {
super.open();
this.set(`__nMemoryRunningIndex`, 0);
}
}

View File

@@ -1,6 +1,16 @@
import { BT } from "../BT";
import { Status } from "../header";
import { LeafNode } from "./AbstractNodes";
import { BTNode } from "./BTNode";
/**
* 叶子节点 基类
* 没有子节点
*/
export abstract class LeafNode extends BTNode {
constructor() {
super([]);
}
}
/**
* 次数等待节点(无子节点)
@@ -56,12 +66,12 @@ export class WaitTime extends LeafNode {
protected override open(): void {
super.open();
this._value = new Date().getTime();
this._value = 0;
}
public tick(): Status {
const currTime = new Date().getTime();
if (currTime - this._value >= this._max * 1000) {
public tick(dt: number): Status {
this._value += dt;
if (this._value >= this._max) {
return Status.SUCCESS;
}
return Status.RUNNING;

View File

@@ -15,8 +15,8 @@ export interface IBTNode {
/**
* @internal
*/
_execute(): Status;
tick(): Status;
_execute(dt: number): Status;
tick(dt: number): Status;
/**
* 优先写入自己的黑板数据, 如果没有则写入父节点的黑板数据
@@ -67,7 +67,7 @@ export abstract class BTNode implements IBTNode {
/**
* @internal
*/
public _execute(): Status {
public _execute(dt: number): Status {
// 首次执行时初始化
const isRunning = this._local.openNodes.get(this) || false;
if (!isRunning) {
@@ -76,7 +76,7 @@ export abstract class BTNode implements IBTNode {
}
// 执行核心逻辑
const status = this.tick();
const status = this.tick(dt);
// 执行完成时清理
if (status !== Status.RUNNING) {
@@ -98,7 +98,7 @@ export abstract class BTNode implements IBTNode {
* 子类必须实现此方法
* @returns 执行状态
*/
public abstract tick(): Status;
public abstract tick(dt: number): Status;
/**
* 清理节点(执行完成时调用)

View File

@@ -1,9 +1,19 @@
import { IBlackboard } from "../Blackboard";
import { BT } from "../BT";
import { Status } from "../header";
import { Composite, MemoryComposite } from "./AbstractNodes";
import { IBTNode } from "./BTNode";
import { BTNode, IBTNode } from "./BTNode";
import { WeightDecorator } from "./Decorator";
/**
* 组合节点基类
* 有多个子节点
*/
export abstract class Composite extends BTNode {
constructor(...children: IBTNode[]) {
super(children);
}
}
/**
* 记忆选择节点 从上到下执行
* 遇到 FAILURE 继续下一个
@@ -11,23 +21,29 @@ import { WeightDecorator } from "./Decorator";
*
* 遇到 RUNNING 返回 RUNNING 下次从该节点开始
*/
@BT.CompositeNode("MemSelector", {
name: "记忆选择节点",
group: "基础组合节点",
desc: "记住上次运行位置的选择节点,从记忆位置开始执行",
})
export class MemSelector extends MemoryComposite {
public tick(): Status {
let index = this.get<number>(`__nMemoryRunningIndex`);
@BT.CompositeNode("Selector", { name: "选择节点", group: "基础组合节点", desc: "选择节点" })
export class Selector extends Composite {
public override _initialize(global: IBlackboard, branch: IBlackboard): void {
super._initialize(global, branch);
this._local = branch.createChild();
}
protected override open(): void {
super.open();
this.set(`__nRunningIndex`, 0);
}
public tick(dt: number): Status {
let index = this.get<number>(`__nRunningIndex`);
for (let i = index; i < this.children.length; i++) {
let status = this.children[i]!._execute();
let status = this.children[i]!._execute(dt);
if (status === Status.FAILURE) {
continue;
}
if (status === Status.SUCCESS) {
return status;
}
this.set(`__nMemoryRunningIndex`, i);
this.set(`__nRunningIndex`, i);
return Status.RUNNING;
}
return Status.FAILURE;
@@ -35,29 +51,35 @@ export class MemSelector extends MemoryComposite {
}
/**
* 记忆顺序节点 从上到下执行
* 顺序节点 从上到下执行
* 遇到 SUCCESS 继续下一个
* 遇到 FAILURE 停止迭代 返回 FAILURE 下次重新开始
*
* 遇到 RUNNING 返回 RUNNING 下次从该节点开始
*/
@BT.CompositeNode("MemSequence", {
name: "记忆顺序节点",
group: "基础组合节点",
desc: "记住上次运行位置的序列节点,从记忆位置开始执行",
})
export class MemSequence extends MemoryComposite {
public tick(): Status {
let index = this.get<number>(`__nMemoryRunningIndex`);
@BT.CompositeNode("Sequence", { name: "顺序节点", group: "基础组合节点", desc: "顺序节点" })
export class Sequence extends Composite {
public override _initialize(global: IBlackboard, branch: IBlackboard): void {
super._initialize(global, branch);
this._local = branch.createChild();
}
protected override open(): void {
super.open();
this.set(`__nRunningIndex`, 0);
}
public tick(dt: number): Status {
let index = this.get<number>(`__nRunningIndex`);
for (let i = index; i < this.children.length; i++) {
let status = this.children[i]!._execute();
let status = this.children[i]!._execute(dt);
if (status === Status.SUCCESS) {
continue;
}
if (status === Status.FAILURE) {
return Status.FAILURE;
}
this.set(`__nMemoryRunningIndex`, i);
this.set(`__nRunningIndex`, i);
return Status.RUNNING;
}
return Status.SUCCESS;
@@ -65,24 +87,22 @@ export class MemSequence extends MemoryComposite {
}
/**
* 选择节点 从上到下执行
* 返回第一个不为 FAILURE 的子节点状态
* 否则返回 FAILURE
* 并行节点 从上到下执行 全部执行一遍
* 返回优先级 FAILURE > RUNNING > SUCCESS
*/
@BT.CompositeNode("Selector", {
name: "选择节点",
group: "基础组合节点",
desc: "依次执行子节点,直到找到成功或运行中的节点",
})
export class Selector extends Composite {
public tick(): Status {
@BT.CompositeNode("Parallel", { name: "并行节点", group: "基础组合节点", desc: "同时执行所有子节点,全部成功才返回成功" })
export class Parallel extends Composite {
public tick(dt: number): Status {
let result = Status.SUCCESS;
for (let i = 0; i < this.children.length; i++) {
let status = this.children[i]!._execute();
if (status !== Status.FAILURE) {
return status;
let status = this.children[i]!._execute(dt);
if (result === Status.FAILURE || status === Status.FAILURE) {
result = Status.FAILURE;
} else if (status === Status.RUNNING) {
result = Status.RUNNING;
}
}
return Status.FAILURE;
return result;
}
}
@@ -117,7 +137,7 @@ export class RandomSelector extends Composite {
return (child instanceof WeightDecorator) ? (child.weight) : 1;
}
public tick(): Status {
public tick(dt: number): Status {
if (this.children.length === 0) {
return Status.FAILURE;
}
@@ -139,58 +159,11 @@ export class RandomSelector extends Composite {
left = mid + 1;
}
}
const status = this.children[childIndex]!._execute();
const status = this.children[childIndex]!._execute(dt);
return status;
}
}
/**
* 顺序节点 从上到下执行
* 遇到 SUCCESS 继续下一个
* 否则返回子节点状态
*/
@BT.CompositeNode("Sequence", {
name: "顺序节点",
group: "基础组合节点",
desc: "依次执行所有子节点,全部成功才返回成功",
})
export class Sequence extends Composite {
public tick(): Status {
for (let i = 0; i < this.children.length; i++) {
let status = this.children[i]!._execute();
if (status === Status.SUCCESS) {
continue;
}
return status;
}
return Status.SUCCESS;
}
}
/**
* 并行节点 从上到下执行 全部执行一遍
* 返回优先级 FAILURE > RUNNING > SUCCESS
*/
@BT.CompositeNode("Parallel", {
name: "并行节点",
group: "基础组合节点",
desc: "同时执行所有子节点,全部成功才返回成功",
})
export class Parallel extends Composite {
public tick(): Status {
let result = Status.SUCCESS;
for (let i = 0; i < this.children.length; i++) {
let status = this.children[i]!._execute();
if (result === Status.FAILURE || status === Status.FAILURE) {
result = Status.FAILURE;
} else if (status === Status.RUNNING) {
result = Status.RUNNING;
}
}
return result;
}
}
/**
* 并行节点 从上到下执行 全部执行一遍
* 返回优先级 SUCCESS > RUNNING > FAILURE
@@ -201,10 +174,10 @@ export class Parallel extends Composite {
desc: "同时执行所有子节点,任意一个成功即返回成功",
})
export class ParallelAnySuccess extends Composite {
public tick(): Status {
public tick(dt: number): Status {
let result = Status.FAILURE;
for (let i = 0; i < this.children.length; i++) {
let status = this.children[i]!._execute();
let status = this.children[i]!._execute(dt);
if (result === Status.SUCCESS || status === Status.SUCCESS) {
result = Status.SUCCESS;
} else if (status === Status.RUNNING) {

View File

@@ -5,7 +5,7 @@
*/
import { Status } from "../header";
import { Decorator, LeafNode } from "./AbstractNodes";
import { LeafNode } from "./Action";
/** 条件叶子节点 */
export abstract class Condition extends LeafNode {
@@ -19,16 +19,3 @@ export abstract class Condition extends LeafNode {
return this.isEligible() ? Status.SUCCESS : Status.FAILURE;
}
}
/** 条件装饰节点 */
export abstract class ConditionDecorator extends Decorator {
/**
* 判断是否满足条件
* @returns 是否满足条件
*/
protected abstract isEligible(): boolean;
public tick(): Status {
return this.isEligible() ? this.children[0]!._execute() : Status.FAILURE;
}
}

View File

@@ -6,8 +6,30 @@
import { BT } from "../BT";
import { Status } from "../header";
import { Decorator, NumericDecorator } from "./AbstractNodes";
import { IBTNode } from "./BTNode";
import { BTNode, IBTNode } from "./BTNode";
/**
* 修饰节点 基类
* 有且仅有一个子节点
*/
export abstract class Decorator extends BTNode {
constructor(child: IBTNode) {
super([child]);
}
}
/** 条件装饰节点基类 */
export abstract class ConditionDecorator extends Decorator {
/**
* 判断是否满足条件
* @returns 是否满足条件
*/
protected abstract isEligible(): boolean;
public tick(dt: number): Status {
return this.isEligible() ? this.children[0]!._execute(dt) : Status.FAILURE;
}
}
/**
* 结果反转节点
@@ -15,14 +37,10 @@ import { IBTNode } from "./BTNode";
* 第一个Child Node节点, 返回 FAILURE, 本Node向自己的Parent Node也返回 SUCCESS
* 第一个Child Node节点, 返回 SUCCESS, 本Node向自己的Parent Node也返回 FAILURE
*/
@BT.DecoratorNode("Inverter", {
name: "反转器",
group: "基础装饰节点",
desc: "反转子节点的执行结果,成功变失败,失败变成功",
})
@BT.DecoratorNode("Inverter", { name: "反转器", group: "基础装饰节点", desc: "反转子节点的执行结果,成功变失败,失败变成功" })
export class Inverter extends Decorator {
public tick(): Status {
const status = this.children[0]!._execute();
public tick(dt: number): Status {
const status = this.children[0]!._execute(dt);
if (status === Status.SUCCESS) {
return Status.FAILURE;
@@ -40,15 +58,12 @@ export class Inverter extends Decorator {
* 规定时间内, 根据Child Node的结果, 本节点向自己的父节点也返回相同的结果
* 超时后, 直接返回 FAILURE
*/
@BT.DecoratorNode("LimitTime", {
name: "时间限制器",
group: "基础装饰节点",
desc: "限制子节点执行时间,超时返回失败",
})
export class LimitTime extends NumericDecorator {
@BT.DecoratorNode("LimitTime", { name: "时间限制器", group: "基础装饰节点", desc: "限制子节点执行时间,超时返回失败" })
export class LimitTime extends Decorator {
@BT.prop({ type: BT.ParamType.float, description: "最大时间(秒)", defaultValue: 1 })
protected _max: number = 1;
private _value: number = 0;
/**
* 时间限制节点
* @param child 子节点
@@ -60,15 +75,15 @@ export class LimitTime extends NumericDecorator {
}
protected override open(): void {
this._value = Date.now();
this._value = 0;
}
public tick(): Status {
const currentTime = Date.now();
if (currentTime - this._value > this._max * 1000) {
public tick(dt: number): Status {
this._value += dt;
if (this._value > this._max) {
return Status.FAILURE;
}
return this.children[0]!._execute();
return this.children[0]!._execute(dt);
}
}
@@ -77,25 +92,27 @@ export class LimitTime extends NumericDecorator {
* 必须且只能包含一个子节点
* 次数超过后, 直接返回失败; 次数未超过, 返回子节点状态
*/
@BT.DecoratorNode("LimitTicks", {
name: "次数限制器",
group: "基础装饰节点",
desc: "限制子节点执行次数,超过次数返回失败",
})
export class LimitTicks extends NumericDecorator {
@BT.DecoratorNode("LimitTicks", { name: "次数限制器", group: "基础装饰节点", desc: "限制子节点执行次数,超过次数返回失败" })
export class LimitTicks extends Decorator {
@BT.prop({ type: BT.ParamType.int, description: "最大次数", defaultValue: 1 })
protected _max: number = 1;
private _value: number = 0;
constructor(child: IBTNode, max: number = 1) {
super(child);
this._max = max;
}
public tick(): Status {
protected override open(): void {
this._value = 0;
}
public tick(dt: number): Status {
this._value++;
if (this._value > this._max) {
return Status.FAILURE;
}
return this.children[0]!._execute();
return this.children[0]!._execute(dt);
}
}
@@ -105,21 +122,24 @@ export class LimitTicks extends NumericDecorator {
* 子节点是成功或失败,累加计数
* 次数超过之后返回子节点状态,否则返回 RUNNING
*/
@BT.DecoratorNode("Repeat", {
name: "重复节点",
group: "基础装饰节点",
desc: "重复执行子节点指定次数",
})
export class Repeat extends NumericDecorator {
@BT.DecoratorNode("Repeat", { name: "重复节点", group: "基础装饰节点", desc: "重复执行子节点指定次数" })
export class Repeat extends Decorator {
@BT.prop({ type: BT.ParamType.int, description: "重复次数", defaultValue: 1 })
protected _max: number = 1;
private _value: number = 0;
constructor(child: IBTNode, max: number = 1) {
super(child);
this._max = max;
}
public tick(): Status {
protected override open(): void {
this._value = 0;
}
public tick(dt: number): Status {
// 执行子节点
const status = this.children[0]!._execute();
const status = this.children[0]!._execute(dt);
// 如果子节点完成(成功或失败),增加计数
if (status === Status.SUCCESS || status === Status.FAILURE) {
this._value++;
@@ -139,20 +159,23 @@ export class Repeat extends NumericDecorator {
*
* 子节点成功 计数+1
*/
@BT.DecoratorNode("RepeatUntilFailure", {
name: "重复直到失败",
group: "基础装饰节点",
desc: "重复执行子节点直到失败或达到最大次数",
})
export class RepeatUntilFailure extends NumericDecorator {
@BT.DecoratorNode("RepeatUntilFailure", { name: "重复直到失败", group: "基础装饰节点", desc: "重复执行子节点直到失败或达到最大次数" })
export class RepeatUntilFailure extends Decorator {
@BT.prop({ type: BT.ParamType.int, description: "最大重试次数", defaultValue: 1 })
protected _max: number = 1;
private _value: number = 0;
constructor(child: IBTNode, max: number = 1) {
super(child);
this._max = max;
}
public tick(): Status {
const status = this.children[0]!._execute();
protected override open(): void {
this._value = 0;
}
public tick(dt: number): Status {
const status = this.children[0]!._execute(dt);
if (status === Status.FAILURE) {
return Status.FAILURE;
}
@@ -174,21 +197,24 @@ export class RepeatUntilFailure extends NumericDecorator {
*
* 子节点失败, 计数+1
*/
@BT.DecoratorNode("RepeatUntilSuccess", {
name: "重复直到成功",
group: "基础装饰节点",
desc: "重复执行子节点直到成功或达到最大次数",
})
export class RepeatUntilSuccess extends NumericDecorator {
@BT.DecoratorNode("RepeatUntilSuccess", { name: "重复直到成功", group: "基础装饰节点", desc: "重复执行子节点直到成功或达到最大次数" })
export class RepeatUntilSuccess extends Decorator {
@BT.prop({ type: BT.ParamType.int, description: "最大重试次数", defaultValue: 1, step: 1 })
protected _max: number = 1;
private _value: number = 0;
constructor(child: IBTNode, max: number = 1) {
super(child);
this._max = max;
}
public tick(): Status {
protected override open(): void {
this._value = 0;
}
public tick(dt: number): Status {
// 执行子节点
const status = this.children[0]!._execute();
const status = this.children[0]!._execute(dt);
if (status === Status.SUCCESS) {
return Status.SUCCESS;
}
@@ -206,11 +232,7 @@ export class RepeatUntilSuccess extends NumericDecorator {
/**
* 权重装饰节点
*/
@BT.DecoratorNode("WeightDecorator", {
name: "权重装饰器",
group: "基础装饰节点",
desc: "权重装饰节点",
})
@BT.DecoratorNode("WeightDecorator", { name: "权重装饰器", group: "基础装饰节点", desc: "权重装饰节点" })
export class WeightDecorator extends Decorator {
@BT.prop({ type: BT.ParamType.int, description: "权重", defaultValue: 1, step: 1 })
private _weight: number;
@@ -221,8 +243,8 @@ export class WeightDecorator extends Decorator {
this._weight = weight || 1;
}
public tick(): Status {
return this.children[0]!._execute();
public tick(dt: number): Status {
return this.children[0]!._execute(dt);
}
public get weight(): number {

View File

@@ -34,8 +34,8 @@ export class BehaviorTree<T> {
/**
* 执行行为树
*/
public tick(): Status {
return this._root._execute();
public tick(dt: number): Status {
return this._root._execute(dt);
}
/**

View File

@@ -8,7 +8,7 @@ import { BehaviorTree } from "./BehaviorTree";
import { BT } from "./BT";
import { IBTNode } from "./BTNode/BTNode";
interface INodeConfig {
export interface INodeConfig {
id: string,
className: string,
parameters: Record<string, any>,

View File

@@ -2,14 +2,12 @@
/** 行为树 */
export { BehaviorTree } from "./behaviortree/BehaviorTree";
export { Blackboard } from "./behaviortree/Blackboard";
export { } from "./behaviortree/BT";
export * from "./behaviortree/BTNode/AbstractNodes";
export * from "./behaviortree/BTNode/Action";
export { IBTNode } from "./behaviortree/BTNode/BTNode";
export * from "./behaviortree/BTNode/Composite";
export * from "./behaviortree/BTNode/Condition";
export * from "./behaviortree/BTNode/Decorator";
export { createBehaviorTree } from "./behaviortree/Factory";
export { createBehaviorTree, INodeConfig } from "./behaviortree/Factory";
export { Status } from "./behaviortree/header";
// 导出装饰器内容