第13章 游戏对象的行为
本章将介绍一些游戏对象行为控制的基本知识和框架。这个框架可以让你更好的规划和控制人物的行为,帮助ai进行行为策划等。
游戏对象的行为
游戏对象的行为,我们主要分成几种,比如玩家自主控制的人物行为,游戏对象固定模式行为,游戏对象AI行为等。
状态机
我们在写游戏的时候,经常面对这个情况:人物在不动的时候播放idle动画,人物跑起来要播放run动画,而人物跳起来的时候,不能走,只能等到人物落地后才可以进行其他动作。也就是说,人物总处于不同的状态下,而在不同的状态下,会有不同的行为。这种对于不同状态下不同行为的处理,我们笼统的叫做状态机。
最简单的状态机原形
由于lua没有case语法,因此我们最简单的状态机实际上就是一系列的if elseif组成的状态判断。比如下面代码。
这段代码有下面几个核心需要解释:
在update中,首先对state进行分类,当状态为idle的时候,它将进行移动测试,开火测试和待机测试。移动测试意义是当有按键按下的时候,移动,播放移动动画,并且进入移动状态。开火实际上跟移动差不多这里没写,然后待机测试是当人物不移动时进入待机模式。在移动状态下,只有移动和待机测试,而没有开火测试,于是,在移动状态下就是不能开火的。
通过上面,我们可以理解到,idle可以通向run,fire 而 run只能通向run,这样的模式就可以画出一个行为树来。谁在什么条件下能通向谁。这个就是状态机的核心了。我们在精炼一下状态机的要素:
- 状态,状态机肯定要有状态标签啦,它标志着当前的状态名称。
- 状态选择器 有了它就可以在不同的状态下做出各自的单独的行为。
- 行为 在进入这个状态时,以及在这个状态下,我们进行的动作,退出这个状态时我们进行的动作。
- 状态条件 在这个状态下,在何种条件下,我们退出当前的状态而进入其他状态。
改良版状态机
上面由if else以及行为与状态条件混在一起的状态机,往往会造成代码冗长,而且不太容易看得出各个状态之间的关系。因此我们对上面的代码进行改良:
这样,用表来代替选择,看起来更整洁了。但是仅仅是把状态判断改成了表的键值读取,我们仍然有大量的代码重复,以及无法识别状态间关系。
进一步改良的状态机系统
|
|
这样是把状态机以数据的形式单独列出来。有很多好处,首先,我们能够通过friend字段看到我们的行为网络;然后,将进入状态的条件和friend结合起来,方便条件的复用。state的配置文件也可以单独放在其他位置,使代码更加整洁。
状态机系统
接下来我们就再一次扩充我们的stateSystem,让它更加完整。具体实现这里就不写了,可以看下我们的状态机系统都有哪些接口:
首先,我们来说一下状态机中的状态state。它包含:
friend成员,是这个状态下可以通过条件检测同向其他状态的名称。其排布必须按照执行的优先级来进行。
condition方法,是个函数,如果函数返回true则切换到该条件的state中。比如按下移动键。
enter方法,当人物状态从其他进入到本状态时,一次性激活这个方法。
update方法,当人物在此状态所特有的行为。
exit方法,当人物从本状态跳出时执行的收尾行为,一次性。
然后我们再看一下系统。
states表,来存放所有的状态,以状态名为键值。
reg方法,注册单独的状态于状态系统中。
switch方法,从一个状态跳到另一个状态,并激活exit和enter方法。
set方法,直接强制设置某个状态,不激活exit,只激活enter方法。
update,遍历本状态下所有的friend状态的条件,如果条件成立则跳转状态,否则执行本状态下的update。
使用这种状态机的优点在于,状态机数据比较清晰,某个状态的进入条件,更新方法,相邻状态等都在一个表中体现,很容易理清逻辑。在游戏单位那一端,仅需要写好相应的行为,然后更新状态机即可。所以游戏单位的代码十分简洁。
游戏对象的自动行为
游戏对象的自动行为,实际上就是除了我们玩家手动控制的游戏对象外,其他游戏对象的行为。
我们首先来看一些比较刻板的行为,他们一般不受或者比较少的收到环境的影响,比较稳定的执行自己的行为模式。
最简单的比如,超级马里奥中的敌人,他们只会在碰到障碍时转向,或者仅仅简简单单的取玩家的位置,指向或移动向玩家的方向。大多数横版卷轴,弹幕游戏都是这种行为模式。他们在设计起来比较简单,玩家可以多少预判出敌人的行为模式从而采取对应的措施。
有时候,即时是这种固定模式,也有一些不同的变化。一个游戏对象的行为有若干种,他们之间的调度,往往通过顺序或随机的方式进行,并且行为之间会有一个惯性。比如开火–闲置–躲闪–闲置,他们的切换需要一个定时器的参与。比较典型的比如:雷电中的boss飞机,他们一般并不会根据玩家的行为而改变行为模式,(改变的仅仅是移动方向,开炮的方向等),而是按照自己的套路来。再比如一些NPC的无意识走动(经常被吐槽的),走走停停,走的时候一般是随机方向,然后按这个方向走一段时间/距离后,可以选择等待换方向等。
游戏对象的条件行为/行为树
当然,我们并不满足于让所有的npc都那么傻。不然,游戏就没有什么挑战了。于是我们希望由某些条件来控制游戏对象的行为。这时,就需要借助条件队列或者行为树的帮助了。
最简单的条件队列如下:
很简单是吧,我们拿一个例子来套进去,假设这个是敌人的行为队列。
cond1 如果自身生命小于10% action1 离开玩家
cond2 如果玩家在攻击范围 action2 攻击
cond3 如果玩家在视野范围 action3 移动向玩家
action0 无意识走动
我们可以在思维中模拟,一个npc,正在无意识走动,玩家过来了,进入到视野范围,于是他走向玩家,当进入到攻击范围时,他会自动攻击,当玩家远离他时,由于脱离攻击范围,条件又变成在视野内,于是他有接近玩家。。。当濒死时逃跑直到生命恢复。
这是最简单的队列形式。如果在某一个条件下还有子条件呢? 它就变成了行为树。
比如在濒死条件下,如果脱离玩家攻击范围,则喝药,否则跑离玩家。
当然,这里是正向条件,还有反向条件,没有某种条件,游戏对象将保持某种行为。这里的所有行为都是串发的,还有并发条件的,他们的返回结果也可以是以加权形式的,比如对方兵力,对方资源,己方兵力,己方资源等等条件。这里就不一一介绍了。
a星寻路
寻路算法在游戏是十分重要的,对于自动移动来讲,只要有障碍的移动,就需要用到寻路算法。我们最常用的是a星寻路了,因为它实现很简单,用起来也很简单。而且可以和tile碰撞配合使用。它是一种,将游戏中的单位和障碍抽象成矩阵的算法,具体算法原理及实现请自行百度,我没记错的话,甚至还有一个love2d的a星寻路实践。a星寻路还有很多变体,主要就是对深度优先和广度优先进行调整。
love2d最常用的寻路库是jumper,它内部包含了好多种寻路算法,按照自己的需求使用即可。
一些有意思的算法
这些算法也许并不能帮你写ai,但是他们可以给你对对象随机行为上给出一些思路,这些算法请自行百度。
元胞自动机
元胞自动机的特点是,在十分简单的规则下,产生复杂的行为表现。
蚁群效应
蚁群算法的特点是,我们对行为趋向的描述不一定非得在某个个体上,而是留在公共的环境,让所有个体来对环境进行感知和改变,从而达到优化的目的。
鱼群效应
鱼群算法也是一种针对群体的算法,他可以体现出简单规则下,对某些随机行为的放大,从而形成比较复杂的一致性。
遗传算法
遗传算法的特点是,对某些倾向进行保留和放大,从而得到趋近的结果。