type
status
date
slug
summary
tags
category
icon
password
fullWidth
fullWidth
行为树入门推荐阅读这篇文章:行为树介绍 - Robohub
itch.io传送门(WebGL平台):PacManDemo

行为树定义

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

节点类型

notion image
行为树的节点类型可分为两大类:
  1. 控制节点:也称内部节点,根据子节点的状态和特定规则定义如何遍历行为树。序列(Sequence)选择(Selector)和 平行(Parallel)节点可以有多个子节点,装饰(Decorator)节点只有一个子节点,并使用自定义策略,如重复(Repeat)始终失败(AlwaysFailure)取反(Invert),来修改其行为。
    1. 序列(Sequence)
      1. Sequnce按顺序执行子项,直到一个子项返回 Failure 或所有子项返回 Success
        Sequnce按顺序执行子项,直到一个子项返回 Failure 或所有子项返回 Success
    2. 回退(Fallback)/选择(Selector)
      1. Fallback按顺序执行子节点,直到其中一个子节点返回 Success 或所有子节点返回 Failure
        Fallback按顺序执行子节点,直到其中一个子节点返回 Success 或所有子节点返回 Failure
    3. 平行(Parallel)
      1. Parallel的每个子节点将按顺序单独更新,至少有M个子节点(介于1到N)成功时,Parallel返回Success,当所有子节点都失败时,Parallel返回Failure
        Parallel的每个子节点将按顺序单独更新,至少有M个子节点(介于1到N)成功时,Parallel返回Success,当所有子节点都失败时,Parallel返回Failure
    4. 装饰(Decorator)
      1. Decorator使用自定义策略修改单个子节点,例如,Invert会将Success更改为Failure
        Decorator使用自定义策略修改单个子节点,例如,Invert会将Success更改为Failure
  1. 执行节点:是行为树的叶子节点,有行为(Action)条件(Condition)两类。

执行过程

行为树在时钟周期(Tick)的离散更新步骤中执行,子节点根据树的构造方式地柜执行,会向父节点返回状态,可以是成功(Success)失败(Failure)执行中(Running)
🐈‍⬛
更复杂的行为树节点状态
下面通过一个机器人搜索物体的例子来理解行为树的执行过程。
notion image
如果只存在一个地点A,那么行为树很简单,到A处,找到物体。
notion image
如果在执行行动前先进行条件检查:检测机器人是否已经在A处,如果在就不执行去A处的动作,直接返回成功状态;如果不在,才执行去A处的动作。
notion image
如果机器人要寻找多个感兴趣的对象,可引入更上级的回退(Fallback)节点。
notion image
如果考虑在一个地点同时寻找多个对象,而不是单个对象,又可以进行如下修改。
notion image

黑板

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

行为树和有限状态机

行为树和有限状态机在模块化反应性之间存在权衡。一般来说,行为树更易组合和修改,而有限状态机在设计反应式行为方面更有优势。

行为树的优点

以下案例是机器人移动到一个物体上,关闭夹具抓取物体,再移动回起始位置。
行为树(左)和有限状态机(右)
行为树(左)和有限状态机(右)
如果想修改此行为,在抓取物体之前检查有效性,并进行纠正,如果使用行为树,可以直接插入一个子树,而有限状态机必须重新连接多个转换,由此可见行为树非常适合模块化。
notion image

行为树的缺点

对于反应式行为,比如机器人低电量时需要马上回到电源处。
如果使用行为树来实现会非常麻烦,需要在任意任务的子树中支持充电行为。
notion image
但对于状态机实现起来很简单,只需要从所有其它动作连线到充电动作即可。
notion image
分层有限状态机(FSM)建立高级状态以简化状态间的转换,比如定义一个Nominal的超级状态,表示正常运转和充电两种明确的模式。
notion image

应用场景

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

框架搭建

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

节点基类

行为树节点基类(BTNode)

行为树中所有节点的基类,包含Execute接口,返回节点状态NodeStatus

控制节点基类(ControlNode)

继承自BTNode,可以有子节点。

执行节点基类(ExecutionNode)

继承自BTNode,不能有子节点。

各类控制节点

序列(Sequence)

重写Execute方法,按顺序执行子节点。

并行(Parallel)

初始化时需要传入成功所需的最小成功子节点数successThreshold,重写Execute方法,依次执行子节点,当满足最小成功子节点数时直接返回Success,否则返回RunningFailure

选择(Selector)

按顺序执行子节点,全部失败才返回Failure

装饰(Decorator)

装饰节点只能有一个子节点,需要提供判断是否有子节点的HasChild属性和设置子节点的SetChild接口。
实现几种常用的装饰节点:
  • 反转(Invert):反转节点的执行结果
  • 重复(Repeat):重复执行子节点指定次数
  • 总是成功(AlwaysSuccess):无论子节点结果如何都返回成功
  • 总是失败(AlwaysFailure):无论子节点结果如何都返回失败

各类执行节点

条件(Condition)

通过传入的Func<bool>委托来判断某个条件是否成立,返回SuccessFailure

行为(Action)

通过传入的Func<NodeStatus>委托执行具体行为,直接返回行为执行结果SuccessFailureRunning

执行器(Runner)

从根节点开始运行行为树,初始化时必须传入根节点root

构建器(Builder)

链式调用的方式方便地构建行为树结构,提供各类节点的构造方法,End()Build()接口。
  • End():结束当前控制节点的构建,从节点栈中弹出当前控制节点,必须与对应控制节点配对使用,如果嵌套多层控制节点,也需要对应数量的End()与之配对。
  • Build():完成行为树的构建并返回一个可执行的BTRunner

*黑板(Blackboard)

也可以引入黑板(Blackboard)作为行为树节点之间数据共享的媒介(对于体量较小的游戏,可直接将共享的数据存在GameManager中,或者使用其他的运行时数据管理类)。
以下是黑板的一种可能实现形式,使用键值对存储数据,并提供基于泛型的增删查改功能。

使用案例

使用以上行为树框架制作吃豆人游戏的怪物AI。

怪物模拟

静态类GhostSimulator中定义了各种条件行为
  • 条件:
    • 玩家是否处于能量豆状态
    • 玩家是否在范围内
  • 行为:
    • 巡逻:沿着巡逻点缓慢移动
    • 追击:朝玩家所在的方向快速接近玩家
    • 逃跑:朝玩家所在方向的反方向快速逃离玩家

行为树构建

提供BuildBehaviorTree的方法,将怪物的行为逻辑硬编码到代码中。

AI怪物行为

怪物逃跑
怪物逃跑
怪物由追转逃
怪物由追转逃
 
怪物追击玩家
怪物追击玩家
怪物由逃转追
怪物由逃转追
游戏AI行为决策-分层任务网络(HTN)的简单应用游戏AI行为决策-目标导向行为规划(GOAP)通用框架
Loading...
Latest posts
捣鼓记录:NotionNext+Vercel搭建自定义域名的博客流程
2025-7-3
捣鼓记录:创建Unity工程的Git仓库步骤和注意事项
2025-7-3
自制Python任务调度模块-MySchedule
2025-7-3
自卷笔记:一些乱七八糟的踩坑记录和小知识小技巧
2025-7-3
工作笔记:一些乱七八糟的踩坑记录
2025-6-25
游戏AI行为决策-行为树(BehaviorTree)框架搭建与应用
2025-6-14