一些游戏AI设计制作时候的思维方式和技巧,主要来自GameAIPro的这篇文章
游戏AI架构的常用技巧
M*N复杂度的解法:分治
游戏AI开发的一个核心难点就是应对逐渐上升的复杂度。
当新的环境,或者新的行为添加到现有体系,其复杂度多为M*N乘法系数上升。
举例: 当现有移动,防御,技能A,技能B,4种行为后增加一种技能C 由于技能B可以衔接在任意一种行为后(当然,也可能是前),将会增加4种需要思考的情况
而使用分治法,则可以有效降低这种情况下扩张的新组合选项个数,使其变为N的常数级。
举例: 将移动,防御,划分为休闲动作 将技能A,技能B划分为攻击行为,2种行为大类后增加一种技能C 则只需要增加2种需要思考的情况
同样的思路也出现在状态机与分层状态机上:状态机互相的连线在状态过多时无法维护,也不能很好的支持复用,于是有了分层状态机。
捡完芝麻不丢西瓜:行为栈
栈是一种常见的数据结构,其核心特征是先进后出(类似的数据结构队列,是先进先出)。
利用先进后出的特性,我们可以为AI引入上下文的时间维度概念。
举例: 怪物在执行【靠近】->【攻击】的逻辑 【靠近】过程中发现一个攻击利器:棒球棒,可以触发【捡武器】行为
如果没有历史信息:可能会重新进行决策来判断当前行为,这可能会带来行为的更改!之前要攻击,现在可能就是移动发呆了。
如果有历史信息(以栈结构为例):则可以继续上一次没有执行完的动作(其中捡完武器,是否重新决策是可选环节)
本质上是上下文知识的记忆,只是运用栈的结构,可以自然的实现行为与历史相关这一目的。
多维度复杂条件的决策:评估函数
我们迟早会遇到多种条件复合的决策情形,这会相当复杂而且容易出错。
举例: 条件:自身血量,目标血量,自身与目标距离 行为:攻击,逃跑,……
方便理解,我们列出一张枚举表格:
自身血量 | 目标血量 | 距离 | 行为 |
---|---|---|---|
血量健康 | 血量健康 | 近 | 攻击 |
血量健康 | 血量健康 | 远 | 攻击 |
血量健康 | 血量不健康 | 近 | 攻击 |
血量健康 | 血量不健康 | 远 | 攻击 |
血量不健康 | 血量健康 | 近 | 逃跑 |
血量不健康 | 血量健康 | 远 | 逃跑 |
血量不健康 | 血量不健康 | 近 | 攻击 |
血量不健康 | 血量不健康 | 远 | 逃跑 |
根据怪物血量的性格(胆小怕死或者无畏自杀),擅长发起攻击的距离,等等要素,我们可以一一决策各种情形下的行为。多样化的行为!这正是游戏AI的魅力。
但是多数情况下,我们是没能力使用枚举维护复杂复合情况下的决策的(参考第一部分使用分治降低复杂度),也或者说:这是性价比比较低的方式。
本质上,我们是建立复合参数到固定行为的一种映射,所以我们可以使用函数的思想。
将每一个条件(离散值或者连续值)进行值的映射,并为其赋予一个乘法缩放系数,再通过值的求和来考虑所有条件的影响。
这样,我们就可以通过最终值来决策。避免决策调试与配置的复杂度。
通过知识引入智能:黑板变量
什么是智能?一个简单的解释是能够运用历史经验。也即储存知识。
我们可以使用变量来记录象征着知识的值,这不仅可以方便我们在运行时记录知识。
举例:
- 记录本次释放指定行为的次数,控制释放总数
- 记录环境信息或者对方行为信息,采取对应的行为
也可以方便灵活使用变量来提高计算与配置的效率
举例:
- 缓存复合结果到变量,而不在每个使用到的地方复合求值
- 传递变量让同一个节点产生差异性的行为,以此做到更好的抽象与复用
适合的人干适合的事:模块化
好的关卡是模块化的,好的AI也是,这不仅是第一部分说到的分治的思想。也包括:
将复杂度下放到其他逻辑模块。
举例:
- 复杂的转向、动作组合跳转,直接使用定制动作配置
- 寻路放到单独的寻路模块、镜头放入镜头模块,AI只作为内容的使用方
将知识储存到场景中。
举例: 场景中包含可交互物与交互方法,这样就把交互逻辑与逻辑主题分开,方便组合、更新。
总结:灵活使用解决问题
AI有着多种技术架构与架构对应擅长的逻辑表达,有些时候面临的是工具的表达能力的问题。但是更多时候面对的是复杂情况的逻辑实现问题,灵活使用上述思维方式和技巧,能解决对应的复杂问题。