文/Jesse Crafts-Finch

  作为人类,我们似乎总倾向于使用自己所熟悉的解决方法。我们总是会按照我们所知道的方式去做某些事,而不是按照做这些事的“最佳”方式。因为总是带着这种想法,所以我们很容易使用一些过时的技术,并使用那些同时代人所不理解,或者并不是那么有效的方式去执行特定功能。所以我希望通过本文以及之后的文章向广大读者们介绍更多能够在编程中带给你们帮助的解决方法。今天我要分享的便是行动列表!

  行动列表是所有游戏开发者都必须清楚的简单但却强大的AI。尽管不能与巨大的AI网络相匹敌,但是它们允许相对复杂的突发行为,并且执行起来也很简单。不管你是刚刚接触AI编程还是着眼于扩展工具包的资深开发者,本文都将向你详细介绍行动列表并提供一些有效的例子帮助你更好地执行解决方法。让我们开始吧。

  从前

  几年前我开始开发《King Randall’s Party》,在游戏中玩家将建造城堡并为了保卫它与尝试着摧毁它的King战斗。我需要创造一个能够瞄准玩家城堡并计划如何去摧毁它以达到自己目的的机智的AI。这对我来说是个巨大的挑战,因为目标太大我很难同时顾及全面。所以就像任何其他优秀的程序员那样,我将其分解成一个比较容易管理的问题集。这时候我所面临的第一个挑战便是:我需要创造一个拥有特定行为集的单位。

173832cdv72oveus8oxh2s.png
  然后你需要对它们进行先后排序。从最低到最高。

173832p7fkmmmkwqjrmk22.png
  现在我们需要迭代该列表去检查每个行动是否能够有效执行。然后我们将检查行动的粘附性能,如果某个项目会阻碍之后的行动,我们便会退出该列表。之后我们便会清楚为什么这种阻碍如此重要了。

  在我们的例子中,我们的第一个行动“攻击玩家”将只在AI靠近玩家的时候执行。让我们假设它并未靠近玩家,所以它将检查是否能在当前位置上创造一个阶梯等,直至它找到一个能够执行的行动条款,如打破门。然后它将执行破门代码。

17383277xf6276tq0nqrax.png
  “阻碍”便是在这里开始发挥作用。如果破门马上发生,它便不会阻碍到任何之后的行动,而之后的列表内容便可以继续执行。但通常情况都不会如此—-行动总是占据一个以上的帧数。所以在这种情况下破门行动项目将调用unit.Attack(door),即将单位的当前状态从等待改成破门,而在门被打破之前将恢复到true的状态。

  一个简单的有限状态机

  这听起来好像是可行的。Frankie提供了一个很好的建议,行动列表似乎也非常适合我的项目。但在此之前我却从未听过它们,所以我对此还是充满疑问—-我听到的大多数关于AI的内容都必须使用基于转变的有限状态机,即类似于在MUnity3D用于创造动画所使用的工具。你将定义一些状态并识别它们何时以及如何彼此转变。Frankie向我我解释了当你在创造一个基于转变的有限状态机时,你需要定义你想要拥有的所有状态,然后你也要定义这些个体状态间的所有转变。这真的会让内容很快变得复杂。如果你拥有一个“伤害”的状态,你便需要识别能够转变成这个状态的状态。如走路变成伤害,跳跃变成伤害,蹲伏变成伤害,攻击变成伤害等等。这是很有用的方法,但却很快便会变得复杂。如果你的AI需求非常简单的话,这可能会是一笔潜在的不必要开支。

  关于基于转变的状态机的另一个难点便是调试。当你设置了一个停止点并着眼于AI当前的状态,这时候如果没有额外的调试代码,你便不可能了解AI是如何进入当前的状态,它之前的状态是什么以及怎样的转变将它带到现在的状态。

  行动列表的缺陷

  当我进一步深入行动列表时,我意识到它们非常适合我的执行内容,但同时我也发现它们存在一些缺陷。最大的缺陷便源自其最大的优点—-简单性。因为这是一个有序列表,所以我不可能拥有任何复杂的优先顺序结构。所以如果我希望“攻击玩家”排在“破门”前面,但却在“移向目标”后面,但同时我又希望“移向目标”出现在“破门”后面,我便很难使用行动列表做到这点,而用有限状态机的话又会非常繁琐。

  总而言之,行动列表对于简单的AI系统真的很有帮助,但是如果你想创建的是较为复杂的AI,那么执行行动列表便会较困难。也就是说你可以通过某些方法去扩展行动列表概念而将它们变得更有帮助。

  扩展这一概念的方法

  我的一些AI单位需要完成多种任务—-它们可能需要同时移动并发动攻击。我可以创造多个行动列表(注:一个处理移动,一个处理攻击),但这么做也存在问题,即如果有些移动类型会妨碍攻击,而有些攻击又需要单位站在原地的话该怎么办?这时候行动线路(Action Lanes)便会发挥作用。

  行动线路是对于“阻碍”概念的扩展。基于行动线路,行动项目便能够识别阻碍执行的特定行动项目类型并允许项目顺畅地执行任务。让我们进一步展示这一执行。

  行动线路只是决定行动的一种附加方法。每个行动项目都属于一条或多条线路,当阻碍性能变成true时,它将添加所属的线路,因为每个行动都拥有一条或多条线路,所以它们只是阻碍属于这些线路的行动。举个例子来说吧,因为单位在创造阶梯时必须保持不动且不能发动攻击,所以攻击玩家属于行动线路,移向目标属于移动线路,创造截图同时属于这两种线路。然后我们将排列这些项目的顺序,如果它们同时执行便会阻碍到后续的行动。
173833hzyehzwryvngezj5.png

  执行案例

  现在我们已经了解了这一理论,所以我们可以尝试一次真正的执行过程。首先让我们设置行动列表和行动项目。对于行动项目我希望能够分离执行,所以让我们创造一个接口。

173833m08hc98shilgi60y.png
  以下便是BreakDoor行动项目的IActionItem执行:

173833u2n5qoaav38480k2.png
  对于行动列表本身我可以使用一个简单的列表。然后我们将使用IActionItems去加载它。

173834g4p4f41z4leplgg4.png
  之后我们将设置一个方法并在每帧中迭代列表。需要记得我们同样也需要处理阻碍。

173834z44mgegybgeueey1.png
  如果你想要使用行动线路的话事情会变得更复杂。这时候我们将把行动线路定义为位字段然后修改IActionItem界面。

17383422bmsgrgg2hbs2ps.png
  然后我们将为这些线路修改迭代程序。如果行动项目的线路遭遇阻碍,它们便会被略过,但检查的时候仍是正常的。如果所有线路都遭到阻碍,我们便会改变这一循环。

173834kylvkg197f94cvcl.png
  结论

  可以看出需要理解的内容有很多。Frankie让我总结从中学到的内容,于是我经过综合思考得出了一些关键要点。

  行动列表比小型基于转变的状态系统更容易创建与维护。

  它们创造了一个可识别的优先系统。

  存在一些扩展方法去处理扩展功能。

  在编程中我们很难找到最佳解决方法。所以我们必须确保我们的工具箱中拥有各种编程工具,如此我们便可以及时选择一个能够有效解决问题的合适工具。最终证明行动列表非常适合《King Randall’s Party》。而它是否也同样适合你们的项目呢?

  相关阅读趣说游戏AI开发:曼哈顿街角的A*算法

游戏邦编译

锐亚教育

锐亚教育,游戏开发论坛|游戏制作人|游戏策划|游戏开发|独立游戏|游戏产业|游戏研发|游戏运营| unity|unity3d|unity3d官网|unity3d 教程|金融帝国3|8k8k8k|mcafee8.5i|游戏蛮牛|蛮牛 unity|蛮牛