《Unity3D高级编程之进阶主程》第八章 AI(3) 非典型性AI
前面讲了状态机和行为树在AI中的运用,在实际开发过程中,特别是一些在游戏行业做过5-10年左右的很多人似乎似乎已经习惯了用这些固定的工具去写AI逻辑,一想到游戏AI就固定思维的认为就用状态机去写或者说就用行为树去写。
思维跳不出这些‘工具’,我们就成不了大师。大师是什么,是能将‘剑’运用到最高境界的人,手里‘无剑’而心中有剑是他们的至高境界。万事万物都是同一个道理,我们的‘技术’就像剑术,在练习过程中都会经过几个阶段,具体有哪些阶段其实每个人都不一样,但大多数人都可以总结为这三大阶段,即‘识剑’是第一个阶段,认识剑是怎么样的,也就是认识‘技术’是个怎样的东西用在哪里。而‘用剑’是另一个阶段,如何用剑或者说如何运用这项‘技术’是大多数人停留的阶段。它是一个怎么用剑怎么用好剑的过程,这是一个非常漫长枯燥的过程,以至于很多人做着做着就放弃了,随着工作和学习时间的增长,已经没有了提升没有了成就感,不再有兴趣专研和练习,慢慢地挫折感和自卑彻底压垮了人的意志。如果你有幸突破’用剑‘的阶段就会进入’无剑‘的阶段,手里没有剑而心中有剑在剑术的造诣上达到了至高的境界,无剑胜有剑在这个阶段体现的淋漓尽致,你可能不再在一线编程,或者你编写的程序相对比较少但是是最核心的了,即使这样你的心里也清清楚楚的知道一线的程序员们的编程习惯和编程方向,你用自己所能去帮助他们编写出更好更健壮的程序,帮助的手法也不再是手把手亲自教导,因为这样效率太差,普及率太低,因此此时你可能不再亲自出马,而是利用制度、规则、流程来提高所有人的技术高度,从而支撑部门做出更好的产品。
我们接触的面要尽量的广,接受和包容的东西要尽量的多,才能体验到更多的东西。游戏开发也是,世界上不是只有RPG角色扮演类一种游戏类型,还有SLG策略类游戏,还有休闲娱乐类游戏,还有体育竞技类游戏,还有教育类游戏等等等。只有状态机只有行为树是无法为项目开发出最好最适合的AI的,我们应该尽量包容非典型性AI。
===
下面我们来了解下游戏项目中非典型性的AI。
1,庄家式AI
顾名思义,是庄家统治游戏的一种玩法,玩法中AI就是全局的统治者,它决定了整个游戏的高潮和低谷,AI如何操作成为了至关重要的关键点,玩家是否喜欢这款游戏很大程度上取决于AI的策略。
这种类型游戏大部分以彩票或赌局的形式存在,比如老虎机,百家乐,21点,以及部分以彩票为形式的游戏。
AI策略的目的是要让玩家玩的好又不能赢太多而且最终是要输给AI的。玩家喜欢继续玩但从总体上来看却又是输的,让庄家就是游戏运行商有的赚,但又不能太狠,让游戏细水长流。
这种AI常以输赢的概率为基本控制手段,如果现阶段以玩家欢乐为主,那么胜的概率变得大一点,玩家会认为幸运女神眷顾他了,一段时间后为了让玩家遇到点困难,让玩家有挑战性,这时AI慢慢降低了赢的概率,玩家缓慢觉得赢的次数开始减少,有些玩家会以为幸运女神走了,需要靠自己的‘本事’玩下去了,他们不断得发现和寻找输赢的规律,以及出牌和翻牌的概率,直到输得所剩无几,然后AI开始了缓慢提高胜率的操作,让玩家又看到了希望,找到了自信,认为自己终于不费苦心找到了规律和破解的秘密,于是又开始了新一轮有输有赢但输多赢少的旅程。如此这般不断得循环往复,AI以一种庄家的形式控制着整个游戏过程。
从技术上来看,这种控制概率的过程,就是加法和减法的过程。其核心是给出一个概率后,如何让它真的起到概率的作用,而且这种概率能让人很舒适的接受,比如当有30%概率赢时能否做到真正的随机值在30%的概率上徘徊,比如10局中有2-4局能中玩家可以得到想要的那个点位或者想要的牌什么的,而不是10局中0-5局能赢,这样输赢实际结果波动到了不好的体验。
2,可演算式AI
可演算式的AI在策略类游戏中非常常见,在页游里的大部分的自动对战,以及现在卡牌手游中的大部分自动战斗都是可演算式的AI,是可以根据两边的阵容数据和一个随机种子来演算出整场战斗的每个细节的。例如,两个军队的各5个英雄,互相间攻击并释放技能,服务器需要在一瞬间把所有需要在客户端演示的内容都计算出来,并以数据的形式发送给客户端,客户端根据演算的数据进行展示人物的动作,位移,技能释放等,客户端并不需要计算任何内容,所有内容都在服务器里已经计算好了,以数据形式代表了整个过程。
可演算式AI的特点是逻辑一定是确定性的,不能是模糊的,或者会随机改变的,或者随时间变化而变化的结果,同样的数据第一次计算和多次计算的结果必须是相同的,才能最终体现出可演算的这个特征。其次,可演算式AI大都是根据时间轴来演化游戏的进程,在那里‘时间轴’的概念在可演算式AI中是比较常见的。
什么是时间轴演算路径?我们用卡牌对战算法来举几个例子。
首先最简单的时间轴演算路径,例如,先由敏捷度最高的英雄进攻,等待英雄进攻完毕后再由其次高敏捷度的英雄进攻,依次进行下去直到所有的英雄进攻完毕再重新一轮进攻。这种相对比较简单,可以把进攻看成一轮一轮的回合,每个回合都相当于是一次for循环,每次for循环前先对敏捷度进行排序,再在for循环中依次计算进攻了谁受到了多少伤害,以及是否死亡。最后把进攻的数据和伤害的数据用队列的形式存储在数据中发送给客户端,客户端受到数据后再进行演示。也可以不发送具体数据,而是只是发送随机数的种子,通过种子来产生与服务器一样的随机数,最后再通过使用同一套算法来达到校验的目的,即如果前端的计算结果和后端的计算结果一致则认为客户端的演算正确,这样就减少了很多数据传输的压力。
稍微复杂点的时间轴逻辑,例如游戏里的卡牌角色不再由敏捷度来决定进攻的先后次序了,而是由每个英雄的进攻间隔冷却时间来决定进攻次序,对战开始时开始计算每个英雄的进攻冷却时间是否结束,谁的冷却时间先结束谁就最先得到进攻权,进攻完毕后再等待下一个英雄的冷却时间,以此类推。从技术上讲,冷却时间不需要等待,把所有英雄加入一个队列,排序一下就可以得到谁的剩余冷却时间最短,就立马可以开始计算进攻细节,完毕后根据冷却时间插入到队列中去,任然还是一个有序的剩余冷却时间队列,一直这样计算下去直到演算结束一方胜利或失败。这种演算方式就有了更多了时间轴的概念。
我们再来看看更复杂的时间轴,前面所说的进攻逻辑都是在其他英雄都停止的状态下进行的,现在进攻时不再需要其他英雄等待了,一旦冷却时间结束就可以立刻进攻。也就是说,每个英雄都可以在其他英雄还在进攻时进行进攻操作,只要他的冷却时间到位,并且他的进攻是需要花费固定时间的,等到进攻完毕后再判断对方和自己是否死亡。这种方式,不只是多了一个进攻消耗时间这么简单了,还有死亡时间的判断。在还没有死亡判定前,此英雄虽然是即将死了但任然可以被进攻。从技术上来看,计算的量从单一的剩余时间量排序,增加了进攻消耗时间排序插入,以及死亡判定排序,我们既要在冷却时间结束时计算进攻,还要在选择进攻对象时计算该英雄是否已经判定死亡,最后在进攻完毕后计算当前的时间并插入到排序队列中。这样我们就需要将每个关键时间点都计算出来,并依次演算,由于前面的关键时间点有可能影响后面的角色AI的动作,导致我们不能演算太多的时间段,只能一步步来做AI演算。
再复杂点,英雄在进攻一半时可以被别的英雄打死,也可以被其他英雄的技能打断,也包括了随时被回复血量蓝量,增加状态等等,让战斗更加逼真、更具有实时性。加强了实时性的要求,更加考验可演算式AI的复杂度,由于太多的需要计算的时间内容,所以我们必须有一个能有效管理时间轴的方法。从技术上来看,我们必须定义‘时间节点’这个概念了。
‘时间节点’就是一个事件在整个过程的时间轴上的发生时的位置。
有了时间节点这个概念,我们可以用时间节点为计算标准,把所有人物的下一个事件的时间节点计算出来,比如,移动到达敌方位置,释放技能,回到原地,冷却时间结束,每个人只计算最近的一个时间节点,并把计算出来的结果放进队列中,然后就可以找出时间节点里离我们最近的一次事件的发生节点,也就是时间差最少的节点,执行它。
由于每个时间点都有可能引起其他时间点的变化,例如这个做东把对方打死了,对方的时间节点就消失了,又例如打断了别人技能释放,对方就回到了冷却队列,时间节点就需要重新计算了,又例如加速了友军的攻击速度,因此所有友军正在攻击或打算攻击的时间节点都要重新计算了等等等等,这些关键点的时间节点被前面的时间节点打断则需要重新计算。因此每次在执行完一个时间节点后,都要对有可能产生变化的人物的时间节点进行重新计算,并重新加入队列。这样经过重新计算后,我们可以再次找出离我们最近的一个时间节点了。如此往复的重复这个计算过程,直到没有任何时间节点可计算和执行,最终决出这张战斗的胜利或失败。
时间轴贯穿了整个AI过程,人物之间的打斗,移动,释放法术,冷却时间等待,AI每次只计算一个时间节点,因为只有最近的那个时间节点是一定不会被其他节点影响的,这样既照顾到了可演算的根本原则,也照顾到了游戏的实时性,让战斗更加精彩。最后我们可以把每个计算的结果都记录下来,这样可以随时在客户端进行演示,整个过程的事件发生的时间都将准确无误呈现出来。如果觉得这样传输数据太浪费网络资源,则可以使用我前面提到的,传输随机种子,然后通过两边一致的算法进行各自演算。
3,博弈式AI
游戏项目中大部分AI的目的都是以娱乐玩家为主,并没有要真正打败玩家,最多也是要与玩家达到一种平衡。而博弈式AI则不同,它的目标就是为了打败玩家,它是为了赢得比赛而生的。
博弈AI最大的特点是搜索。通过搜索将所有下一步可能发生的,以及下几步可能发生的事情都记录在内存中,以此来确定电脑该怎么进行下一步动作,进而获得最大的效益。
有人不理解为什么是搜索,在AI选择下一步的动作前,下一步要发生的情况有很多,我们需要选择最佳的那个。于是把所有下一步的情况都列出来,再把下一步的后几步的情况也列在下一步的下面,计算的步数太多CPU计算量太大的因此裁剪了后面的步数,假设我们只计算和预测五步的结果,我们需要找到第五步的最好结果,再反推到第一步来确定第一步到第五步是怎么走的。
既然我们要从第一步走到第五步,假设第一步有10多种情况,第一步到第五步都有10多种情况,每一步之间都有一个结果就相当于一点到另一个点的路径一样,从第一步到第五步就可以视为路径的一种,那么从第一个点到第五个点的最短路径,就是第一步到第五步的最佳结果。把视角一转换后发现事情变得的简单了,用最短路径算法就可以解决搜索问题。
不过事情远没有这么简单,这里只是举了个简单的例子,情况会复杂到每一步有几百种情况,普通的搜索方式计算机可能完全无法承受。为了能让计算机更快更有效的计算出结果,通常在搜索中我们都需要引入枝剪和优化,设定一些固定技巧,以及拒绝计算一些明显比较差的选择,以此来改进搜索算法的效率。
除了搜索我们还需要对每一步的结果进行估值。估值表示了当前局势的好坏程度,通常用一个0到100的浮点数来表示估值,估值的准确性是衡量AI的一个关键点,只有正确的计算出局势的估值,AI才能知道当前选择的行动方向它的效益是多少,是否达到了最大效益的目的。
我们拿五子棋来说,棋盘上的每一个点都是对手下一步的可能性,通常不会在空白处下一个完全不着边的棋,所以搜索范围缩减到了当前棋盘上有棋子的范围周围的空位。AI对所有这些空位录入到程序中并搜索下一步最佳摆放位置,当AI把棋子放入该位置上后,对方做出的反应后AI由此引发了后几步的对弈可能。我们假设AI只搜索和计算后5步的预测,也就是说,计算出来的当前落子的估值,就是所有棋盘中可落子的后5步内最优估值。
最后通过数据录入和训练增强AI的博弈能力。人类的手法很高明,欺骗性很强,很容易就能找到AI的盲点,所以录入人类的想法也是博弈AI的关键点。
96年97年的人机国际象棋大战就是录入了大量的棋局来告诉AI人类的一些固定走法,让AI在对弈中识别人类的套路。通过数据录入以达到对固定手法的判断,这样做的局限性比较大,无法应付快速更新换代的技巧和想法。为了做出更强大的博弈式AI,我们不得开始让AI拥有自学的能力,通过模仿人脑的神经系统网络,再用数据训练,让机器学习更加有效。它们原理就是数据训练+估值判定,给AI不断地喂数据的同时,告诉AI这个数据是对的还是错的,或者说估值能达到多少,通过不断的数据训练(俗称喂数据)的方式,让AI的神经网络系统像人脑那样形成链路,这样就拥有了丰富的‘经验’,进而在对弈中不断学习,每次对弈对AI来说既时竞赛同时也是学习。
感谢您的耐心阅读
Thanks for your reading
版权申明
本文为博主原创文章,未经允许不得转载:
《Unity3D高级编程之进阶主程》第八章 AI(3) 非典型性AI
Copyright attention
Please don't reprint without authorize.
微信公众号,文章同步推送,致力于分享一个资深程序员在北上广深拼搏中对世界的理解
QQ交流群: 777859752 (高级程序书友会)