行为树BT设计与实现

  • 1 介绍
  • 2 汇总
    • From McYY
      • 整体结构
      • 运行逻辑
      • 节点共享数据
      • 中断的实现
    • From AKara
    • From Luyu Huang
    • From 阿高
    • From xiyoo0812
  • 参考

1 介绍

状态机与行为树BT

2 汇总

From McYY

lua行为树设计与实现
LuaBT

项目需要,之前行为树用的是behaviorDesigner,要改成纯lua的。先做了一版用递归实现,代码可读性高但是中断机制实现起来比较复杂,而且创建自定义action重写方法时需要调用父类的方法, 如果忘了调用就会出现问题, 所以改成了用栈模拟递归。

用栈模拟递归好处在于效率高,并且容易控制,用非递归实现后自定义一个行为树节点,那么该节点不用知道父亲的方法,只要做好自己的事情就OK了,完整测试工程已上传到了github:https://github.com/MCxYY/LuaBT

整体结构



其中BTManager存储着所有行为树BTree,unity每帧调用BTManager的Update,而BTManager调用所有运行中的BTree的Update,BTree管理着自身的节点Task,根据逻辑执行调用Task的OnAwake()、OnStart等
Shared是节点共享数据,在后文中讲述
Task的OnAwake是整颗行为树激活时运行一次
OnStart是进入该Task时运行一次
OnUpdate是该Task执行中时每帧运行一次
OnPause(bPause)是整棵行为树暂停或者从暂停中苏醒时运行,bPause为true则暂停
OnEnd()是该Task退出时运行一次

运行逻辑

行为树(BTree)启动的时候调用BTree.Init()方法先序遍历该树,获得一系列节点数据,比如赋值儿子index,每个节点的儿子index是什么,每个节点的父亲index等。
行为树(BTree)中存储着一个list<stack>,这个是运行栈,行为树启动时创建一个运行栈,塞进去树根;每当有并行分支,则创建一个运行栈,塞进去分支第一个运行的节点。

节点(Task)的状态有四种:
1、ETaskStatus.Inactive //未激活
2、ETaskStatus.Failure //失败
3、ETaskStatus.Success //成功
4、ETaskStatus.Running //运行中
运行栈中放的节点都是处于Running状态,update时遍历运行栈,取出栈顶节点执行,如果节点执行完毕后状态不等于running,说明该节点不需要再次运行,那么就出栈。

节点运行的时候
如果该节点是ParentTask类型则需要运行儿子,其状态由儿子执行完毕后的状态来决定;
如果该节点是Task类型没有儿子,那么其状态就是其Update的状态;

节点共享数据

节点共享数据分为三种:一,树之间任意节点全局共享的数据 二,树内任意节点共享的数据 三,节点内不共享数据
节点内数据那就不用说了,在节点内声明的数据都是节点内数据
BehaviorDesigner的共享数据是通过编辑器保存读取的
由于时间不允许,没有做编辑器,所以我就做了个存储的类简单的实现了下
Shared.lua就是存储的类,其实里面就是一个table,对外只提供一个GetData(name)的方法,如果没有name的变量就创建个值为空的table保存起来,返回这个table。之所以用table存,是因为table在lua中属于引用类型。

中断的实现

中断的实现应该是行为树中比较复杂的功能了,涉及到树上的一些算法及运行栈的操作,牵涉到的判断也多,这里会重点讲述。

中断必要的原因是可能存在以下情况(不限于此情况):
比如怪物正在向目标点移动的时候遇到玩家需要攻击,此时移动的节点状态是running,没有中断的时候只能走到目标点的时候返回success停止移动才开始进入其他节点,这时候就错过了攻击玩家,中断的作用就体现出来了,就算是在running状态也能打断运行栈进入攻击节点。
BehaviorDesigner打断的方法是将打断类型分为这么几类:
EAbortType = {
  None = 0, //不打断
  Self = 1, //打断自身
  LowerPriority = 2, //打断低优先级
  Both = 3, //同时包括Self和LowerPriority两种效果
}
其中只有Composite类型的节点可以拥有打断操作。
Self打断类型:指的是Composite节点下面的直系子孙(这个名词是我临时取得。。意思是Composite与Conditional中间可以插入Decorate,可以插入Composite但插入得Composite类型必须是Self或Both)Conditional类型的节点的状态发生变化时,那么打断正在运行且是Composite子孙的分支,重新进入变化的Conditional节点所处的分支中。打断的结构大概如下图所示:

(绿色的指正在运行中的节点)

From AKara

使用行为树(Behavior Tree)实现游戏AI

谈到游戏AI,很明显智能体拥有的知识条目越多,便显得更智能,但维护庞大数量的知识条目是个噩梦:使用有限状态机(FSM),分层有限状态机(HFSM),决策树(Decision Tree)来实现游戏AI总有那么些不顺意。试试Next-Gen AI的行为树(Behavior Tree)吧。

行为树(Behavior Tree)具有如下的特性:
有4大类型的Node:

  • Composite Node
  • Decorator Node
  • Condition Node
  • Action Node

任何Node被执行后,必须向其Parent Node报告执行结果:成功 / 失败。
这简单的成功 / 失败汇报原则被很巧妙地用于控制整棵树的决策方向。

From Luyu Huang


在笔者的项目中 NPC 要有自动化的行为, 例如怪物的巡逻, 寻敌和攻击, 宠物的跟随和战斗等. 完成这些需求最好的做法是使用行为树(Behavior Tree)。
行为树首先是一棵树, 它有着标准的树状结构: 每个结点有零个或多个子结点, 没有父结点的结点称为根结点, 每一个非根结点有且只有一个父结点。 在行为树中, 每个节点都可以被执行, 并且返回 Success, Failure 或 Running, 分别表示成功, 失败或正在运行。行为树会每隔一段时间执行一下根结点, 称为 tick。 当一个节点被执行时, 它往往会按照一定的规则执行自己的子节点, 然后又按照一定的规则根据子节点的返回在确定它自己的返回值。行为树通常有 4 种控制流节点(Sequence 节点, Fallback 节点, Parallel 节点和 Decorator 节点)和 2 种执行节点(动作节点和条件节点):

  • Sequence 节点(顺序
    每当 Sequence 节点被执行时, 它都会依次执行它的子节点, 直到有一个子节点返回 Failure 或 Running. Sequence 节点的返回值就是最后一个子节点的返回值。
    Sequence 节点有点像逻辑与的操作: 只有所有的节点返回成功它才返回成功。 我们通常用符号 “→” 表示 Sequence 节点.

  • Fallback 节点
    每当 Fallback 节点被执行时, 它都会依次执行它的子节点, 直到有一个子节点返回 Success 或 Running. Fallback 节点的返回值就是最后一个子节点的返回值。
    与 Sequence 节点相反, Fallback 节点有点像逻辑或的操作: 只要有一个节点返回成功它就返回成功. 我们通常用符号 “?” 表示 Fallback 节点。

  • Parallel 节点(并行
    每当 Parallel 节点被执行时, 它都会执行它所有的子节点。如果有至少 M 个节点返回 Success, Parallel 节点就返回 Success; 如果有至少 N - M + 1 个节点返回 Failure, Parallel 节点就返回 Failure, 这里 N 是其子节点的数量; 否则返回 Running。
    我们通常用符号 “⇉” 表示 Parallel 节点。

  • Decorator 节点(修饰
    有的时候会有一些特殊的需求, 需要用自己的方式执行子节点和处理其返回结果。 Decorator 节点就是为此而设计的, 它的行为都是自定义的. 可以说, Sequence, Fallback 和 Parallel 节点都是特殊的 Decorator 节点。 我们通常用 “δ” 表示 Decorator 节点。

  • 动作节点和条件节点
    一般来说, 动作节点和条件节点是行为树中的叶子节点, 它们都是根据具体需求具体实现的. 当动作节点被执行时, 它会执行一个具体的动作, 视情况返回 Success, Failure 或 Running. 当条件节点被执行时, 它会做一些条件判断, 返回 Success 或 Failure. 行为树并不关心一个节点具体做了什么事 – 是所谓的 “执行动作” 或是 “判断条件”, 所以说它们唯一的区别就是动作节点有可能会返回 Running 而条件节点不会。

  • 带记忆的控制流节点
    正如上面我们看到的, 控制流节点在每次 tick 的时候都会依次执行其所有的子节点并获取其返回值. 然而有时对于某些节点, 一旦执行了一次, 就不必再执行第二次了。 记忆节点便是用来解决这一问题的。在控制流节点中, Sequence 节点和 Fallback 节点可以是带记忆的. 当子节点返回 Success 或 Failure 时, 记忆节点总是会把返回值缓存起来; 一旦一个子节点的返回值被缓存了, 就不会执行这个子节点了, 而是直接取缓存中的值; 直到这个节点返回 Success 或 Failure, 便清空缓存。
    记忆节点有一些非常巧妙的应用. 我们通常在节点的右上角加上 * 号表示这个节点是记忆节点. 比如说记忆 Sequence 节点记作 “→∗”。

From 阿高

基于Unity行为树设计与实现的尝试
行为树的设计与实现

1、行为树只是单纯的一棵决策树,还是决策+控制树。为了防止不必要的麻烦,我目前设计成单纯的决策树。
2、什么时候执行行为树的问题,也就是行为树的 Tick 问题,是在条件变化的时候执行一次,还是只要对象激活,就在Update里面一直Tick。前者明显很节省开销,但那样设计的最终结果可能是最后陷入事件发送的泥潭中。那么一直Tick可能是最简单的办法,于是就引下面出新的问题。目前采用了一直Tick的办法。
3、基本上可以明显节点有 Composite Node、 Decorator Node、 Condition Node、 Action Node,但具体细节就很头疼。比如组合节点里的Sequence Node。这个节点是不是在每个Tick周期都从头迭代一次子节点,还是记录正在运行的子节点。每次都迭代子节点,就感觉开销有点大。记录运行节点就会出现条件冗余问题,具体后面再讨论。目前采用保存当前运行节点的办法。
4、条件节点(Condition Node)的位置问题。看到很多设计都是条件节点在最后才进行判断,而实际上,如果把条件放在组合节点处,就可以有效短路判断,不再往下迭代。于是我就采用了这种方法。

From xiyoo0812

参考

1、McYY–lua行为树设计与实现
2、MCxYY/LuaBT
3、使用行为树(Behavior Tree)实现游戏AI
4、行为树及其实现
5、基于Unity行为树设计与实现的尝试
6、【Godot】行为树(一)了解与设计行为树代码
7、xiyoo0812/luabt/–gitee
8、xiyoo0812/luabt/–github
9、xiyoo0812/luaoop

行为树BT设计与实现相关推荐

  1. 数据结构题:根据所给权值设计相应的哈夫曼树,并设计哈夫曼编码

    文章目录 前言 一.涉及到的知识点 二.例题讲解 1.例题1 2.例题2 总结 前言 在数据结构树的这章中,常常有题目,是这样的"给定一组权值-,试设计相应的哈夫曼树",有的还要求 ...

  2. 树状选择框测试用例_分类树测试用例设计工具:CTE XL

    1.1工具安装 CTE XL是一款免费的分类树测试用例设计工具,安装过程很简单,下载好安装程序后,运行安装程序:如图 点击下一步: 点击下一步: 是否创建桌面按钮: 安装: 安装完成后,需要注册才能获 ...

  3. 一个权限树的设计与实现

    如图,实现如下功能: 1,加载自动读出当前权限,自动展开(基本功能) 2,有一个选择/取消全部的功能(之前设计成独立的按钮,最后改成一个根目录的形式,如上图) 3,任何父级与子级的全选关系动态关联,具 ...

  4. java 039 s rule_Java开发架构篇:DDD模型领域层决策规则树服务设计

    沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 在上一章节介绍了领域驱动设计的基本概念以及按照领域驱动设计的思想进行代码分层,但是仅仅只是从一个简单的分层结构上依然没法理解DDD以及如何去开发 ...

  5. 一个多层级节点用户树的设计构思

    背景: 由于近期一个项目要开发一个多层级用户体系,并且还要支持部分链条的可移动性,经过初步构思,已经有了一个初步的思路,今天在这里简单分享一下. 要求: 1. 用户结点数要支持百万级,层级理论上支持无 ...

  6. DDD专题案例二《领域层决策规则树服务设计》

    前言介绍 在上一章节介绍了领域驱动设计的基本概念以及按照领域驱动设计的思想进行代码分层,但是仅仅只是从一个简单的分层结构上依然没法理解DDD以及如何去开发这样的微服务.另外往往按照这样分层后依然感觉和 ...

  7. java数据结构与算法之平衡二叉树(AVL树)的设计与实现中的事实代码

    普通二叉查找树的问题   在开篇,我们提到过,普通二叉树(二叉查找树)在操作的时间复杂度上不一定遵循O(㏒n),也有可能是O(n),这是为什么呢?在上一篇中,我们明明插入都按照一定规则比较的呀,其实那 ...

  8. 三十、赫夫曼树的设计与代码实现

    一.基本介绍 给定 n 个权值作为 n 个叶子结点,构造一棵二叉树,若该树的带权路径长度(wpl)达到最小,称这样的二叉树为 最优二叉树,也称为哈夫曼树(Huffman Tree), 还有的书翻译为霍 ...

  9. 如何使用Trie树,设计实践Google一样的输入提示功能

    来源 | 搜索技术 责编 | 小白 Google和百度都支持输入提示功能,辅助你快速准确的输入想要的内容. 如下:输入"五一",会提示"五一劳动节"等. 那如何 ...

  10. catia设计树_CATIA设计实例教程

    第三章 零件设计 ------ 活塞.连杆.汽缸组件 本章是设计活塞. 连杆与汽缸的三维模型. 进一步熟悉绘制草图. 拉伸成形. 旋转成形.拉伸切除.旋转切除.钻孔.倒(圆)角等命令,同时增添混成.特 ...

最新文章

  1. twitter 监控登陆活动
  2. select设置默认文字,不出现在下拉框中
  3. Android Lollipop (5.0) 原生代码 Settings 首页加载逻辑分析
  4. (转)Asp.net 中 Get和Post 的用法
  5. 框架设计之菜鸟漫漫江湖路系列 开篇
  6. Google+再曝漏洞!5250万用户信息恐泄露
  7. 鸟哥Linux学习笔记(从头学习Linux基础)
  8. STM32 DCMI调试
  9. 苹果手机html5定位,苹果手机常去地点可以记录多长时间?
  10. 实战腾讯云ORC文字识别
  11. 单片机C语言之学习矩阵按键
  12. xms java_java xms xmx xmn xss解释
  13. 电气AutoCAD基础教学(三)——块的绘制
  14. [C++]稀疏矩阵(一维数组描述)
  15. CSP-JS全套认证须知
  16. Red Hat Enterprise Linux7.4版本网盘下载
  17. 小公司的“鸡头”OR大公司的“凤尾”
  18. 调试JavaScript/VB Script脚本程序(Wscript篇)
  19. 语义相似度(理论篇)
  20. 2021 CCF CSP-J2

热门文章

  1. 适合入门的linux教程,Linux入门记录系列教程,适合Linux初学者阅读
  2. Win8彻底关闭UAC
  3. 剑侠录java_江湖风云录-剑侠红颜
  4. jmail邮件服务器,jmail.smtpmail的核心代码:
  5. Docker上安装DM8数据库
  6. 51单片机的家居空气质量监测系统proteus仿真设计
  7. 频谱分析仪的作用是什么?TFN FAT811手持式频谱分析仪
  8. android 混淆字符串,android 代码混淆
  9. Java使用WebService调用远程服务wsdl的方法,搭建一个demo,简单易用的教程
  10. java中移动光标怎么写_java 程序当中无法做到光标跟随