编程文汇

游戏中的AI:新手完全手册(下)

学习和适应

我们在开始时提到游戏AI通常不使用“机器学习”,因为它通常不适合在游戏世界中实时控制智能角色。然而,这并不意味着当它有意义时我们不能从该领域获得一些灵感。一个射击游戏中,我们可能想要电脑学习如何寻找最佳射击点,以获得最多杀戮。或者像Tekken或街头霸王这样的格斗游戏中,在我们反复使用combo的时候,我们希望电脑能识别我们的招式,并开始格挡。这样就迫使我们尝试不同的策略。因此有时候某种程度的机器学习会很有用。

统计和概率

在我们看一些更复杂的例子之前,考虑一下,我们可以通过一些简单的度量,并使用这些数据来做出决策。例如,假设我们有一个即时战略游戏,我们试图猜测玩家是否可能在最初的几分钟内发动冲击,以决定是否需要建立更多的防御。我们可能想要从玩家的过去行为中推断,以指示未来的行为可能是什么样的。首先,我们没有关于可以推断的玩家的数据 - 但每次AI对抗人类对手时,它都可以记录第一次攻击的时间。在几次比赛之后,这些时间可以被平均,并且将是该玩家将来可能进行攻击时的相当接近的近似值。

简单平均值的问题在于它们随着时间的推移倾向于中心,所以如果一个玩家在前20次采用冲击策略并在接下来的20次切换到一个慢得多的策略,平均值将在中间位置,告诉我们没什么用的。解决此问题的一种方法是简单的窗口平均值,例如仅考虑最后20个数据点。

类似的,估计玩家某些动作在未来发生的概率方法,就是假设玩家过去的偏好将会用到未来。例如,如果一个玩家用火球攻击我们5次,闪电箭攻击2次,并且只用一次手部战斗,那么他们可能更喜欢火球,8次使用5次。根据这一点,我们可以看出不同武器使用的概率是火球= 62.5%,闪电箭= 25%,徒手= 12.5%。建议我们的人工智能角色找到一些火防御装备!

另一个有趣的方法是使用Naive Bayes分类器检查大量输入数据,并尝试对当前情况进行分类,以便AI角色可以做出适当的反应。贝叶斯分类器可能因其在电子邮件垃圾邮件过滤器中的使用而闻名,在邮件垃圾邮件过滤器中,他们检查电子邮件中的单词,将它们与过去那些单词是否主要出现在垃圾邮件或非垃圾邮件中进行比较,并使用它来判断最新的电子邮件是否是垃圾邮件。虽然输入数据较少,但我们可以做类似的事情。通过记录我们看到的所有有用信息(例如构建哪些敌方单位,或者他们使用哪些法术,或者他们学了哪些技能),然后记录由此产生的情况(战争与和平,紧急战略与防御战略等) ,我们可以根据这个选择适当的行为。

通过所有这些学习方法,在发布之前的游戏测试期间收集的数据上运行它们可能就足够了 - 而且通常更可取。这允许AI适应您的测试者使用的不同策略,但在发布后不会改变。相比之下,如果在发布后还能自动适应玩家的话,AI可能因为过于有预测性,甚至难以被击败。

基于权重自适应适应

让我们进一步了解一下。我们不是仅仅使用输入数据在离散的预编程策略之间进行选择,而是希望改变一组值,以便为我们的决策提供信息。鉴于对游戏世界和游戏规则的充分理解,我们可以执行以下操作:

  • 让AI收集有关游戏世界的状态和游戏期间的关键事件(如上所述);
  • 在收集数据时根据数据更改多个值或“权重”;
  • 基于处理或评估这些权重来实施决策。

想象一下,计算机代理在FPS地图中有几个主要房间可供选择。每个房间都有一个重量,决定了对这个房间的偏好度,所有房间都以相同的值开始。在选择去哪里时,它会随机选择一个房间,但会根据这些权重进行区别。现在想象一下,当电脑被杀时,它会记录它所在的房间并减轻权重,因此将来不太可能再回到这里。同样地,假设电脑获得了杀了人 - 它可能会增加房间的权重,将其移动到偏好列表中。因此,如果一个房间对AI玩家特别致命,它将来会开始避免它,但如果其他一些房间让AI得分很多,它会继续回到那里。

马尔可夫模型

如果我们想使用这样收集的数据进行预测怎么办?例如,在玩家进行游戏的过程中,我们记录玩家经过的每个房间,经过一段时间,我们可能会期望用它来预测玩家接下来可能会移动到哪一个房间。通过跟踪玩家所在的当前房间和之前他所能看到房间,并将其记录为一对值,我们可以计算出每种情况导致后一种情况的概率,并将其用于预测。

想象一下,有3个房间,红色,绿色和蓝色,这些是我们在观看游戏时看到的观察结果:

看到的第一个房间 总观察次数 看到下一个房间 观察次数 百分比
红色 10 红色 2 20%
绿色 7 70%
蓝色 1 10%
绿色 10 红色 3 30%
绿色 5 50%
蓝色 2 20%
蓝色 8 红色 6 75%
绿色 2 25%
蓝色 0 0%

每个房间的目击次数都相当均匀,所以它并没有告诉我们哪里可能是伏击的好地方。玩家可以在地图上均匀地产生偏差,同样可能会出现在这三个房间中的任何一个房间。但是他们输入的 下一个 房间的数据可能很有用,可以帮助我们预测玩家在地图上的移动。

我们可以一眼看出绿色房间显然非常适合玩家 - 红色房间里的大多数人然后前往绿色房间,下次我们检查时,在绿色房间看到的50%的玩家仍然在那里。我们还可以看到蓝色房间是一个非常不受欢迎的目的地 - 人们很少从红色或绿色房间通过蓝色房间,似乎没有人在蓝色房间里逗留。

但数据告诉我们一些更具体的内容 - 它说当一个玩家在蓝色房间时,我们看到他们的下一个房间最有可能是红色房间,而不是绿色房间。尽管绿色房间总体上比红色房间更受欢迎,但如果玩家目前在蓝色房间,那么这种趋势会略微逆转。下一个状态(即他们选择前往的房间)显然取决于之前的状态(即他们目前所处的房间),因此这些数据可以让我们对他们的行为进行更好的预测,而不是我们独立计算观察结果。

我们可以使用过去状态的知识来预测未来状态,这种想法被称为马尔可夫模型 ,这样有准确可度量事件的样本(例如'什么房间是玩家')被称为马尔可夫链。由于它们表示连续状态之间发生变化的可能性,因此它们通常在视觉上表示为有限状态机,其概率与每个转换一起显示。以前我们使用状态机来表示代理所处的某种行为状态,但概念可以扩展到任何类型的状态,不管是否与角色有关。在这种情况下,玩家所在房间的状态可以这样表示:

MarkovChain1.png

这是表示不同状态转换的相对机率的简单方法,为AI提供预测下一状态的能力。但是我们也可以通过使用该系统来展望未来的两个或更多步骤。

如果在绿色房间中看到玩家,我们使用我们的数据估计他们仍然有50%的机会在绿色房间进行下一次观察。但那之后他们仍然有机会观察到什么呢?这不仅仅是他们留在绿色房间进行2次观察(50%* 50%= 25%)的机会,而且还有他们离开和回来的机会。这是一个新表,其中先前的值应用于3个观察,1个当前和2个假设的未来。

观察1 假设观察2 百分比机会 假设观察3 百分比机会 累积机会
绿色 红色 30% 红色 20% 6%
绿色 70% 21%
蓝色 10% 3%
绿色 50% 红色 30% 15%
绿色 50% 25%
蓝色 20% 10%
蓝色 20% 红色 75% 15%
绿色 25% 5%
蓝色 0% 0%
总: 100%

在这里,我们可以看到,经过两次观察后,在绿色房间看到玩家的机会可能是51% - 21%来经过红色房间而来,5%经过蓝色房间而来,25%的人一直在绿色房间。

该表只是一个视觉辅助 - 该过程只需要在每一步中乘以概率。这意味着你可以预测未来,这里有个重要的前提:我们假设下一次进入某个房间的机率完全取决于当前所在的房间。这就是我们所说的 Markov Property - 这个想法中未来的状态只取决于当前状态。虽然它允许我们使用像马尔可夫链这样的强大工具,但它通常只是一个近似值。玩家可以根据其他因素(例如他们的健康水平或他们拥有多少弹药)来改变他们所在的房间,并且由于我们不会将此信息作为我们州的一部分捕获,因此我们的预测将不那么准确。

N-Grams

我们的格斗游戏combo攻击示例怎么样?这是一种类似的情况,我们希望根据过去的状态预测未来状态(为了决定如何阻止或规避攻击),而不是查看单个状态或事件,我们希望查找 序列 构成组合动作的事件。

一种方法是将每个输入(例如 KickPunchBlock )存储在缓冲区中,并将整个缓冲区记录为事件。 因此,想象一个玩家反复按下 KickKickPunch 使用' SuperDeathFist '攻击,AI系统将所有输入存储在缓冲区中,并记住每一步使用的最后3个输入。

输入 目前输入序列 最后的输入
Kick Kick none
Punch Kick, Punch none
Kick Kick, Punch, Kick Kick, Punch, Kick
Kick Kick, Punch, Kick, Kick Punch, Kick, Kick
Punch Kick, Punch, Kick, Kick, Punch Kick, Kick, Punch
Block Kick, Punch, Kick, Kick, Punch, Block Kick, Punch, Block
Kick Kick, Punch, Kick, Kick, Punch, Block, Kick Punch, Block, Kick
Kick Kick, Punch, Kick, Kick, Punch, Block, Kick, Kick Block, Kick, Kick
Punch Kick, Punch, Kick, Kick, Punch, Block, Kick, Kick, Punch Kick, Kick, Punch

(粗体行是玩家发起SuperDeathFist攻击的时候。)

有可能看到玩家选择 踢的 所有时间,然后是过去的另一个 ,然后注意下一个输入总是 Punch 。这让AI代理可以预测如果玩家刚刚选择了Kick然后踢了Kick,他们可能会选择 Punch next,从而触发 SuperDeathFist。 这允许AI考虑选择抵消该操作的操作,例如阻止或规避操作。

这些事件序列称为N-gram ,其中N是存储的项目数。在前面的例子中,它是一个3-Gram,也称为三元组,这意味着前两个条目用于预测第三个条目。在5-Gram中,前4个条目有望预测第5 个,依此类推。

开发人员需要仔细选择N-gram的大小(有时称为“顺序”)。较低的数字需要较少的内存,因为可能的排列较少,但它们存储的历史较少,因此丢失了上下文。例如,2-Gram(有时称为'bigram')将有 KickKickKickPunch的 条目,但无法存储 KickKickPunch ,因此没有特定的意识到该组合。

另一方面,更高的数字需要更多的内存并且可能更难训练,因为你将有更多可能的排列,因此你可能永远不会看到相同的两次。例如,如果您有3个可能的 KickPunchBlock 输入并使用10-gram,那么您将有近60,000种不同的排列。

一个二元模型基本上是一个微不足道的马尔可夫链 - 每个“过去状态/当前状态”对都是一个二元组,你可以根据第一个来预测第二个状态。3-Gram和更大的N-gram也可以被认为是马尔可夫链,其中除了N-gram中的最后一项之外的所有项目一起形成第一状态而最后一项是第二状态。我们的格斗游戏示例代表了从 Kick然后Kick 状态转移到 Kick然后Punch 状态的机会。通过将输入历史的多个条目视为一个单元,我们基本上将输入序列转换为一个状态,这为我们提供了Markov属性 - 允许我们使用Markov链来预测下一个输入,从而猜测哪个组合移动即将到来。

数据表示 Knowledge representation

我们已经讨论了制定决策,制定计划和做出预测的几种方法,所有这些都是基于角色对世界状态的观察。但是我们如何有效地观察整个游戏世界呢?我们之前看到,我们对世界建模的方式会对如何导航产生重大影响,因此很容易想象这也适用于游戏AI的其他方面。我们如何以一种表现良好的方式收集和组织我们所需的所有信息(因此它可以经常更新并由许多角色使用)(让信息易于用于决策)?我们如何将纯粹的 数据 转化为 信息知识 ?这因游戏而异,但有一些广泛使用的常用方法。

标签

有时我们已经掌握了大量可用数据,我们所需要的只是分类和搜索数据的好方法。例如,游戏世界中可能存在大量物体,其中一些物体用作掩体可以避免射击。或许我们有一堆语音行,只在某些情况下适用,我们想要一种方法来快速知道哪个是哪个。显而易见的方法是附加一点信息用来检索,这些信息称为 标签

以掩体为例; 一个游戏世界可能有很多道具 - 板条箱,桶,草丛,铁丝网。其中一些适合作为掩体,例如板条箱和桶,其中一些不适用,例如草和铁丝网。因此,当您的角色执行“移至掩体”操作时,它需要搜索本地区域中的对象并确定哪些是候选对象。它通常不能只按名称搜索 - 你可能有“Crate_01”,“Crate_02”,最多 “Crate_27”适用于您的艺术家制作的各种箱子,而您不想在代码中搜索所有这些名称。你肯定不希望每次艺术家制作一个新的箱子或桶变化时都要在代码中添加额外的名称。相反,您可能会想到搜索包含单词“Crate”的任何名称,但有一天您的艺术家会添加一个“Broken_Crate”,其中有一个大洞并且不适合掩体。

因此,您所做的是创建“掩体”标签,并要求艺术家或设计师将该标签附加到任何适合作为掩体的项目上。一旦他们为你的所有桶和(完整的)板条箱执行此操作,您的AI例程只需搜索具有该标签的任何对象,并且它可以知道它是合适的。如果稍后重命名对象,此标记仍然有效,并且可以添加到将来的对象中而无需修改任何代码。

在代码中,标记通常只表示为字符串,但如果您知道所使用的所有标记,则可以将字符串转换为唯一数字以节省空间并加快搜索速度。有些引擎提供了内置的标记功能,例如Unity虚幻引擎4, 因此您所要做的就是决定您的标记集并在必要时使用它们。

智能对象

标签是一种将额外信息添加到角色中以帮助其理解可用选项的方式,因此诸如“找到附近提供掩体的所有地方”或“找到范围内的每个地方法师”的查询都可以以较低的开销有效执行。但有时候标签中没有足够的信息来满足我们的需求。

想象一下,一个中世纪的城市模拟器,在这里,冒险者可以自己四处游荡,训练,战斗,并在必要时休息。我们可以在城市周围放置训练场,并给他们一个“训练”标签,这样冒险者就可以轻松找到训练的地方。但是让我们想象一个是射箭场,另一个是巫师学校 - 我们想要在每种情况下展示不同的动画,因为它们代表了在“训练”的标签下完全不同的活动,而不是每个冒险家都会对两者都感兴趣。我们可能会决定向下钻取并拥有“弓箭射击训练”和“魔法训练”标签,并为每个标签分别设置训练程序,并在这些例程中内置特定的动画。这样可行。但想象一下,你的设计团队会说,“让我们有一个罗宾汉培训学校,它提供 射箭和剑术“!现在剑客进入,然后他们要求“甘道夫的法术和剑术学院”。您发现自己需要为每个位置支持多个标签,根据冒险者需要的培训方面等查找不同的动画。

另一种方法是将这些信息直接存储在对象中,以及它对播放器的影响,以便简单地告知AI 选项是什么,并根据代理的需要从中进行相应的选择。然后它可以移动到相关位置,执行对象指定的相关动画(或任何其他先决条件活动),并相应地获得奖励。

要执行的动画 用户的结果
射箭场 射击箭 +10射箭技巧
魔法学校 剑决斗 +10剑技巧
罗宾汉的学校 射击箭 +15射箭技巧
剑决斗 +8剑技巧
甘道夫的学院 剑决斗 +5剑技巧
铸法术 +10魔法技能

在上述4个位置附近的射手角色将被赋予这6个选项,其中4个是不相关的,因为角色既不是剑用户也不是魔术用户。通过匹配结果,在这种情况下,技能的提高,而不是名称或标签,我们可以轻松地用新的行为来扩展我们的世界。我们可以添加旅馆休息和食物。我们可以允许冒险者去图书馆阅读有关法术的内容,还可以了解高级射箭技术。

对象名称 要执行的动画 最终结果
酒馆 购买 -10饥饿
酒馆 睡觉 -50疲倦
图书馆 读书 +10施法技巧
图书馆 读书 +5射箭技巧

如果我们刚刚进行了“射箭训练”行为,即使我们将我们的图书馆标记为“弓箭射击训练”,我们仍然需要一个特殊情况来处理使用“阅读书”动画而不是通常的剑术动画。通过将这些关联转换为数据并将数据存储在世界中,为我们提供了更大的灵活性。

通过让对象或位置 - 如图书馆,旅馆或培训学校 - 告诉我们他们提供什么样的服务,以及角色必须做些什么才能获得它们,我们可以使用少量的动画和简单的描述。结果,以实现大量有趣的行为。这些对象可以代替被动地等待查询的对象,而是可以提供关于它们可以用于什么以及如何使用它们的大量信息。

响应曲线

通常情况下,状态可以用连续值来衡量 - 例如:

  • “健康百分比”通常从0(死)到100(完全健康)
  • “距离最近的敌人”的范围从0到某个任意正值

AI系统的某些方面可能需要别的连续值。例如,AI系统在决定是否逃离时,可能会将距离最近的敌人和角色的当前健康状况都考虑在内。

然而,系统不能仅仅添加两个状态值来创建某种“安全”级别,因为这两个单位不具有可比性 - 它会认为一个距离敌人200米的近乎死亡的角色,和距离敌人100米的满血角色,同样安全。虽然健康百分比值大致呈线性,但距离不是 - 距离敌人200米和距离190米之间的差异,远远小于距离10米和距离1英尺之间的差异。

理想情况下,我们需要将2维的度量转换为一维,以便直接进行比较。我们希望设计人员能够选择转换的方式,以便他们可以控制每个值的权重。响应曲线是一个可以做到这一点的工具。

响应曲线最简单的解释是它是一个沿X轴输入的图形,任意值如“距离最近的敌人”,沿Y轴输出,通常是从0.0到1.0的标准化值。通过图形曲线确定从输入到标准化输出的映射,设计者调整这些线以获得他们想要的行为。

对于我们的“安全”水平计算,我们可能决定将健康百分比保持为线性值 - 即,无论我们是受伤严重还是轻伤,10%的健康状况同样好 - 因此将其映射到0到1的范围是直截了当的:

safetyhealthvalues.png

到最近的敌人的距离有点不同,因为我们可能根本不关心超过一定距离的敌人 - 比方说50米 - 而且我们更关心近距离的差异而不是长距离。

在这里我们可以看到40或50米的敌人的“安全”输出非常相似:0.96对1.0。

safetydistancevalues.png

然而,15米外的敌人 - 大约0.5米 - 和5米外的敌人 - 之间的安全差异大约为0.2。这更好地反映了当敌人越来越近时适用的紧迫性。

由于这两个值都缩放到0到1范围,我们可以将整体安全值计算为两个输入值的平均值。健康状况为20%的角色和50米外的敌人可以获得0.6的安全评分。健康状况为75%的角色和距离仅5米的敌人可以获得0.47的安全评分。而一名身体严重受伤的角色,健康状况为10%,距离敌人只有5米,其安全分数仅为0.145。

要记住的一些事情:

  • 通常使用某种加权平均值将响应曲线的输出组合成最终值 - 这样可以更容易地重复使用相同的曲线来计算不同的值,在每种情况下使用不同的权重来反映不同的重要性。
  • 当输入值超出规定范围时 - 例如,在我们上面的示例中距离超过50米的敌人 - 通常将输入值钳位到最大值,因此计算起来就好像它们在该范围内一样。
  • 响应曲线的实现通常采用数学方程的形式,通常通过线性方程或简单多项式运行(可能是钳位的)输入。但是任何允许设计者创建和评估曲线的系统都可以满足要求 - 例如,Unity AnimationCurve 对象允许放置任意值,是否平滑值之间的界线,以及评估任何值沿着这条线。

黑板

通常我们发现自己处于这样一种情况,即角色的AI需要开始跟踪其在游戏过程中所获取的知识和信息,以便可以在未来的决策中使用。也许一个角色需要记住最后一个攻击它的角色是谁,因此它知道这应该是短期攻击的焦点。或者它可能想要注意它听到干扰后有多长时间,因此在一段时间后它可以停止调查并回到之前做的任何事情。通常,写入数据的系统与读取数据的系统完全分离,因此需要从角色程序轻松访问它,而不是直接内置到各种AI系统中。读取可能在写入后的某个时间发生,

在硬编码的AI系统中,通常是添加必要的变量。这些变量直接内联或以单独的结构或类的形式写入角色实例,以保存此信息。AI根据需要从该数据读取和写入。作为一种简单的方法这很好,但随着添加的信息越来越多而变得笨拙,并且每次都从新编译游戏。

更高级的方法可能是将此数据存储更改为自定义二进制内容 - 这就允许在更改数据结构的前提下添加新变量,从而增加变量的工作可以由配置文件和脚本完成,无需重新编译。如果每个角色只保留一个map,每个离散知识对应一个键值对,各种AI系统就可以协作添加信息并在需要时读取。

这些方法在人工智能中被称为“黑板”,想法是,每个参与者(比如,各种AI,如感知、寻路、决策)需要记录他们的内容时,都可以写在黑板上,同时可以阅读其他人写的黑板上的任何其他内容,以执行他们的任务。好比一个聚集在董事会周围的专家团队,每次写下他们与团队分享的有用的东西,读取同行之前的贡献的内容,直到达成一致的解决方案或计划。共享变量的硬编码列表有时被称为“静态黑板”(因为存储信息的插槽在运行时是固定的),相比之下,共享map通常被称为“动态黑板”,

在传统的AI中,重点通常是众多系统之间的协作以共同解决问题,但在游戏AI中,工作的系统相对较少。尽管如此,仍然可能会进行某种程度的合作。想象一下动作RPG中的以下内容:

  • “感知”系统定期扫描该区域并将以下条目记录到角色的黑板中:
    • “最近的敌人”:“Goblin#412”
    • “最近敌人的距离”:35.0
    • “最近的好友”:“战士#43”
    • “最近好友的距离”:55.4
    • “上次发生的骚乱”:下午12:45
  • 当关键事件发生时,战斗系统等系统可以在黑板上记录数据,例如:
    • “最后被攻击”:下午12:34

很多这样的数据可能看起来多余 - 毕竟,只要知道敌人是谁并查询他们的位置,就应该很容易在需要的时候获得距离最近的敌人的距离。但是,如果为了确定角色是否受到威胁而在一帧内进行多次检测,这就可能是一个缓慢的操作 - 特别是如果我们还需要重复查询以找出哪个敌人最接近。无论如何,“最后被打断”或“最后被攻击”的时间戳无法立即得出 - 需要记录这些事情发生的时间,而黑板是存放它的合理位置。

虚幻引擎4使用动态黑板系统提供给其行为树的数据。通过提供这个共享数据对象,设计人员可以很容易地根据蓝图(可视脚本)将新值写入黑板,并且行为树稍后可以帮助选择行为,而无需重新编译引擎。

Influence Maps(影响图)

游戏AI中的一个常见问题是确定代理应该尝试移动的确切位置。在射击游戏中,我们可能选择了一个动作,例如“移动到封面”,但是面对移动的敌人,我们如何确定封面的位置?同样,它对“逃离”究竟意味着什么 - 最安全的地方在哪里?或者在RTS游戏中,我们可能希望让我们的部队攻击对手防御中的弱点 - 什么是确定最弱点位置的便捷方式?

这些都可以被认为是地理查询,因为我们查询的是环境的形状和形式以及环境中实体的位置。我们的游戏很可能拥有所有数据,但理解它是棘手的。例如,如果我们想要找到敌人防御中的弱点,那么只要选择最弱的建筑物或防御工事的位置就不够好,如果它的两侧是两个强大的武器系统!我们需要一种方法来考虑当地的情况,以便更好地了解全貌。

Influence Maps是一种旨在完成此任务的数据结构。它代表了一个实体可能对其周围区域产生的“影响”,并通过结合多个实体的影响,呈现出对整个景观更加真实的观点。在实现方面,我们通过覆盖2D网格来表达游戏世界,并且在确定实体所在的网格方格之后,我们可以将他们的影响力分数 - 代表我们试图建模的游戏玩法的各种因素 - 应用于该方格和一些周围的实体。我们在同一网格中累积这些值以获得整体情况。然后我们可以通过各种方式查询网格,以了解世界,并对位置和目的地做出决策。

让我们以“对手防守中最弱点”为例。我们有一个让步兵进攻的防御墙,但是后面有3个弹射器 - 左边靠近2个,右边靠近1个。我们如何为这次袭击选择一个好位置?

首先,我们可以为每个弹射器分配射击范围内所有网格+1防御分数。在一个炮塔的影响地图上绘制这些分数如下所示:

AI Influence Maps 1.png

蓝色的盒子覆盖了我们可能会考虑攻击墙壁的所有方块。红色方块代表弹射器影响范围,在这种情况下意味着它的攻击范围,会对入侵单位造成威胁。

如果我们现在加入第二个弹射器的影响力:

AI Influence Maps 2.png

现在我们有一个较暗的区域,两个弹射器的影响重叠,在这些方格中分数+2。蓝色区域内的+2平方将是一个特别危险的地方!让我们加入最后一个弹射器的影响:

AI Influence Maps 3.png

[图标:CC-BY:https//game-icons.net/heavenly-dog/originals/defensive-wall.html ]

现在我们已经完整地指出了弹射器覆盖的区域。在我们的潜在攻击区,我们有一个对弹射器有+2影响的正方形,11个有+1的正方形,但是我们有2个正方形对弹射器有0个影响 - 这些是我们选择的攻击位置的主要候选者,我们可以攻击墙壁而不会被弹射器攻击。

这里影响图的好处是它将连续的一组几乎有无限的可能位置的问题空间,转换为一组我们可以快速推理的离散的粗略位置。

尽管如此,我们可以通过选择少量的候选攻击位置来获得好处 - 为什么我们会选择使用影响图而不是手动检查每个位置的每个炮塔的距离?

首先,影响图的计算非常廉价。一旦用影响分数写出地图,除非其中的实体移动,否则根本不需要改变。这意味着您不必一直执行距离计算或继续迭代每个可能的单元 - 我们将该信息“烘焙”到地图中一次,可以根据需要多次查询。

其次,我们可以叠加并组合多个影响图以执行更复杂的查询。例如,为了找到一个安全的地方逃离,我们可能会用我们的敌人的影响图,减去我们的友方的地图 - 因此具有高负分的网格将被认为是安全的。

AI Influence Maps 4.png

更多红色意味着更危险,更多绿色意味着更安全。它们重叠的区域可以完全或部分抵消,以反映冲突的影响区域。

最后,通过在世界中绘制它们,它们很容易可视化。对于需要根据可见属性调整AI的设计人员而言,这可以提供有价值的帮助,并且可以实时观察以了解AI为何做出决策。

结论

希望这能让您全面了解游戏AI中使用的一些最常用的工具和方法,以及它们有用的情况。 一些 不太常用但可能同样有效 的其它技术 尚未在此涵盖,比如:

  • 用于优化任务的算法,包括hill-climbing, gradient descent, and genetic algorithms
  • 对抗性搜索/规划算法,如minimax和alpha-beta修剪
  • 分类技术,如感知器,神经网络和支持向量机
  • 处理代理感知和记忆的系统
  • AI架构方法,例如混合系统,包含架构以及其他AI分层系统
  • 动作规划和动作匹配等动画工具
  • 性能考虑因素,例如:level of detail, anytime algorithms, and timeslicing

要详细了解这些主题以及上面详细介绍的主题,以下来源可能会有用。

当然,在GameDev.net上你有人工智能文章和教程参考以及人工智能论坛

许多最好的材料都可以在书中找到,包括以下内容:

除此之外,还有几本关于通用游戏人工智能的好书,由行业专业人士撰写,很难将其中任何一本高于其他人 - 阅读评论并选择一个听起来适合你的评论。

你喜欢这个教程吗?您可能也喜欢GameDev.net的初学者指南系列中的其他教程:

你有兴趣为GameDev.net写作吗?联系我们

[Wayback Machine Archive]

HierarchicalStateMachine2.png

StateMachine1.png