行为树 Behavior Tree 原理 一
行为树 结构图如下,一棵倒置的树
行为树采用节点描述行为逻辑,主要节点类型有:
组合节点:选择节点、顺序节点、随机选择节点、随机顺序节点、随机权重节点、并行节点、并行执行所有节点、if 判断并行节点、if 判断顺序节点
修饰节点:修饰节点取反、修饰节点重复、修饰节点_返回 Fail、修饰节点_返回 Success、修饰节点_直到 Fail、修饰节点_直到 Success
条件节点
行为节点
一棵行为树表示一个AI逻辑。执行这个 AI 逻辑,需要从根节点开始深度优先遍历执行节点,执行过程中,节点根据自身的类型确定执行逻辑或调用子节点,节点将执行结果反馈给父节点。
从结构上节点可分为:组合节点、叶节点 (单节点)。
组合节点就是拥有子节点的节点。
叶节点 (单节点)就是不能添加子节点的节点。
其中叶节点 :条件节点、行为节点。其他为组合节点
如下图一个行为树示例图:
行为树节点顺序如下,
节点1 为根节点, 节点2、节点5为中间节点,节点3、4、6、7,为子节点
节点 1、2、5为组合节点,节点3、4、6、7为单节点
节点执行结果
执行结果可分为三种:Fail、Success、Running
。
Fail
: 节点执行失败(如:条件节点判定为 false、执行节点失败、没有符合执行的节点等)
Success
:节点执行成功(如:条件节点判定为 true、执行节点成功等)
Running
:节点执行中(如:正在跑向目标、正在吃饭、动画播放中等)
行为树节点
叶节点
-
条件节点
条件节点(Condition)即是条件判定,例如:“是否饿了?”、“是否有饭?”、“是否有作业”等。
如果条件测试结果为真,向父节点返回 Success,否则返回 Fail。
条件节点为叶子节点 -
行为节点
行为节点(Action)用来执行实际的工作,如:做饭、吃饭、打球、写作业等。
行为节点有些可能一帧就可以执行完成,有些可能需要多帧才能完成。
行为节点执行过程中失败,向父节点返回 Fail,
执行完毕(成功)向父节点返回 Success,
正在执行中向父节点返回 Running。
组合节点
组合节点用来控制树的遍历方式,每种组合节点的遍历方式都不相同。下面是各个组合节点的执行逻辑
1.选择节点
选择节点:依次从头顺次遍历执行所有子节点
当前执行节点返回 Success,退出停止,向父节点
返回 Success
当前执行节点返回 Fail,退出当前节点
继续执行下一个节点
当前执行节点返回 Running, 记录当前节点,向父节
点返回 Running,下次执行直接从该节点开始
如果所有节点都返回Fail,执行完所有节点后
向父节点返回 Fail
2.顺序节点
顺序节点:依次执行子节点
当前执行节点返回 Success,就继续执行后续节点
当前执行节点返回 Fail,退出停止,向父节点
返回 Fail,下次执行直接从第一个节点开始
当前执行节点返回 Running, 记录当前节点,向父节
点返回 Running,下次执行直接从该节点开始
如果所有节点都返回 Success,向父节点返回 Success
3.随机选择节点
随机选择节点:(参考选择节点)
每次随机一个未执行的节点,总随机次数为子节点个数
当前执行节点返回 Success,退出停止
向父节点返回 Success
当前执行节点返回 Fail,退出当前节点
继续随机一个未执行的节点开始执行
当前执行节点返回 Running, 记录当前节点
向父节点返回 Running,下次执行直接从该节点开始
如果所有节点都返回Fail,执行完所有节点后
向父节点返回 Fail
4.随机顺序节点
随机顺序节点:(参考顺序节点)
每次随机一个未执行的节点,总随机次数为子节点个数
当前执行节点返回 Success,继续随机一个未执行的节点
当前执行节点返回 Fail,退出停止
向父节点返回 Fail
当前执行节点返回 Running, 记录当前节点
向父节点返回 Running,下次执行直接从该节点开始
如果所有节点都返回 Success
向父节点返回 Success
5.随机权重节点
随机权重节点:(参考随机选择节点)
每次根据节点权重随机一个未执行的节点
总随机次数为子节点个数
当前执行节点返回 Success,退出停止
向父节点返回 Success
当前执行节点返回 Fail,退出当前节点
继续随机一个未执行的节点开始执行
当前执行节点返回 Running, 记录当前节点
向父节点返回 Running
下次执行直接从该节点开始
如果所有节点都返回Fail,执行完所有节点后
向父节点返回 Fail
6.并行节点
并行节点:依次从头顺次遍历执行所有子节点
当前执行节点返回 Fail,退出停止,向父节点
返回 Fail
当前执行节点返回 Success,记录当前节点,继续
执行下一个节点,记录所有返回 Success 的节点
当前执行节点返回 Running, 记录当前节点,继续
执行下一个节点,记录所有返回 Running 的节点
如果没有节点返回 Fail
如果所有节点都返回 Success 向父节点返回 Success
否则向父节点返回 Running
7.并行执行所有节点
并行执行所有节点:依次从头顺次遍历执行所有子节点
当前执行节点返回 Success、 Fail、Running 都继续
执行下一个节点,分别记录返回三种结果的节点个数
执行完所有节点后
如果所有节点都返回 Success 向父节点返回 Success
如果所有节点都返回 Fail 向父节点返回 Fail
否则一定有节点返回了Running 向父节点返回 Running
8.if 判断并行节点
if判断并行节点:
只能有 二或者三个子节点
第一个为判断节点只能返回Success、Fail
如下方配置三个节点
第一个节点返回 Success时,执行 第二个节点
第一个节点返回 Fail 时,执行 第三个节点
或者配置两个节点
第一个节点返回 Success时,执行 第二个节点
因为是并行节点,每次执行都会先执行第一个节点
根据第一个节点返回 (Success 或者 Fail)选择执行第M个节点
执行第一个条件节点,记录返回结果 result
根据配置 判断 执行第 M(二、三) 节点
if N 是空 then
执行 第 M 节点的 Enter
elseif N 不等于 M then
执行 第 N 节点的 OnExit
执行 第 M 节点的 Enter
end
记录当前执行节点:令 N = M
执行 第 N 节点的 Execute 并记录结果 result
if result == Success then
令 N = nil
return 向父节点返回 Success
elseif result == Fail then
令 N = nil
return 向父节点返回 Fail
else -- result == Running
return 向父节点返回 Running
end
9.if 判断顺序节点
if判断顺序节点:
只能有 二或者三个子节点
第一个为判断节点只能返回Success、Fail
配置同上第8条
因为是顺序节点,每次执行时
如果当前有正在执行的第二、第三个节点则
直接执行它的 Execute
如果没有,则执行第一个节点,根据第一个节点返回
根据第一个节点返回 (Success 或者 Fail)选择执行第M个节点
if N 是空 then
执行第一个条件节点,记录返回结果 result
根据配置 判断 执行第 M(二、三) 节点
执行 第 M 节点的 Enter
记录当前执行节点:令 N = M
elseif N 不等于 M then
执行 第 N 节点的 OnExit
执行 第 M 节点的 Enter
end
记录当前执行节点:令 N = M
执行 第 N 节点的 Execute 并记录结果 result
if result == Success then
令 N = nil
return 向父节点返回 Success
elseif result == Fail then
令 N = nil
return 向父节点返回 Fail
else -- result == Running
return 向父节点返回 Running
end
10.修饰节点取反
取反修饰节点 Inverter 对子节点执行结果取反
11.修饰节点重复
修饰节点_重复:
开始执行该节点时,将记录次数清零
顺序执行所有子节点(记为 1 次),不关心节点返回结果
如果 执行次数 < 配置执行次数 向父节点返回 Running
如果 执行次数 >= 配置执行次数 向父节点返回 Success
12.修饰节点_返回 Fail
修饰_返回Fail:
执行节点,无论节点返回 Success、Fail、Running
执行结束后永远向父节点返回 Fail
13.修饰节点_返回 Success
修饰_返回Success:
执行节点,无论节点返回 Success、Fail、Running
执行结束后永远向父节点返回 Success
14.修饰节点_直到 Fail
修饰_直到Fail:
执行节点
如果节点返回结果不是 Fail
向父节点返回 Running
直到节点返回 Fail,向父节点返回 Success
15.修饰节点_直到 Success
修饰_直到Success:
执行节点
如果节点返回结果不是 Success
向父节点返回 Running
直到节点返回 Success,向父节点返回 Success