Unity AI 之 行为树 的简单介绍

1. 行为树诞生

在设计游戏AI的时候,我们的目标就是找到一个简单,可扩展的编辑逻辑的方案,从而加速游戏开发的迭代速度。

这里以士兵为例子,假设士兵有空闲、战斗、逃跑三种状态,状态机(FSM)是最先映入脑海的方案,那么士兵的状态机图示可以如下展示。

但是随着开发进行,状态一多,状态机维护起来就没那么轻松了,状态机之间的转换线有如脱缰野马,驾驭不住。比如再添加个躲藏和返回起点状态,那么连线就会越来越复杂。

所以前人大佬们设计出了行为树的抽象来解决这个难题。

2. 什么是行为树?

行为树,英文是Behavior Tree,简称BT,是一棵用于控制 AI 决策行为的、包含了层级节点的树结构。

2.1 原理:遍历

当我们要决策当前这个士兵要做什么样的行为的时候, 我们就会自顶向下的,通过一些条件来搜索这颗树,最终确定需要做的行为(叶节点),并且执行它,这就是行为树的基本原理。

2.2 组成

行为树主要由以下四种节点抽象而成组合节点、装饰节点、条件节点、行为节点。

组合节点(Composites)

主要包含:Sequence顺序条件,Selector选择条件,Parallel平行条件以及他们之间相互组合的条件。

②修饰节点(Decorator)

连接树叶的树枝,就是各种类型的修饰节点,这些节点决定了 AI 如何从树的顶端根据不同的情况,来沿着不同的路径来到最终的叶子这一过程。
如让子节点循环操作(LOOP)或者让子task一直运行直到其返回某个运行状态值(Util),或者将task的返回值取反(NOT)等等

③条件节点(Conditinals)

用于判断某条件是否成立。目前看来,是Behavior Designer为了贯彻职责单一的原则,将判断专门作为一个节点独立处理,比如判断某目标是否在视野内,其实在攻击的Action里面也可以写,但是这样Action就不单一了,不利于视野判断处理的复用。一般条件节点出现在Sequence控制节点中,其后紧跟条件成立后的Action节点。

④行为节点(Action)

行为节点是真正做事的节点,行为节点在树的最末端,都是叶子节点,就是这些 AI 实际上去做事情的命令;

通过使用行为树内的节点之间的关联来驱动角色的行为,比直接用具体的代码告诉一个角色去做什么事情,要来得有意思得多,这也是行为树最让人兴奋的一点。这样我们只要抽象好行为,就不用去理会战斗中具体发生了什么。

3. 树的遍历-Tree Traversal

行为树的一个特点是,它会“一层一层”地去对节点依次进行检查,而这每一层都需要花费一个 tick 的时间,所以它需要花数个 tick 才能完成从顶部走到底的过程,来完成其逻辑,这和一般用代码实现功能是很不同的。

这并不是一个很有效率的方式,尤其是当你的树变得非常深的时候。我认为行为树的实现必须具备可以在一个 tick 内完成整个行为树的判断逻辑。

4. 工作流-Flow

行为树由多种不同类型的节点构成,它们都拥有一个共同的核心功能,即它们会返回三种状态中的一个作为结果。这三种状态分别是:

  • 成功-Success;
  • 失败-Failure;
  • 运行中-Running;

前两个,正如它们的名字,是用来向它们的父节点通知运行的成功或失败的结果。第三种是指还在运行中,结果还未决定,在下一个 tick 的时候再去检查这个节点的运行结果。

这个功能非常重要,它可以让一个节点持续运行一段时间来维持某些行为。

比如一个“walk(行走)”的节点会在计算寻路和让角色保持行走的过程中持续返回“Running”来让角色保持这一状态。
如果寻路因为某些原因失败,或是除了某些状况让行走的行为不得不中止,那么这个节点会返回“Failure”来告诉它的父节点;
如果这个角色走到了指定的目的地,那么节点返回“Success”来表示这个行走的指令已经成功完成。

这些状态可以用来决定行为树的走向,确保 AI 可以按照我们预期的方式来以某些顺序去执行行为树里的行为。

具体介绍见文末的文章,这里只是做记录和介绍

数据上下文-Data Context

当一个行为树被调用时,一个数据上下文也被创建出来。

它的用途是存储被节点所解释和改变的变量。这些变量随后可以被节点结合着数据的上下文加以读写,从而使整个行为树保持为一个统一的整体。

行为树说明:

行为树是各种经典的控制节点+行为节点进行组合,从而实现一些复杂的行为状态的控制,这里使用的行为树插件是 Behavior Designer,它包含四种节点。分别是action节点、组合节点、条件节点和修饰节点。
(1) Composites 组合节点,主要包含:Sequence顺序条件,Selector选择条件,Parallel平行条件以及他们之间相互组合的条件。
(2) Decorator 装饰节点,该节点只包含一个子节点,并未该子节点添加一些特殊的功能,如让子节点循环操作或者让子task一直运行直到其返回某个运行状态值,或者将task的返回值取反等等
(3) Actions 行为节点,行为节点是真正做事的节点,其为叶节点。Behavior Designer插件中自带了不少Action节点,如果不够用,也可以编写自己的Action。对于自己编写的行为状态,一般都是需要添加自己的
(4) Conditinals 条件节点 ,用于判断某条件是否成立。目前看来,是Behavior Designer为了贯彻职责单一的原则,将判断专门作为一个节点独立处理,比如判断某目标是否在视野内,其实在攻击的Action里面也可以写,但是这样Action就不单一了,不利于视野判断处理的复用。一般条件节点出现在Sequence控制节点中,其后紧跟条件成立后的Action节点。
Behavior Designer对于变量的共享做了如下处理:
在同一个Behavior Tree(一般一个GameObject有一个Behavior Tree)的Task间共享的局部变量可以直接在编辑器的Variables添加;另外也支持在不同Behavior Tree之间共享的全局变量;还支持Task与非Task(游戏系统中的其他脚本)之间进行变量传递,通过下面代码进行:
behaviorTree.GetVariableName(“MyVariableName”);
behaviorTree.SetVariableName(“MyVariableName”, value);

自己编写行为子类,自己可以创建行为自定义的节点,这些节点需要继承四种节点的一种。如,我们可以创建一个怪物巡逻的条件节点,也就是怪物默认为巡逻状态,当玩家与怪物的距离小于一定距离的时候,怪物开始追击玩家。

参考链接:

伍一峰:游戏AI - 行为树Part1:简介

伍一峰:游戏AI - 行为树Part2:框架

AI 行为树的工作原理 | indienova 独立游戏

猜你喜欢

转载自blog.csdn.net/u014361280/article/details/122896054