在游戏开发中,以状态切换来驱动其执行流程的系统,引入行为树可以大大简化编码和配置 行为树会通过访问子节点,并根据子节点的返回值决定逻辑在树中的走向。 实际上就是可视化,规范化的与或非,子交并补,if else switch case for while 依据这个理念衍生出了很多不同的节点。
作为一个通用的行为树库,它还提供了可视化编辑器behavior3editor 文件简介说明 Behavior3.js库主要包含两个重要文件。如下附件:
前者为behavior3.js库文件,后者为用于typescript项目的dts解释文件,其中包含了行为树库中所有的接口和枚举。
接入文档
1: 引入库文件
将behavior3js.js放入项目工程中lib/third/文件夹下
web下在index.html中加入
<script type="text/javascript" src="libs/third/behavior3js.js"></script>
微信小程序下在game.js中加入
require("./third//behavior3js.js”)
2: 加入解释文件
将behavior3js.d.ts放入项目工程中libs/文件夹下
3: 行为树子节点逻辑绑定
通过将行为树中定义的标签节点与项目中实际执行逻辑进行绑定
绑定行为节点的方法
绑定条件判断节点的方法
name为行为树的标签字符串,properties为实际执行逻辑,形如
{
tick: function(tick: b3.Tick) {
}
}
其中tick为行为树执行触发函数,当运行至该节点时会调用,并传入b3.Tick对象,该对象中可获取执行target和节点间缓存容器blockbroad
4: 行为树初始化
其中jsonData为行为树在线编辑器导出的数据文件
treeNames为行为树子节点逻辑绑定集合
5:执行行为树
在线编辑器使用教程
简单介绍一下几类行为树节点的功能(这里的节点指树节点)
1.Composite 组合节点,可以连接多个子节点,用于决策行为树走向
2.Decorator 装饰节点, 可以连接一个子节点, 用于修饰子节点的返回值
3. Action 行为节点, 可以执行行为 (实际上也应该返回值)
4. Condition 条件节点, 可以用于做出判断并返回不同的值
上面四种节点是一种理念,不是一种实现上的限制,实际上他们除了可以拿到的参数不同,就没什么区别了
1.使用Tree as JSON作为导入和导出格式
2.使用New node进行创建自定义的行为和条件节点
3.节点表示
-
Sequence节点表示:顺序执行子节点,当子节点返回b3.SUCCESS直接访问下一个子节点,直到所有子节点都返回b3.SUCCESS, ,才从当前节点返回b3.SUCCESS,当子节点返回b3.FAILURE,则直接从当前节点返回b3.FAILURE,其余节点就不再执行,相当于AND
-
Priority节点表示:当子节点返回b3.FAILURE直接访问下一个子节点,直到所有子节点都返回b3.FAILURE, 才从当前节点返回b3.FAILURE,当子节点返回b3.SUCCESS,则直接从当前节点返回b3.SUCCESS,当子节点只要有一个成功,就返回,其余节点不再执行,相当于OR
-
MemSequence 节点 逻辑和Sequence一样,但是这里涉及到一个b3.RUNNING的返回值, 就是子节点并不知道执行结果是成功还是失败,我得晚一点才能告诉你,那如何处理这种情况咧? 对于Sequence节点,当它再次遍历子节点的时候,还是从第一个子节点开始遍历, 但是MemSequence却会记住之前在Running的子节点,然后下次遍历的时候, 直接从Running的那个子节点开始,这可以很方便的继续之前中断的逻辑决策。
-
MemPriority 节点 逻辑和Priority一样,然后Running的概念和MemSequence一样。
-
衍生节点
1.Repeator 节点,重复执行子节点maxLoop次 2.RepeatUntilFailure 节点 ,重复执行子节点直到子节点返回Faiilure 3.RepeatUntilSuccess 节点, 你懂的 4.Limiter 节点 限制进入子节点的上限次数为maxLoop 5.Inverter 节点 反转返回值 6.MaxTime 节点 限制子节点可以执行的时间为maxTime毫秒
1.Successder 节点 返回b3.SUCCESS
2.Failer节点 返回b3.FAILURE
3.Error节点 返回b3.ERROR(调试用)
4.Runner节点 返回b3.RUNNING
5.Wait 节点 等待milliseconds毫秒
完整流程使用demo
这是一个能够根据状态决定是否躲避,攻击,寻路的怪物ai
import BehaviorTree from "../../core/ai/behavior/BehaviorTree";
import { MonsterBTComp } from "../commponent/MonsterBTComp";
export enum MonsterTreeTitle{
attack = "attack",
idle = "idle",
patrol = "patrol",
dodge = "dodge",
flee = "flee",
lowLife = "lowLife",
attackDesire = "attackDesire",
watchBullet = "watchBullet",
onRange = "onRange",
}
export default class MonsterBehaviorTree extends BehaviorTree {
public constructor() {
super();
}
public init(configPath: string) {
this.treeNames = {};
this.tree = null;
this.initAction();
this.initConditions();
super.init(configPath);
}
public initAction() {
this.loadAction(MonsterTreeTitle.attack, {
tick: function(tick: b3.Tick) {
let target: MonsterBTComp = tick.target;
target.attack();
return b3.SUCCESS;
}
});
this.loadAction(MonsterTreeTitle.idle, {
tick: function(tick: b3.Tick) {
return b3.SUCCESS;
}
});
this.loadAction(MonsterTreeTitle.patrol, {
tick: function(tick: b3.Tick) {
let target: MonsterBTComp = tick.target;
target.patrol();
return b3.SUCCESS;
}
});
this.loadAction(MonsterTreeTitle.flee, {
tick: function(tick: b3.Tick) {
let target: MonsterBTComp = tick.target;
target.flee();
return b3.SUCCESS;
}
});
this.loadAction(MonsterTreeTitle.dodge, {
tick: function(tick: b3.Tick) {
let target: MonsterBTComp = tick.target;
target.dodge();
return b3.SUCCESS;
}
});
}
public initConditions() {
this.loadCondition(MonsterTreeTitle.watchBullet, {
tick: function(tick: b3.Tick) {
let target: MonsterBTComp = tick.target;
if (target.watchBullet()) {
return b3.SUCCESS;
} else {
return b3.FAILURE;
}
}
});
this.loadCondition(MonsterTreeTitle.onRange, {
tick: function(tick: b3.Tick) {
let target: MonsterBTComp = tick.target;
if (target.isOnRange()) {
return b3.SUCCESS;
} else {
return b3.FAILURE;
}
}
});
this.loadCondition(MonsterTreeTitle.lowLife, {
tick: function(tick: b3.Tick) {
let target: MonsterBTComp = tick.target;
if (target.lowLife()) {
return b3.SUCCESS;
} else {
return b3.FAILURE;
}
}
});
this.loadCondition(MonsterTreeTitle.attackDesire, {
tick: function(tick: b3.Tick) {
let target: MonsterBTComp = tick.target;
if (target.attackDesire()) {
return b3.SUCCESS;
} else {
return b3.FAILURE;
}
}
});
}
}