人工智能实战:智能NPC技术详解
人工智能实战:智能NPC技术详解
智能NPC系统是游戏开发中的一个重要技术,它可以让NPC的行为更加自然和多样化,提升玩家的游戏体验。本文将详细介绍智能NPC系统的关键技术,包括底层驱动、事件触发和强化学习生成行程表等,帮助读者深入了解这一技术的实现原理和应用场景。
背景
在许多游戏中,NPC(非玩家角色)一直是不可缺少的一部分。随着近年来大世界题材游戏的火热,氛围型NPC越来越引人关注。策划可以通过这些氛围型NPC来讲述游戏世界观,玩家也可以通过观察NPC的行为来获得更多的沉浸感。但是,如何生产这些氛围型NPC一直是个问题。
一般来说,NPC表现的好坏取决于策划的配置是否完备。只有将玩家能想到的情形都配置出来,玩家的体验才可以保证。但这样的工作量对于策划和程序都是灾难级的。因此,经过一段时间的研发,我们产出了一个智能NPC系统,策划可以通过简单的配置就能大量生成比较高质量的氛围型NPC。
关键技术
(1)底层驱动
智能NPC系统里的NPC是按照行程表进行驱动的,他们都会日出而作日落而息。如上图所示,是一个NPC的行程表示意图,到了行程表上的时间节点就会执行下一个动作。
在代码层面上,不同动作的具体实现是通过模版来实现的。比如摆摊的行为模式是一个模版,巡逻的行为模式是另一个模版。如果NPC的行程表从摆摊变为巡逻,那么只需要切换其装载的模版即可,可以减少功能的重复开发。这些模版的底层实现是通过腾讯的开源行为树编辑器behaviac实现的。这种图形化的编辑器比较适合策划和程序的合作。
(2)事件触发
除了行程表驱动NPC之外,我们还想让NPC具有对环境的感知能力。比如可以和路过的玩家进行打招呼,或者像上图一样可以被跳舞的人所吸引,自发地围观跳舞。
这个功能需要向周边的人广播跳舞的事件,所以自然需要AOI(Area of Interest)系统。在智能NPC系统中,我们维护了一个带有哨兵节点的十字链表。当有节点在Update时跨越了某个NPC的哨兵节点,则为进入或离开了这个NPC的AOI范围。以下是Update时的代码:
public void UpdateAOI(int id, Vector3 newPos)
{
OrthogonalNode main = nodeMap[id];
Vector3 offset = newPos - main.pos;
main.pos = newPos;
main.xSentryHead.pos += offset;
main.xSentryTail.pos += offset;
main.ySentryHead.pos += offset;
main.ySentryTail.pos += offset;
// 根据Offset的大小改变移动节点的顺序
if (offset.x < 0)
{
MoveToCorrect(main.xSentryHead, _handleWhenMoveSentryNode, MoveType.E_MT_OnlyX);
MoveToCorrect(main, _handleWhenMoveNode, MoveType.E_MT_OnlyX);
MoveToCorrect(main.xSentryTail, _handleWhenMoveSentryNode, MoveType.E_MT_OnlyX);
}
else if (offset.x > 0)
{
MoveToCorrect(main.xSentryTail, _handleWhenMoveSentryNode, MoveType.E_MT_OnlyX);
MoveToCorrect(main, _handleWhenMoveNode, MoveType.E_MT_OnlyX);
MoveToCorrect(main.xSentryHead, _handleWhenMoveSentryNode, MoveType.E_MT_OnlyX);
}
if (offset.z < 0)
{
MoveToCorrect(main.ySentryHead, _handleWhenMoveSentryNode, MoveType.E_MT_OnlyY);
MoveToCorrect(main, _handleWhenMoveNode, MoveType.E_MT_OnlyY);
MoveToCorrect(main.ySentryTail, _handleWhenMoveSentryNode, MoveType.E_MT_OnlyY);
}
else if (offset.z > 0)
{
MoveToCorrect(main.ySentryTail, _handleWhenMoveSentryNode, MoveType.E_MT_OnlyY);
MoveToCorrect(main, _handleWhenMoveNode, MoveType.E_MT_OnlyY);
MoveToCorrect(main.ySentryHead, _handleWhenMoveSentryNode, MoveType.E_MT_OnlyY);
}
}
通过十字链表可以以一个比较小的开销实现AOI功能。解决了AOI问题后还会有以下问题:在什么地方进行围观事件的广播,在跳舞的模版里进行广播的话会不会造成功能上的不合理耦合;怎么确保来围观的人能够大致站成一个圆圈;行程表中的大部分行程为了模板化都可以拆解为什么时间去什么地方干什么,围观跳舞这个动作的地点怎么确定。
为了解决以上问题,我们设计了一个基于地点的事件触发系统。事先设定好跳舞的舞台和其周围均匀分布的围观跳舞的地点。当有NPC来舞台上跳舞时,舞台上绑定的事件被触发,向周围的NPC广播来围观的信号,目标是设定好的围观地点。如果收到广播信号的NPC愿意前来,就会走到围观点播放鼓掌动画。通过这样的设计,在代码层面将跳舞、广播事件、围观三个动作进行了解耦,方便后续的代码维护和策划的灵活配置。
(3)强化学习生成行程表
设计实现好模版、事件触发等基础机制后,我们来介绍智能NPC被称为智能的核心点,那就是由借助强化学习的AI技术,让NPC不通过策划配置,而是自行决定自己的行程表。
强化学习适合解决连续决策问题,看似和生成行程表毫不相关,但是我们在设计上对其进行了一些转化。为每个NPC设置初始属性和目标属性,再为每个可执行的行程设定好其对NPC的影响,最后设计一个模拟的游戏环境让强化学习模型去学习这些行程。最后强化学习模型就可以根据NPC当前的属性和目标的属性为其设计出最适合的行程表,通过这种设计将行程表的问题转化为了连续决策问题。
从表现上来说,如果一个NPC开始很贫穷,但是目标是成为一个很富有的人,那么在强化学习的影响下他每天的行程就会更倾向于摆摊而不是散步游玩。
总结
总体上来说,智能NPC系统将策划配置NPC的工作模式从精细地设定每一个NPC的行为,转变为了设定NPC世界的规则。在这种规则下,只需要简单的配置目标属性和当前属性就能快速产出大批量生动的氛围型NPC。