Unity游戏开发——行为树(Part1)

一.什么是行为树?

前言(本人也是刚学行为树不久,可能会有哪些地方写的不严谨或者不对,欢迎指正.勿喷)

概述

1.行为树(BehaviorTree) 在游戏中用于控制角色AI的一种实现方式,在游戏AI中对象的动作和行为大部分都是编辑好的(例如攻击移动等)(除非是FIFA那种可能不是提前编辑好的)但是在什么时间什么地点什么条件调用都是不确定的,因此我们要用行为树或者状态机来实现这些对象的决策任务(注意行为树并不是决策树,决策树是为了制定决策,行为树是为了控制行为)。说白话其实就是把各种条件和执行的规则抽象成树的节点,把角色整个对象的AI行为,抽象成一棵树。通过树的节点来决策下一步该做什么不该做什么。

与状态机的区别以及优缺点

与状态机区别

1.其实个人认为状态机扩展性不差,就是看这个状态机具体怎么写。每扩展一个状态就声明一个类去继承状态基类就可以了。但是状态机是不能处理和模仿过于复杂的智能行为的。比如模拟一场大型的战役中的人物时,对战场情况太多,各种的条件组合突发情况太多,使用人类逻辑编写每个状态,状态就会呈指数级攀升,而且维护其转换条件和状态等那肯定是极为痛苦的。所以这里就用到行为树了,行为树简化了逻辑拼凑的方式,让思维能力有限的人类能更容易的写出控制机器人的智能行为。可以说是“化繁为简”。
2.状态机的实现大都是写在状态类里面的(看每个人的具体写法),然后利用状态管理器管理每个状态以及书写着公共方法,但是行为树大都是每个节点是一个基类,具体的逻辑行为是抛给外部实现的(也是看每个人的具体写法),就是写好一个树的架子,然后往里面填逻辑就可以了。

优点

1.最主要的就是状态机虽然易拓展状态但是不易拓展条件直接的切换,但是行为树又将条件与“状态”又解耦了一次。
2.灵活性更强,不像状态机多一个状态得声明一个类。
3.更加的模块化

缺点

1.自己得梳理出树形图再编写代码,如果没有树形图做辅助,编码将有些许困难。而且没有像BehaviorDesinger那种可视化图辅助,Debug比状态机费劲。
2.如果AI逻辑不是很复杂,那么就没有必要用行为树,用行为树反而会有点难。
3.代码构建树时候有些许麻烦,实例化很多的节点,以及添加子节点。(这里个人认为可以利用工厂模式之类的来优化一下)
肯定还有其他的优缺点,以后慢慢探索吧!

行为树的介绍

行为树结构概述以及驱动方式

1.行为树的本质是树状节点,每个节点都可以选择某种类型的功能节点,也可以选择某个叶子结点,即使没有子节点功能,功能节点以各种逻辑顺序来选择继续访问下面的子节点还是直接停止,并将结果返回给父节点,子节点的结果将为父节点提供参考,以便继续运行相关的逻辑。所以,行为树本身就是一种树形的父子节点之间的逻辑结构,我们可以理解为节点逻辑通过扩展节点的功能来实现复杂的行为逻辑。
2.综上所述,行为树的驱动方式为:从根部开始执行(update执行),逐级进入子节点,每个节点都会有评估函数,确定节点状态。每个节点在评估后会返回一个状态,根据节点状态,决定下一步行为。每帧如此向下执行。
3.每个节点的状态类型:Success,Failed,还有Running,让上层节点根据返回的类型决定下一步该怎么做。

举个例子

下方是一个敌人巡逻,玩家在一定范围内就会追击玩家,玩家脱离敌人可视范围就会回去巡逻的简答AI例子的树形图。
在这里插入图片描述
这里的执行顺序就是从根节点开始,根节点为选择节点(后续会详细介绍每个节点的意义,这里留个印象就好,选择节点会依次遍历节点,如果遇到一个符合条件的节点就会返回True,如果返回Fasle会执行下一个节点),如果可视范围内没有敌人,从左到右以此遍历子节点,会执行Seq巡逻节点,之后从左向右节点遍历执行下面两个子节点(因为是顺序节点),如果没找到敌人(执行反向修饰节点,判断没有敌人),就会执行执行节点(执行节点就是执行具体的行为逻辑,这里也就是巡逻)。执行节点会返会成功状态,或者Running状态。 之后又会执行一个Update(在我的写法中如果是Running状态,子节点的索引是不刷新的,下一次Update其实还是执行的Seq巡逻,如果玩家靠近巡逻敌人,具体的逻辑会返回Failed,然后会从新刷新索引,从新从第一个索引的子节点也就是Attack执行)。之后按照逻辑会执行追击节点了。(具体的顺序后续会再说一次)

行为树节点介绍

下面就来介绍一下行为树的组成,各个节点的介绍,它包含了4大类型节点。(其实这个也不是定死的,可以根据自己的需求重写自定义节点)。

复合节点

1.选择节点:

Selector Node :选择节点的规则是当执行本类型节点时,他将从头到尾迭代执行自己得子节点,如果遇到一个子节点执行后返回True,则停止迭代,本节点会向自己得上层父节点也会返回True。否则所有子节点都将返回Fasle,那么本节点也会向自己的父节点返回Fasle。

2.顺序节点:

Sequence Node:顺序节点的规则是当执行本类型节点时,他将从头到尾迭代执行自己的子节点,,如果遇到一个子节点执行后返回Fasle,就会立即停止迭代,同时本节点会向自己的父节点也会返回Fasle,相反所有子节点都将返回True ,那么本节点也会向自己的父节点返回Ture。

3.并行节点:

Parallel Node :并发节点的规则是当执行本类型节点时,它将并发自己的所有的子节点。并发节点又分为三种策略,这与他们向父节点返回的值和并行节点采取的具体策略有关。

并行选择节点:

Parallel Selector Node:其规则为执行完所有子节点后,如果有一个子节点返回Fasle,则向自己的父节点返回Fasle,只当所有的子节点返回True 时,才向父节点返回True。

并行顺序节点:

Parallel Sequence Node: 其规则为执行完所有子节点后,如果有一个子节点返回True,则向自己的父节点返回True,只当所有的子节点返回Fasle 时,才向父节点返回Fasle 。

并行混合节点:

Parallel Hybird Node , 其规则为执行完所有子节点后,其规则为执行完所有子节点后,按指定数量的节点返回True或者Fasle后再决定返回结果。(个人觉得就是自定义)

  1. 并行节点提供了并发性,由于可以在线程或者协程级别提供并发操作,所以其在性能上能成分利用CPU提高性能。(个人倾向于UniTask毕竟线程调不到主线程的东西,协程会产生垃圾)。不过个人暂时就是单纯的同步遍历子节点执行(一帧之内执行了后期会优化),毕竟也得控制执行顺序。所以通常并发节点下会挂载多个Action子节点,条件子节点或者子树,已提供实时性。并行节点在提高性能的同时,也确实增加了维护的难度以及处理并发逻辑过多会产生卡顿的问题。
  2. 除此之外,为了增加AI的复杂性和随机性,选择节点和顺序节点可以进一步的非线性迭代的加权随机变种。比如,随机权重选择节点,每次执行不同的起点为每次执行不同的First True 子节点提供了可能。而随机权重顺序节点每次执行的顺序不同。他可以提供不同的随机迭代顺序,能避免AI总是出现可预期的结果,让结果更有随机性。

修饰节点

修饰节点(Decorator Node) 的功能为,它的子节点执行后,将对返回的结果值进行额外的修饰处理,然后返回给他的父节点。以下是几个例子,这个节点可以根据需求通过自定义的方式创造,可拓展性很强。

反向修饰节点:

其功能为将结果反置返回给上级处理,即当子节点为True时,返回给自己的父节点为Fasle,反之同理。

直到失败修饰节点:

其功能为子节点在这指定的次数到达前一直都是向上返回失败信息,到达指定次数后返回成功信息。

计数修饰节点:

只运行子节点N次,之后将不再运行。

时间修饰节点:

在指定时间内一直返回成功信息,超过规定时间范围,无论节点返回什么结果,都返回失败信息。

条件节点

条件节点(ConditionNode)相对简单,若条件满足返回True,否则返回Fasle。比如遇到血条是否为0的判断,是否发现敌人的判断。该节点本人的写法是和行为节点一样,通常是叶子结点的存在。当然也可以有子节点只不过写法不同。

行为节点

行为节点(Action Node) 通常作为最后的叶子结点,它在完成具体的一次行为之后根据计算或配置返回返回值。行为节点可以是执行一次得到结果(返回失败或成功),也可以分步执行很多次。例如,向前走这个行为一直可以执行返回Running,直到走出这个范围为止。

  1. 在行为数中执行任何的节点之后,都必须向其上层父节点报告执行结果,Success,Failed,或者Running(比如例子中玩家,巡逻或者追击走到某个目标点,还未走到目标点所以是执行中)。这种简单的成功,失败,运行中的汇报原则将用于控制整个树的决策方向。
  2. 整个行为树中,只有条件节点和行为节点才能成为叶子节点,也至于叶子节点才是需要特别定制的,而复合节点和修饰节点(其实修饰节点也可以,看个人怎么写)通常控制行为树的决策方向。

总结

结合节点的介绍,再看上文给出的例子。可以梳理出这个执行流程:
在这里插入图片描述

因为玩家在敌人的巡逻范围之外,所以前三个节点依次按顺序迭代,每个节点都是向下执行返回根节点为Failed,只有巡逻节点经过反向修饰节点为True,顺序执行了巡逻的Action节点,返回至父节点为Running。如果玩家进入追击范围,巡逻节点返回Failed(由于返回Running子节点的索引是不刷新的,下一次Update其实还是执行的Seq巡逻)。则进入追击节点执行追击Action节点,返回Running。当玩家脱离追击范围,敌人执行Back顺序节点执行Back Action节点继续巡逻。(其实Back可以删除,直接然后直接巡逻节点也可以,这两个是一样的)。
可能光看文字描述有些抽象,可以建议先去了解下BehaviorDesinger,利用图形来初步了解执行顺序,再来自己手写搭建行为树的框架,效果会更好。
Part2我会将Part1的例子的代码贴出来,以及会介绍逻辑的执行顺序。
下图是条件节点不作为叶子节点的树形图例子,放出来以便大家拓宽思路(与Part1举的例子无关)
在这里插入图片描述

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值