导读:很多人都对《AI War:Fleet Command》中的AI是如何运行表示好奇,而比起大多数RTS游戏中的AI,我们已经获得了更加现实的策略/战略结果。
决策树:大多数RTS游戏中的AI
首先,大多数游戏中的AI系统是基于巨大的决策树(如果A,然 后C,如果B,然后D等等)而运行。这将在某种程度上帮助人类行为,但这却需要更多的开发并最终获得可利用的缺 陷。自从1998年以来,在每一款RTS游戏中我最喜欢的例子便是他们是如何围绕着墙进行探索;如果墙体有一道缝隙,那么AI就会总是努力穿过这个洞。人类玩家会在敌人卡住的这 个缝附近集结大量单位,玩家其实是利用这个洞作为陷阱来“欺骗”AI。但AI还是会一波又一波地朝那个洞去送死。

AI War:Fleet Command
这一基于规则的决策树不仅编程工作量大,而且容易被玩家作为漏洞加以利用。而为了模仿人类玩家是如何游戏的,我们就更需要使用这一方法。一开始我使用了决策树,但是很 快地我便意识到这有点无聊,因为它只停留在基本的概念层次—-如果我想要与人类对抗玩游戏,我只需要与其他人进行游戏便可。我真正希望的是AI能够基于全新方式而行动,即 不同于其他人类所做的那样,就像与Skynet或《Ender’s Game》中的Buggers进行对抗一样。玩家会觉得AI足够新奇且聪明,而因为比起CPU,人类的大脑具有不同的优势与劣势, 所以AI在面对恐怖场景时的表现也与人类不同。在小说和电影中有许多关于这种情况的例子,但是在游戏中却并不多。
分散的智能
我所采取的方法(并在游戏开发早期阶段快速获得意想不到结果)便是模拟每个个体单位的智能,而不只是模拟一个完整的控制智能。如果你读过Michael Crichton(注: 美国畅销书作家和影视导演、制片人)写的《猎物》,你就会发现书里的AI有点像成群的纳米机器人。主要的不同在于我的个体单位比起他的纳米机器人更加聪明,因此在我的游 戏中,AI群体的数量通常为30至2000艘舰船,而不像纳米机器人那样多大数百万或数十亿。这也意味着我的单位在获得真实感方面具有零风险。对我来说最大的好处便是可以基于 较少的代码和代理获得更加智能的结果。
战略层
对于《AI War》中的AI主要存在三个层面:战略,副指挥官和个体单位。所以这并不是一种真正的群体智能,因为它将群体智能(在个体单位层面)与更多全球化规则和行为整合 在一起。AI是如何决定巩固哪个星球,或者决定哪个星球发送电波等都是基于逻辑的战略层——全球指挥官。AI在个体星球中决定如何使用舰船进行攻击或防御是基于副指挥官和 个体舰船逻辑的结合。
副指挥官
很酷的是,副指挥官的逻辑是完全突发的。基于个体单位逻辑的代码编写,单位可以做些对自己有利的事,但同时也需要考虑群组中的其他成员在做什么。这是一种群集行为理念 ,但比起移动更适用于战术和目标选择中。所以当你看到AI将舰船送到你的星球上时,将其分化成2至3个群组,并突然撞击你的星球上的各种目标时,你便会意识到副指挥官行为 是从未进行明确编程。这里并不存在像游戏代码那样的内容,但是AI却会执行一些类似的行动。AI会基于这种方式做出一些让人惊讶的聪明事,它也从不会做出那些基于规则的AI 所做的低能事。
最棒的一点便是这里并不能耍花招。这并不是说系统非常完美,而是如果玩家找到一种方法去欺骗AI,他们就必须全部告诉我,而我可以快速地在代码中添加一个对立内容。因为 我们能够即时掌握相关信息,所以游戏中并不存在任何让玩家去欺骗AI的方法。AI只在主机上的一个单独线程中运行着,所以我会分配给它大量的数据处理工作(使用LINQ—-我的 背景是数据库程序设计和ERP/财务追踪/TSQL的收益预测应用,许多都与这里的AI相关联)。使用各种变量意味着它可以在不影响双核主机上任何内容运行的前提下做出最聪明的决 策。
模糊的逻辑
模糊的逻辑/随机性也是我们AI的另一大重要组件。创造一个不可预测的AI系统的一大环节便是确保它始终能够创造出有效的选择,但却不一定是100%的最佳选择(因为具有重复性 ,“最佳”选择也会变得不那么完美)。如果一位AI玩家只创造过完美的决策,那么你便可以通过想出自己的最佳决策(或者在你的势力中创造一个伪造的弱点,如在墙上设个缺 口)去攻击他,然后你便可以预测带有更高精确度的AI将做什么——在许多其它RTS游戏中的特定例子中,这种方法的有效性接近100%。基于适当的模糊逻辑设置,我想说在《AI War》中你能够预测到AI将会做些什么的机会不会超过50%,我们的游戏中设置的是较难预测的方法。
智能错误
需要记住,较低难度级别会故意创造一些愚蠢的决策,就像新手们可能会做的那样(如不顾一切地追寻最佳目标)。这会让更低级别的AI仍然看起来就像一个真正的对手,但却不 那么可怕。对我来说,为了降低难度而设置AI便是个巨大的挑战。在某种程度上,这只是在阻止较低级别AI执行最佳战术,但同时我还要在它的决策(在较低级别中所做出的决策 )中植入一些不是很完美的假设。
略过经济模拟
最后,《AI War》中的AI是遵循着与人类玩家完全不同的经济规则(注:但是所有帧数和大多数策略规则却是一样的)。举个例子来说吧,在大多数游戏中,AI一开始都是 伴随着2万以上的舰船出现,但是每个玩家一开始只带有4搜舰船。而如果AI的优势能够完全将你压倒,你便会立刻被击退。就像在《超级玛丽兄弟》中,如果每个关卡中的所有坏 人同时向你展开攻击,你便会立刻死去(因此它们被分设在不同地方)。如果FPS游戏中任何特定关卡的所有敌人都直接走向你并准确地朝你射击,你便没有生存的希望。
想想你所玩过的FPS游戏,即模拟你在军事行动中的表现—-所有敌人并不总知道你和同盟在做什么,所以即使敌人拥有压倒性的优势,你也可以通过打击关键目标等方法而获胜。 我认为这与现实中的许多情况非常相似,但是你在其它RTS游戏的小规模战斗模式中却不可能遇到这样的情况。
我将在本系列之后的文章中对该话题进行详细讨论,因为这可能成为我在创造这款游戏时所做出的最富争议的设计决策。一些人可能会将其当成一种欺骗AI的形式,但是我有这么 做的理由。AI船舰所获得的奖励永远不会超过玩家,AI不会拥有有关玩家活动的过多信息,AI并不会因为玩家的任何行动获得奖励或惩罚。关于游戏中的AI的策略和战术代码使用 的是与人类玩家完全相同的规则,这也是我们的智能系统真正突出之处。
不对称AI
在《AI War》中,为了提供程序活动去呈现出大卫对抗巨人歌莉娅之感(注:人类玩家总是扮演着大卫的角色),我为AI的某部分与玩家行为的对抗创造一个单独的规则。 AI经济是基于内部加固点,电波倒计时,以及基于玩家行动增加或减少的整体AI进程数。这能让玩家去设置游戏前进的节奏,从而会添加你通常只能在回合制游戏中遇到的另外一 个策略层面。这是一种非对称系统,即你在带有类似人类行动的AI的PVP游戏中不可能看到的,但是它却能够有效作用于AI作为敌人的合作类游戏中。
上述内容在读者当中引起很大的反响,然而有人批评我把它写得太入门/太宽泛。确实,作为本系列的开头,我把难度设得比较低。如果你不是程序员或IA爱好者,那么超过这个程 度可能不会让你觉得太有趣。
你这是什么意思,你的AI代码就像数据库一样运作?
大部分人提的第一个问题是,我的AI代码怎么可能像个数据库。在之前的文章中,我已经解释了如何使用常见的ROLLUP。这也有助于AI的表现,但那不是我要说的重点。
我使用LINQ来执行游戏AI的目标选择和其他选择,这么做确实相当节省第一层决策的代码(也缩减总体必要代码,我估计我们的游戏AI的决策代码总量不超过20000行)。以下是确 定目标选择的一段LINQ查询代码:
var targets =
//30% chance to ignore damage enemy can do to them, and just go for highest-value targets
( unit.UnitType.AIAlwaysStrikeStrongestAgainst ||
AILoop.Instance.AIRandom.Next( 0, 100 ) < 30 ?
from obj in rollup.EnemyUnits
where ( unit.GuardingObjectNumber <= 0 || //must not be guarding, or guard target must be within certain range of guard post
Mat.ApproxDistanceBetweenPoints( unit.GuardingObject.LocationCenter,
obj.LocationCenter ) < Configuration.GUARD_RADIUS )
orderby obj.UnitType.ShipType == ShipType.Scout ascending, //scouts are the lowest priority
obj.GetHasAttackPenaltyAgainstThis( unit ) ascending, //ships that we have penalties against are the last to be hit
(double)obj.GetAttackPowerAgainstThis( unit, usesSmartTargeting ) / (double)obj.UnitType.MaxHealth descending, //how much damage I can do against the enemy
out of its total health
obj.IsProtectedByForceField ascending, //avoid ships that are under force fields
obj.NearbyMilitaryUnitPower ascending, //strength of nearby enemies
Mat.ApproxDistanceBetweenPoints( obj.LocationCenter, unit.LocationCenter ) ascending, //how close am I to the enemy
obj.UnitType.ShieldRating ascending, //how high are their shields
unit.UnitType.AttackPower ascending, //go for the lowest-attack target (economic, probably)
obj.Health ascending //how much health the enemy has left
select obj
:
from obj in rollup.EnemyUnits
where ( unit.GuardingObjectNumber <= 0 || //must not be guarding, or guard target must be within certain range of guard post
Mat.ApproxDistanceBetweenPoints( unit.GuardingObject.LocationCenter,
obj.LocationCenter ) < Configuration.GUARD_RADIUS )
orderby obj.UnitType.ShipType == ShipType.Scout ascending, //scouts are the lowest priority
( chooseWeaklyDefendedTarget ?
obj.UnitType.TripleBasicFirePower >= obj.NearbyMilitaryUnitPower :
( chooseStronglyDefendedTarget ?
obj.UnitType.TripleBasicFirePower < obj.NearbyMilitaryUnitPower : true ) ) descending, //lightly defended area
(double)obj.GetAttackPowerAgainstThis( unit, usesSmartTargeting ) / (double)obj.UnitType.MaxHealth descending, //how much damage I can do against the enemy
out of its total health
obj.IsProtectedByForceField ascending, //avoid ships that are under force fields
obj.NearbyMilitaryUnitPower ascending, //strength of nearby enemies
obj.GetHitPercent( unit ) descending, //how likely I am to hit the enemy
unit.GetAttackPowerAgainstThis( obj, false ) descending, //how much damage the enemy can do to me
obj.Health ascending //how much health the enemy has left
select obj
);以上使用了很多格式化的缩写,但愿读者借助绿色字的注释能够理解那是怎么回事。一定程度上,你可以把上述代码叫作决策树(它确实有很多分支),但总体说来,代码更加简 洁(如果能恰当地使用格式化的命名的话)和容易阅读了。但这种结构的优点在于,因为是按分类执行的,而不是通过严格的if/else等条件语句,这就相当于给AI一个做某件事而 不做另一件事的倾向。
这段代码考虑到很多情况,有许多可以运行的不同模式,这就使AI本身具有很高的智能。但还不止如此。实际上评估上述逻辑的循环也让AI本身更加智能。
bool foundTarget = false;
foreach ( AIUnit enemyUnit in targets )
{
if ( enemyUnit.Health <= 0 || enemyUnit.CloakingLevel == CloakingLevel.Full )
continue; //skip targets that are already dead, or are cloaked
if ( unit.CloakingLevel == CloakingLevel.Full &&
enemyUnit.UnitType.ShipType == ShipType.Scout )
continue; //don’t give away the position of cloaked ships to scouts
if ( unit.CloakingLevel != CloakingLevel.None &&
enemyUnit.UnitType.TachyonBeamRange > 0 )
continue; //cloaked ships will not attack tachyon beam sources
if ( enemyUnit.UnitType.VeryLowPriorityTarget )
continue; //if it is a very low priority target, just skip it
if ( enemyUnit.IsProtectedByCounterMissiles && unit.UnitType.ShotIsMissile )
continue; //if enemy is protected by counter-missiles and we fire missiles, skip it
if ( enemyUnit.IsProtectedByCounterSnipers && unit.UnitType.ShotIsSniper )
continue; //if enemy is protected by counter-sniper flares and we fire sniper shots, skip it
if ( enemyUnit.GetAttackPowerAgainstThis( unit, false ) == 0 )
continue; //if we are unable to hurt the enemy unit, skip attacking it
if ( unit.EffectiveMoveSpeed == 0 && !unit.UnitType.UsesTeleportation &&
enemyUnit.GetHitPercent( unit ) <>
continue; //stop ourselves from targeting fixed ships onto long-range targets
gc = GameCommand.Create( GameCommandType.SetAttackTarget, true );
gc.FGObjectNumber1 = unit.FGObjectNumber;
gc.FGObjectNumber2 = enemyUnit.FGObjectNumber;
gc.Integer1 = 0; //Not Attack-Moving
unit.LastAICommand = gc.AICommand;
AILoop.Instance.RequestCommand( gc );
foundTarget = true;
break;
}
//if no target in range, and guarding, go back to base if out of range
if ( !foundTarget && unit.GuardingObjectNumber > 0 )
{
Point guardCenter = unit.GuardingObject.LocationCenter;
if ( Mat.ApproxDistanceBetweenPoints( guardCenter, unit.LocationCenter ) >
Configuration.GUARD_RADIUS )
Move( unit, guardCenter );
}以上代码没什么真正出奇的地方,但它的决策点更多了一些(大多是硬性规定而不是倾向)。另外,在追踪逻辑中,一旦选定目标,船舰就有了一个倾向,因为并非目标都是其实 是一样—-它们观察彼此在做什么这个方面确实是必要的,至少在我采用的游戏设计法中是如此,目的是为了让它们高效地分支和拆分和命中更多目标。
我不是一点一点地分析上述代码,而是基本上让代码不言不喻,所以我的注释其实是多余的。
警戒水平
上述代码的一部分重要逻辑是警戒水平,也就是说,以上代码让AI根据受周围舰队保护的情况来决定是否定位目标。所有飞船只都有30%的概率忽略警戒水平,而朝着它们的最佳目 标前进,某些类型的飞船(如Bomber)始终是那么执行的,这使得AI更加难以预测。
这种办法的主要优点是,它导致AI大部分时候选择保护不严密的目标(如消灭所有偏远的采集船或保护不当的施工船),然而,这些飞船或部分这些飞船仍然有可能冒险去进攻有 价值但受严密保护的目标,如你的指挥站或高级工厂。这类不确定事件通常给人非常人为的感觉。即使AI偶尔携大批飞船做出像集体自杀似的行为,如果你不够留神,这种进攻也 可能很有效。
根据玩家反馈改变AI行为
有些读者阅读了本系列的第一部分后,指出我并没有像我所承诺地那样修改他们发现并告诉我的漏洞。言之有理,我确实没有进行解释,所以我接受读者的批评。然而,正如我所 强调的,在内测版本我们没有发现任何利用AI的漏洞,所以我并不太担心这会成为普遍性问题。
但我想说的其实是,在你所看到的这种AI系统做修改是相当简单的。在我们的内测版本中,当有人发现欺骗AI的办法时,通常几天内甚至几小时内我就能想到解决方案。原因很简 单:LINQ代码很短很直观。我要做的只是决定AI要遵守什么规则或倾向、当必须有倾向时,其相对优先级是什么、以及添加几行代码。仅此而已。
有了这种树形决策方案,我认为我的代码得以保持简洁(我有10类AI,有些基本上像数据库—-如AIUnit,有些是rollup—-如AIUnitRollup)。我提倡使用这种代码的理由是,它 不仅运行得好、可以产生相当漂亮的应急行为,而且容易维护和拓展。这是值得考虑的方法—-这类AI容易实验,如果你想在你的下个项目中尝试的话。
该方法是有限的?
当然。我不认为任何AI方法都能够用于排除其它内容。我们的AI设计非常杂,所以我在《AI War》中并不可能使用任何单一的方法。我使用的是与自己正在创造的游戏相关的技术 和方法,在某些方面(如探险),我还将其与传统方法混合在一起。
接下来的内容的目的是讨论我所提议的新方法(意外方面和LINQ方面)的局限性,因此我将阐述这些技巧与其它游戏中的技巧相混合的一些方法。让我们开始分析:
在哪里能够快速分解突发性行为
为了获得突发性行为, 你需要拥有足够的代理。为了阻止它们做一些疯狂的事,你需要设置他们可能出现的位置的界限(就像在《AI War》中,这些界限便是关于在哪里攻击或者 跑向哪里。基本上来看就是战术)。以下是一些我认为不适合突发性行为的例子:
1.带有竞争性的益智游戏,如《俄罗斯方块》。因为这类游戏中通常只有一个代理(即光标)。
2.RTS游戏中的经济模拟。这是相互关联的,即任何决策都会对剩下的经济内容产生潜在的影响(如创建一栋建筑并且机会成本非常高的话,那么就非常需要一些持续协调的类)。
3.为了预测玩家行动,任何类型的游戏都需要历史知识。就像包含褶边装置的纸牌游戏如果未分析任何历史知识的话便不可能有效地经营。
4.因为巨大的机遇成本,任何基于回合制且每个回合具有有限移动数的游戏可能都不能处理好这点。尽管《文明IV》属于回合制游戏,它却能够有效地使用突发性技巧,但是象棋 和围棋却做不到。

AI-War
为什么突发性行为适用于《AI War》
1.游戏中有许多单位,所以能让玩家基于每个单位的智能进行复合思考。
2.尽管决策空间非常有限(该攻击什么,或者在哪里撤退),同样的决策空间却非常复杂。我们只需要着眼于LINQ逻辑中的所有决策点便可。关于任何特定攻击能够获得某种程度 上的成功通常都存在50种方法,并且关于他们的失败也存在1000种方法。这种精确性能让AI在近乎最佳选择间模糊自己的选择,并最终导致战术的不可预测。
3.每个决策制定的成本都很低。AI可以在1秒内为一组舰船做出100个决策,然后当这些命令获得落实,并且情况开始出现不利的改变,他便能够制定一系列全新的决策。
4.在战术中,不可预测性的价值高于上述所有内容,并且能够与突发性行为相互合作。只要AI不做出一些愚蠢的行为,你就可以让它在可行的选择(可能导致让人惊讶的战术)以 及显然的策略(只是偶然事件)中做出选择。如果游戏中存在一些通向成功的方法(就像在赛车类游戏中),那么我们便会因为很难设定决策的界限而不能为合理的突发性行为创 造任何真正的机遇。
随着时间发展而学习
我在《AI War》中有意创造的一个设计选择是确保其专注于当前的情况。我记得在象棋锦标赛中看过一些大师级象棋选手会同时与50名基于常规排名的玩家进行比赛,在房间中四 处走动并以此分析每个位置。在大多数棋盘上,他们只需要看一眼设置便能够立刻做出移动。
我认为如果能让一个AI具有这些能力的话便是一种很棒的设计。它将着眼于当前的故事板,并忽视之前所掌握的内容(实际上这只是一个论题专家而不是具有任何能力的学习型AI ),然后它将根据自己所看到的做出任何有效的选择。当玩家做出一些意想不到的事时,这种设置便非常有趣,因为AI也会作为回应而做出一些意想不到的事。
这非常适合《AI War》,但是如果玩家不能学习并改变,或者我不能扩展并完善AI,这便不能随着时间的发展而发展。这是我有意识设计的一种系统,但是在带有关于AI和人类对 称规则的游戏中,这种方法带有一些局限性。
在这些情况下,最好的方法便是将突发性决策制定与随着时间发展而收集到并评估的数据结合在一起。这些收集到的数据将成为LINQ查询中的每个代理的决策点。当然了,这要求 更多储存空间以及更加强大的处理能力,但是如果有人能够基于适当的有限评估去完成这一任务,那么他最终所获得的好处将变得更加巨大。
LINQ查询难道不只是一种策略树?
一些AI程序员会抱怨,我在这里所分享的LINQ查询与传统的决策树并无差别。对此我想说:你说得没错,在这方面看来它们并没有差别。LINQ查询的主要优势便是可读性的提高( 假设你知道LINQ,那么你便能够理解一些复杂的描述)。
而其它优势则是关于一些“偏好选择”,即能够轻松地呈现出来。比起拥有一个巨大且分支IF/ELSE陈述句,你在LINQ中拥有一个ORDER BY条款,即能够让树的评估变得更加灵活。 换句话说,如果你拥有如下内容:
ORDER BY comparison 1,
comparison 2,
comparison 3你便能够设定一种情况,即1为真,2为假,3为真,就像所有的这三个数都是真的或假的,真的,假的或任何其它比较。而在传统代码中,如果没有足够的副本或使用让人恐惧的 goto,我便想不出任何方法去呈现出这种灵活性。
所以,从理论上来看,LINQ查询理念与决策树理念非常相似,而在实践上,它不仅更具有可读性,同时基于你的决策树的复杂性,它也会变得更加有效。你甚至不需要使用LINQ—- 任何足够复杂的分类算法都可以做相同的事。该方法的新颖性并不是源于LINQ本身,而是关于使用分层分类算法去取代决策树。你可以在C#中表达上述的LINQ代码,如下:
someCollection.Sort( delegate( SomeType o1, SomeType o2 ){int val = o1.property1.CompareTo( o2.property1 );if ( val == 0 ) val = o1.property2.CompareTo(
o2.property2 );if ( val == 0 ) val = o1.property3.CompareTo( o2.property3 );return val;} );实际上,贯穿《AI War》的代码,关于这一分类的陈述其实非常常见。比起同意义的LINQ陈述,还存在其它更有效的执行方法,所以在主线程中(即性能是关键),这便是我所使 用的一种分类方法。在alpha版本中,我直接将其整合到LINQ中,这对于创建方法的原型来说非常棒,但是当我转变了所有内容(除了AI线程)到这些分类中而不是LINQ,对此我是 单纯出于性能原因。如果你是致力于其它语言或不熟悉的SQL类代码,你也可以很轻松地开始并结束这种分类。
结合方法
在《AI War》中,我基本上使用了如下方法:
1.传统探索
2.面向主要战略决策的简单决策引擎类逻辑。
3.面向战术的每单位决策引擎类逻辑。
4.为了降低预测能力并鼓励突发性行为的模糊逻辑(对于决策来说的最小随机性)。
5.基于偏好的代码(以LINQ和分类形式表达出来)而不是强制规则类IF/ELSE决策树逻辑。
如果我想为一款FPS游戏或赛车类游戏编写一个AI,我可能会采取与这些类型游戏执行过的方法相类似的模式。我不能想到更好的方法了。你可以在战争类游戏中将突发性行为与更 大的敌人群组结合在一起,这可能会创造出一些有趣的结果,但是对于个体AI玩家,我就想不出其它不同的做法了。
对于一款益智游戏或像象棋那样的回合制游戏,我也会再次使用来自其它游戏的现有方法,这是关于未来的行动及其结果的深入分析,因此我们需要选择一个最佳方法(注 :如将一些启发法带入速度处理过程中)。
一些冒险游戏也可以使用少量群体行为去创造有趣的效果,但是我想许多玩家应该都喜欢这类型游戏中基于规则的传统敌人设置吧。这些游戏并不是真的想要突出模仿人类的AI, 相反地,它们的AI只是遵循着一些能让人类玩家了解并记住的简单规则。
我们能够较轻松地为分类决策树编写代码,并可能会带来一些更有效的结果,但是最终作用于玩家身上的结果却不会有多大差别。而创造出更容易编程与阅读的AI代码则对所有人 来说都是一件好事(低成本/较少的开发时间,并且可能出现更少的漏洞等等)。
现在,我们来探讨一下不对称系统如资源管理在《AI War》中的运用。
对我来说,这个论题很有挑战性,因为它的两方面都受到游戏设计师们的密切关注。最近我想起来我已经就这个论题写过一篇文章了。我当时是为了向内测人员解释我打算改变概 念。那时候,他们质疑我的想法(如果有人建议,我也会怀疑的),那篇文章非常有助于说服他们相信我的概念可能有些优点—-最有说服力的一点当然是,他们可以看到AI在实际 操作中的良好表现。
这款游戏那时已经开发了近三个月,但令人惊喜的是,在正式发布的两个月后,游戏基本上还是使用我在那篇文章中写到的方案(注:有所改变的是有些AI的最终表现方式 ,以及AI飞船被分类成进攻型和防御型——在《AI War》中这些都是次要的,玩家会注意到其与本文所述有出路,但整体思路并无差别)。
RTS游戏中的AI
大多数即时策略(RTS)游戏的AI都努力模仿人类玩家的行为。RTS游戏的AI与人类玩家承担相同的责任和义务,这种AI的成败取决于对人类行为的模仿水平。
这类AI严重依赖极其复杂的有限状态机(FSM)——换句话说,“如果遇到情况X,则执行Y,然后执行Z。”这种判断算法耗费大量设计和编程时间,且相当容易被玩家预测到。最 糟糕的是,这些算法往往不能产生有趣的结果——面对这类AI就像对抗其他人类玩家,只是比人类更傻一些。聪明的玩家能够通过寻找算法中的模式和缺陷来欺骗AI,AI通常对玩 家的行为反应迟钝——如果有反应的话。即使如此,这些也让某些可怜的AI程序员辛苦工作了好几个月。
其他游戏中的非确定性AI
在大多数现代AI设计中,非确定性是一个重要目标——也就是,面对相同的输入,AI不能总是产生完全相同的输出。否则,AI的可预测性就太强了。达到这个目标的主要办法是模 糊逻辑(输入是“模糊的”,这样输出就不会那么准确了),或者增加不断成长和变化的学习型AI的变体(注:这种AI会根据自己积累的知识做出不同的行为)。
标准的学习型AI的问题是,它很容易学习错误的东西,然后做出一些古怪的举动。排错是很困难的,因为很难知道AI在想什么以及为什么。另外,除非它有一些基本的历史数据, 否则它可能变得非常无预测性和不实用,或者也可能因为确定性方法让它变得容易预测。这就像教一只阿米巴原虫跳踢跶舞,但它却开始放火了,你诧异它为什么这么做,但这就 是它的行为模式的一部分。
因此,甚至有了学习型AI,你的游戏也可能具有很强的可预测性。另外,如果游戏支持保存,那么AI的所有历史数据也得保存下来,否则下次进入游戏,AI就不知道自己要做什么 了(保留它的学习智能)。如此一来,除了其他缺点,又多了保存文件庞大这个问题。
《AI War》中的半无状态AI
对于《AI War》,我希望非确定性AI的行为不依赖任何历史数据。当然,这意味着要定义相当多的模糊逻辑—-那确实是合适的,但我还希望保留一些学习型AI的特征。事实上,我 是希望模拟现代商业的数据挖掘技术(这是通过我的日常工作而精通起来的东西)。我的经验法则是:在任何给定时间,AI应该能够考虑到一系列有意义的变化,然后运用某些规 则和公式,最后得出可能的最佳方案(当然是模糊的)。
举一个人类的例子:在象棋比赛中,通常大师会与大约40个正常人类玩家比赛(纯粹是为了娱乐或宣传)。40个低级选手在屋内围成一圈,他们面前放着棋盘,各自与大师进行比 赛。大师在屋里每走一圈,就在所有人的棋盘上走一步。大师不可能记住40张棋盘上的情况,相反地,他是走到棋盘面前才开始分析,然后走最好的一步。当遇到比较厉害的选手 时,大师就得思考得更严谨一些,但大师通常赢下所有这40盘棋,因为水平毕竟有差距。通常结果是,大师从中挑选最聪明的选手,故意让他赢(如果是为了获得奖励或炫耀,大 师就会打败所有选手)。
《AI War》中的AI与上述大师非常类似——在面对一度看不见的情况时,它能做出可能最理想的选择。AI可以不断积累这些少量的数据(就像大师可能记住最聪明的对手的棋局) ,但总地说来这是不必要的。AI还记住与过去行为有关的数据,目的有二:一是帮助它继续完成上次的决策,除非有强制性理由使它中断;二是防止它的行为变成一种可能被对手 利用的模式。
其他RTS游戏中的指令分散化
组成一个有趣的AI对手的关键因素之一是,它必须能够同时应付多件事。在许多游戏中,AI一次只会移动一个军事单位,而人类玩家往往不是这样的。人类玩家会同时执行绕道、 多场正面进攻和侧面进攻等。
在《AI War》中,战术和战略指挥官必须能够根据手头上的单位执行尽可能多的、合理的不同活动。通常办法是,创造多个独立的AI玩家“代理”,然后把各个单位操作分派给特 定的代理。接着你会遇到这些代理之间必须互相协调的问题,但在《AI War》的半无状态环境中,情况甚至更糟——当分配单位时,你怎么能明智地在这些任意代理之间划分单位 ?怎么知道什么时候刷出更多代理或者合并无用的代理?这些问题都可以解决,但不要低估了它们,毕竟对CPU不利。中心问题是资源管理。不仅授权现存单位的控制,而且要使战 术/策略元素与资源生产和新单位生产保持平衡。
《AI War》中的资源管理
我被指令分散化的问题困扰了好几天,我努力思考能够满足所有指标并最终实现的解决方案:重要的不是AI到底在做什么,而是人类玩家看到它在做什么。如果AI要经过一些玩家 看不到但非常消费编程精力和占用CPU的活动才能得到结果A,那么直接走捷径产生结果A会不会更好?特别是,我意识到就玩家而言,经济模拟的回报是很低的。如果我给AI使用人 类的经济模型——没有资源、技术,只有普通的、线性的飞船生产算法,会产生什么影响?
首先,这种改变导致AI玩家不使用建造船、工厂、反应器或其他与经济有关的舰队。这一定程度上改变了玩法,因为人类玩家无法使用进攻经济建筑的战术来削弱AI。这绝对是一 个缺陷,但无论如何,在大多数RTS游戏中,AI往往掌握大量资源,所以进攻经济建筑也不是一个有效的策略。
确定这种玩法改变的可行性以后,我开始设计飞船的生产算法。说起来容易做起来难,因为各个AI必须知道在游戏的某时它可以生产什么飞船,也要知道每一次生产和保持其他因 素平衡允许使用多少材料。注意,这不是一般意义上的“欺骗性AI”——想做什么就做什么的AI。这里所说的AI玩家遵循的是一套不同的规则,但它们是严格地模拟一般情况下AI 不会做的事。
《AI War》中的指令分散化
解决了经济以后,我们再回头说说分散化问题。现在,在每一次资源事件中,各个AI都允许生产一定数量的某种飞船,但如何智能地选择生产什么飞船?另外,如何处理已经存在 的飞船?
首先,AI必须把它的单位划分成两类——进攻型和防御型。大多数游戏不会这么做,但在《AI War》中这个做法非常有效。各个AI决定它需要多少某种飞船来保护各个星球或它所 控制的中央舰。为了实现这些防御目标,它的首要任务是产生。
所有不需要防御的单位都被当成进攻单位。这就是下面要说的策略选择算法。由AI玩家操作的各个生产单位的飞船会根据复杂的模糊逻辑和加权来生产进攻单位——结果是,AI通 常生产最强的单位(对AI有利的单位),但从来不会完全停止生产较弱的单位(一定程度上,它们在《AI War》中总是仍然有用的)。
《AI War》中的战略路线
《AI War》中的的战略计划包括决定向什么星球派遣什么单位。防御单位往往不会离开它们的星球,除非它们所保护的东西也离开了,这基本上意味着围绕进攻单位选择路线。
这表现为两种一般性活动:1)进攻——在能够造成最大破坏时向某星球派遣飞船;2)如果有可能,在遇到压倒性失败时选择撤退(我见过的其他RTS游戏的AI还没有出现这种行为 )。
AI并不使用诸如玩家得分这样无意义的因素,而是在这类决策中的任何其他非玩法变量。它的所有决策都是根据什么单位在哪里,以及它当前计算出的最可能发生的斗争结果。
《AI War》中的战术决策
就概念而言,战术决策其实是AI中比较简单的部分。各个单位争取在它达到最理想的目标距离时攻击它最可能伤害到的目标。它会一直战斗到它死了或者它的敌人死了或者策略路 线算法要求它撤退。
《AI War》中的AI类型
当我回想大多数RTS游戏中出现的仿人AI时,能令我感到惊喜的AI是非常有限的。在大多数游戏类型中,你对抗的是一群具有不同于人类玩家的力量的不同敌人。有些对手非常强大 ,有些比较脆弱,各有优缺点。在RTS游戏中,所有人都是一样的—-或至少被平衡到接近公平的程度。我认为这么做折损了游戏的寿命。
我觉得更有趣的做法应该是,提供类型更丰富的AI,它们各有强项、弱点以及独特的行为,这也是我在《AI War》所做的。有些AI具有玩家永远得不到的战舰,有些在游戏一开始 就掌控了多个星球,有些从来不占领星球而是像无法无天的突击者在中立星球周围徘徊。可能性是无限的,特别是当玩家在一场游戏中对抗多种类型的AI时。
无限的可能性通常导致有意的不公——就像真实的战争一样。你可以模拟进攻一个由邪恶外星人控制的星系,或相反地,由敌人进攻你。我认为这与《Ender’s Game》类似。有胆 心怕事的AI,也有在你大战一场后趁机进攻的AI。有些AI使用的战术非常怪异,让你防不胜防,还有些AI惯用偷天换日的战术,以隐藏他们真实的意图。有些AI在一开始时技术就 远远强过你,有些AI有大量劳动力但技术低下……
《AI War》的总体目标是为玩家提供一种有趣的、丰富多变的游戏体验。如果所有AI除了行为以外都基本相同(就像大多数游戏),那么玩家的选择就很有限了。《AI War》中丰 富的AI类型不属于传统意义上的“欺骗性AI”——各种AI都有自己遵循的特定规则,它们的规则与人类玩家所遵守的规则并不一样。
类比一下足球游戏中的进攻和防守:各支球队都有一系列不同的目标——一个是自己得分,一个是不要让其他对手得分,但一支球队的总体成功取决于他们对各个目标的执行情况 。在足球中,球队通常也划分出进攻队员和防守队员,这与《AI War》中的AI分类是相似的。在《AI War》中,更确切的类比是,如果一支球队总是进攻,但有三名队员作为后卫 ,也许后卫只能通过换球和传球来得分。但因为队员更多,得分可能容易得多。
《AI War》中的AI类型是,它们故意破坏规则的平衡,但不是通过欺骗,而是提供真正创新丰富的东西。
未来的《AI War》中的AI
现在,AI还没能做出非常有趣的战术决策——侧面攻击、集中火力进攻重点目标等。将来重做AI时,我们将加入这些和其他“行为”。在遭遇战时,AI将评估行为条件,并在满足 条件时做出相应的行为。
事实上,我在这里所说的“行为”与所有类型的AI都有关。现在,AI非常依赖参数,这导致玩家容易预测它的行为,除非通过模糊逻辑增加随机性。这就是许多其他RTS游戏AI的极 限(然而这种程度只花了我几天的时间,而不是几个月几年),但对于《AI War》的结构,我可以继续在AI的方方面面添加新的“行为”,使它越来越强大、越来越多样化、越来 越像人类。比如侦察、狙击、布雷、撤退、利用地势进攻、经济定位、使用运输工具、放弃星球等。所有这些行业都可以通过比较简单的编程加入到现有的结构中;复杂的是行为 本身,而不是这些行为如何交互作用于AI有限状态机的大元素(例如)。这是一种非常对象导向型的AI设计法,符合我的思考习惯。
电脑要求
这是非常模块化的AI系统,可以一直拓展,但它也极其高效—-它已经能够控制成千上万种飞船,且不会严重影响电脑的双核处理器。然而,我们在设计时只考虑双核电脑,所以游 戏很可能在单核电脑中不能运行得同样好。
另一方面,AI逻辑只在游戏主机中运行(这对于RTS游戏是不常见的——也许创造了另一个“第一”),这意味着单线程电脑可以加入其他人的多人模式中。考虑到这是目前最大的 RTS游戏(就游戏玩法中允许使用的活动单位而言),这也是相当了不起的了。
总结
正如本系列开头所说的,本文的主体是在游戏的内测阶段时写的,也就是说比在纯PVP模式下测试机制、联网和图形等早了几个月。我花了大约一周时间制作传统模式的AI,然后发 现那不能达到我的期望,所以我决定回归到根本原则——用更简单的办法实现我的目标。
又思考了几天之后,我写了三天的代码,做出了可用的AI基本原型。为了让内测人员知道我的意图,我写了这篇文章,然后又花了三个月改善、扩展和完成AI(和游戏的其他部分 )。这一切都是在四月的测试前完成的。我们用一个月的时间给这款游戏做测试、修改漏洞和平衡玩法,然后在Arcen网站上推出,5月又在Stardock的Impulse平台上发布。
这种不对称AI是游戏设计中最受没有真正玩过游戏的程序员和游戏设计师批评的一个方面。虽然游戏的玩家越来越多了,但它目前得到的评论还很少(因为宣传还不够多,但多亏 了在Impulse上的热门,玩家的评论越来越多了),正真玩过这款游戏的人似乎并不觉得这是个大问题。再回到我对AI的核心认识:AI真正在做什么并不重要,重要的是玩家看到AI 在做什么以及AI行为对玩家的影响。
这种AI风格产生了许多独特的玩法、看似公平的挑战和你在其他AI上通常看不到的多样性。这虽然距离有感情的机器人还很遥远,但对于创造一款有趣的游戏已经足够了。我们的 目标不就是这个吗?创造一款人们觉得好玩的、有趣的、有挑战性的游戏?我们确实应该尝试更多不同的AI设计,因为我认为AI设计对于决定任何一种类型的游戏是否有趣都有重 要意义。
相关阅读:
文化部查处动漫名单曝光 一大波手游IP面临生死抉择http://www.sfw.cn/xinwen/465770.html
LBS玩法对移动游戏有什么影响?将带来约6亿的潜在玩家http://www.sfw.cn/xinwen/465753.html
《老涵调查》:看不到真实数据的我们看热闹吧http://www.sfw.cn/xinwen/465756.html
