行为树一般应用于游戏内物体的 AI 决策,以树的形式控制物体接下来的动作。比如: 血量太少 -> 吃药、攻击距离不够 -> 移动等等。
前言
最近在摸索小游戏开发,有一些联机型的功能需要机器人参与,所以需要实现一下游戏 AI。综合一下实现方案基本是以下两种:
- 有限状态机(Finite State Machine)
FSM 也是最开始采用的方案,实现起来简单粗暴。缺点是整个逻辑非常臃肿很难模块化,后面再对 AI 进行更新和维护比较恶心,很容易因为漏改、错改导致不可用。
- 行为树(Behavior Tree)
行为树是由一系列节点构成的,每个节点对应一个动作,自上而下执行且每个节点都会返回执行状态。

行为树
行为树一般由:根节点(Root)、序列节点(Sequence)、选择节点(Selector)、并行节点(Parallel)等等节点组成,可能还会有条件节点(Condition)。这些节点也可以被抽象的归为 4 类:
控制节点
- 序列节点(Sequence)
- 选择节点(Selector)
- 并行节点(Parallel)
条件节点
- 条件节点(Condition)
行为节点
- 自定义节点。比如:攻击、防御、吃药等,是真正执行游戏逻辑的节点。
装饰节点
- 逆变节点(Inverter)
- 重复执行节点(Repeater)
- 装饰节点也算是自定义节点,主要作用是辅助子节点执行。
需要注意的是,无论是什么节点都必须实现Exec()
执行方法。
定义节点接口:
1 | type Status int8 |
序列节点
有序的执行子节点,任意子节点失败终止执行返回失败,全部执行完毕返回成功。
1 | // 序列 |
选择节点
同样有序的执行子节点、和序列节点不同的是任意子节点执行成功返回成功。
1 | type Selector struct { |
并行节点
同时执行所有子节点
1 | type Parallel struct { |
条件节点 / 行为节点
顾名思义,执行子节点前需要检查是不是符合条件。
1 | type Condition struct { |
在这引入一个概念: 黑板(Blackboard)
,一般一个行为树有且只有一个,用于各节点间共享数据。就像一群人在黑板前讨论问题,各自的思路、方案都被书写在黑板上供所有人阅读。调用实现:
1 | // 绝招 |
装饰节点
比如让 AI 连续执行 3 次绝招。
1 | type Repeater struct { |
最后
通过以上几个简单实现,大概知道行为树其实就是把想要让 AI 执行的动作抽象成一个一个行为节点,使用组合节点加以控制组成一个复杂的 AI 树。
优点显而易见,可读性好、方便扩展、可以脚本化编写。部分游戏引擎也提供可视化编写,比如:Unreal、Unity 的 Behavior Designer 插件等
缺点是每次都从根节点运行,行为树比较复杂的情况运行效率不会高。当然针对这个问题可以进行优化,就是对节点做 Running 记录,下次从 Running 节点继续执行。
附上示例:bt