一、什么是游戏状态机
游戏中状态机就是一个对象或者角色,有多种状态属性,多种状态下切换变化,比如走路,1段跳跃,2段跳跃,战斗,死亡这些状态。根据当前是什么状态下可以切换到什么状态,比如走路的时候我可以切换到1段跳跃。在1段跳跃下才能做2段跳。
简单的有限状态机可以用枚举+switch的方式实现,用Switch语句来获取状态,并且通过特定的方法来设置状态。但我们可能要在项目的很多地方的调用这段Switch方法,或者设置当前人物的状态。
状态机是一种网络结构。它比较符合我们的常规思维,也比较直观。用于做一些简单的AI没什么大问题。(比如走路的时候搜索敌人,发现敌人,对敌人进行攻击,敌人死亡)用状态机其实更加便捷。但是如果AI比较复杂的话,比如它的状态比较多的话,就显的比较复杂。我们用一张网络(如下面的图)表示,如果状态就几个的话,逻辑上就可控,一旦多起来的话,维护的状态量就会成倍的增加,导致代码上写的很繁琐吃力。
二、什么是行为树?
行为树就是一个包含逻辑节点和行为节点的树结构体,我们把每一种情况都抽象成一个类型的节点,然后我们按照这个规范去写节点,然后把这些节点连接成一棵树。我们每次去找一个行为是时候,就会从树的跟节点出发。遍历各个节点,寻找出一个和当前数据相符合的行为。比如我们有一个士兵有无怪物的时候巡逻,视野内有小怪的时候攻击,视野内看到大BOSS逃跑三个行为。我们就在程序中找到这个英雄,让从左到右遍历自己的子节点,如果子节点的准入条件符合信息的话,就执行该子节点。
如果多个节点都符合条件如何选择?比如上面,视野内同时看到大BOSS和小怪,该怎么执行?
一般有以下选择行为节点
(1)优先级型,从左往右逐个测试,执行条件逐渐放宽
(2)非优先级型,每次从上一个执行的节点开始测试准入条件,用互斥避免节点被屏蔽
(3)权重型,根据权重做概率选择
除了行为节点,其余一般称之为控制节点(Control Node),常用的控制节点有以下三种
(1)选择(Selector):选择其子节点的某一个执行
(2)序列(Sequence):将其所有子节点依次执行,也就是说当前一个返回“完成”状态后,再运行先一个子节点
(3)并行(Parallel):将其所有子节点都运行一遍
控制节点其实就是“控制”其子节点(子节点可以是叶节点,也可以是控制节点,所谓“执行控制节点”,就是执行其定义的控制逻辑)如何被执行,所以,我们可以扩展出很多其他的控制节点,比如循环(Loop)等,与行为节点不同的是,控制节点是与游戏无关的,因为他只负责行为树逻辑的控制,而不牵涉到任何的游戏代码。如果是作为一个行为树的库的话,其中就一定会包含定义好的控制节点库。
如果我们继续考察选择节点,会产生一个问题,如何从子节点中选择呢?选择的依据是什么呢?这里就要引入另一个概念,一般称之为前提(Precondition),每一个节点,不管是行为节点还是控制节点,都会包含一个前提的部分,如下图
前提就提供了“选择”的依据,它包含了进入,或者说选择这个节点的条件,当我们用到选择节点的时候,它就是去依次测试每一个子节点的前提,如果满足,则选择此节点。由于我们最终返回的是某个行为节点(叶节点),所以,当前行为的“总”前提就可以看成是:
当前行为节点的前提 And 父节点的前提 And 父节点的父节点的前提 And….And 根节点的前提(一般是不设,直接返回True)
行为树就是通过行为节点,控制节点,以及每个节点上的前提,把整个AI的决策逻辑描述了出来。
总结:
1、行为树拥有3种节点:
(1)根节点 Root
(2)(逻辑节点(可拓展):
Priority Selector
Sequence
Parallel
(3)行为节点
2、行为树在复杂的情况比有限状态机更清晰,更可拓展
3、行为树有利于逻辑的重用