type
status
date
slug
summary
tags
category
icon
password
fullWidth
fullWidth
行为树入门推荐阅读这篇文章:行为树介绍 - Robohub
itch.io传送门(WebGL平台):PacManDemo
行为树定义
行为树(Behavior Tree)是一个抽象概念,具有以下特征:
- 行为树是树:从根节点开始,按特定顺序遍历,直到达到最终状态(成功或失败)。
- 叶节点是可执行行为:叶子结点会进行具体的操作,返回状态信息(成功、失败或运行中),叶子节点是将行为树连接到特定应用程序的较低级别代码的位置。
- 非叶节点控制数遍历:非叶节点接收其子节点的结果状态,用特定规则来指示接下来应该执行哪个节点。
行为树诞生于游戏领域,用于定义非玩家角色(NPC)的行为,Unreal Engine和Unity都有用于传播工作行为树的专用工具。后来行为树进入了机器人领域,有兴趣可以阅读这篇论文:[1709.00084] Behavior Trees in Robotics and AI: An Introduction。
行为树的优点是易于编写和修改,缺点是牺牲了设计响应式行为的便利性。
节点类型

行为树的节点类型可分为两大类:
- 控制节点:也称内部节点,根据子节点的状态和特定规则定义如何遍历行为树。序列(Sequence)、选择(Selector)和 平行(Parallel)节点可以有多个子节点,装饰(Decorator)节点只有一个子节点,并使用自定义策略,如重复(Repeat)、始终失败(AlwaysFailure)、取反(Invert),来修改其行为。
- 序列(Sequence)
- 回退(Fallback)/选择(Selector)
- 平行(Parallel)
- 装饰(Decorator)




- 执行节点:是行为树的叶子节点,有行为(Action)和条件(Condition)两类。
执行过程
行为树在时钟周期(Tick)的离散更新步骤中执行,子节点根据树的构造方式地柜执行,会向父节点返回状态,可以是成功(Success)、失败(Failure)或执行中(Running)。
更复杂的行为树节点状态
下面通过一个机器人搜索物体的例子来理解行为树的执行过程。

如果只存在一个地点A,那么行为树很简单,到A处,找到物体。

如果在执行行动前先进行条件检查:检测机器人是否已经在A处,如果在就不执行去A处的动作,直接返回成功状态;如果不在,才执行去A处的动作。

如果机器人要寻找多个感兴趣的对象,可引入更上级的回退(Fallback)节点。

如果考虑在一个地点同时寻找多个对象,而不是单个对象,又可以进行如下修改。

黑板
为了存储可以被多个结点访问的共享信息,引入黑板(blackboard)的概念——一个可以被所有结点读写的公共存储区。

行为树和有限状态机
行为树和有限状态机在模块化和反应性之间存在权衡。一般来说,行为树更易组合和修改,而有限状态机在设计反应式行为方面更有优势。
行为树的优点
以下案例是机器人移动到一个物体上,关闭夹具抓取物体,再移动回起始位置。

如果想修改此行为,在抓取物体之前检查有效性,并进行纠正,如果使用行为树,可以直接插入一个子树,而有限状态机必须重新连接多个转换,由此可见行为树非常适合模块化。

行为树的缺点
对于反应式行为,比如机器人低电量时需要马上回到电源处。
如果使用行为树来实现会非常麻烦,需要在任意任务的子树中支持充电行为。

但对于状态机实现起来很简单,只需要从所有其它动作连线到充电动作即可。

分层有限状态机(FSM)建立高级状态以简化状态间的转换,比如定义一个Nominal的超级状态,表示正常运转和充电两种明确的模式。

应用场景
行为树更好还是有限状态机更好需要看具体要解决的问题。有限状态机更方便管理高优先级的操作行为(比如机器人处于正常状态还是充电状态),行为树更适合定义复杂的行为,比如处理错误恢复。实践中,混合使用两者可能会是更好的选择。

框架搭建
此处依据原初的行为树定义搭建代码框架,而不同项目会对原初的行为树做一些魔改以适配业务需求,导致定义和实现有所差异。以下是某项目客户端的行为树模块的框架:

节点基类
行为树节点基类(BTNode)
行为树中所有节点的基类,包含
Execute
接口,返回节点状态NodeStatus
。控制节点基类(ControlNode)
继承自
BTNode
,可以有子节点。执行节点基类(ExecutionNode)
继承自
BTNode
,不能有子节点。各类控制节点
序列(Sequence)
重写
Execute
方法,按顺序执行子节点。并行(Parallel)
初始化时需要传入成功所需的最小成功子节点数
successThreshold
,重写Execute
方法,依次执行子节点,当满足最小成功子节点数时直接返回Success
,否则返回Running
或Failure
。选择(Selector)
按顺序执行子节点,全部失败才返回
Failure
。装饰(Decorator)
装饰节点只能有一个子节点,需要提供判断是否有子节点的
HasChild
属性和设置子节点的SetChild
接口。实现几种常用的装饰节点:
- 反转(Invert):反转节点的执行结果
- 重复(Repeat):重复执行子节点指定次数
- 总是成功(AlwaysSuccess):无论子节点结果如何都返回成功
- 总是失败(AlwaysFailure):无论子节点结果如何都返回失败
各类执行节点
条件(Condition)
通过传入的
Func<bool>
委托来判断某个条件是否成立,返回Success
或Failure
。行为(Action)
通过传入的
Func<NodeStatus>
委托执行具体行为,直接返回行为执行结果Success
、Failure
或Running
。执行器(Runner)
从根节点开始运行行为树,初始化时必须传入根节点
root
。构建器(Builder)
以链式调用的方式方便地构建行为树结构,提供各类节点的构造方法,
End()
和Build()
接口。End()
:结束当前控制节点的构建,从节点栈中弹出当前控制节点,必须与对应控制节点配对使用,如果嵌套多层控制节点,也需要对应数量的End()
与之配对。
Build()
:完成行为树的构建并返回一个可执行的BTRunner
。
*黑板(Blackboard)
也可以引入黑板(Blackboard)作为行为树节点之间数据共享的媒介(对于体量较小的游戏,可直接将共享的数据存在
GameManager
中,或者使用其他的运行时数据管理类)。以下是黑板的一种可能实现形式,使用键值对存储数据,并提供基于泛型的增删查改功能。
使用案例
使用以上行为树框架制作吃豆人游戏的怪物AI。
怪物模拟
静态类
GhostSimulator
中定义了各种条件或行为:- 条件:
- 玩家是否处于能量豆状态
- 玩家是否在范围内
- 行为:
- 巡逻:沿着巡逻点缓慢移动
- 追击:朝玩家所在的方向快速接近玩家
- 逃跑:朝玩家所在方向的反方向快速逃离玩家
行为树构建
提供
BuildBehaviorTree
的方法,将怪物的行为逻辑硬编码到代码中。AI怪物行为




- Author:Yuki
- URL:http://shirakoko.xyz/article/bttree
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!
Relate Posts