蜘蛛侠主机游戏:高度定制化的技术方案

蜘蛛侠主机游戏:高度定制化的技术方案

本篇文章是根据2019 GDC Talk的“Marvel's Spider-Man: A Technical Postmortem”[1]基础上总结和翻译而成的,对原视频中部分过于细节的内容进行了删减,感兴趣的同学可以在文末找到视频原链接进行观看。本文将介绍Insomniac Games的开发者们在游戏《蜘蛛侠》中,面临特定游戏需求带来的特定问题时,采取的各类高度定制化的技术解决方案。

《蜘蛛侠》(Marvel's Spider Man)是由Insomniac Games开发并由索尼互动娱乐发行于PlayStation 4平台的动作冒险游戏,同时也是Insomniac获得授权后基于漫威漫画超级英雄蜘蛛侠制作的作品。游戏包含着全新的蜘蛛侠故事,且与现有的漫画、电子游戏和电影没有任何关联,同时也会展现一个年龄更大且更有经验的蜘蛛侠。作品已于2018年9月7日在全球发售。

本作以纽约为原型,实现了一个拥有庞大地图的开放世界游戏。庞大的地图给开发者们带来了渲染、存储等多方面的挑战;蜘蛛侠在城市中飞檐走壁的高速移动,也给开放世界的动态加载带来了前所未有的难题。

目录

串流(Streamming)

大型开放世界游戏,通常都采用串流的方式,将地图数据进行动态地异步加载。整个游戏地图会被分为一个一个小块,当角色靠近新的小块时,就只需要读取这个小块即可。常用的几种划分方法如下图,这些划分方式可以保证整个地图能够无线的拼凑和延申。

对城市进行网格化划分的方法

本作采取的方法是第一种,矩形划分,整个城市被分为一个一个矩形块进行单独的存储及加载。并出于简化计算量的考虑,在实际进行相交判断时,实际上是使用的圆形的范围,如下图所示。

蜘蛛侠中采取的范围判定方法

在玩家进行移动时,每移动一个区块,就会需要相应地读入附近的5个新区块。

地图区块加载策略

然而,本作和其他开放世界游戏的一个差别,在于主角蜘蛛侠,可以通过吐丝的方式在城市中以极快的速度进行移动。如此快的移动速度,意味着需要在更短的时间内加载更多的区块,IO带宽的压力成倍增加。

所以首先的一个优化思路,就是减少读取区块的数量。考虑到玩家大部分时间都是进行近似的直线运动,所以只需要加载正前方的三块区块就行了,这样大大减少了带宽消耗。

优化后的地图区块加载策略

有人可能会想了,那玩家转弯怎么办?幸运的是,在本游戏中,蜘蛛侠转弯大概需要花将近1s的时间,这完全足够异步加载系统重新进行规划和读取了。

除此之外,另外一个优化策略是Delayed Loading,对区块中部分内容进行延迟加载。考虑到蜘蛛侠在进行快速移动时,镜头模糊会使周围的视线都很模糊,这个时候附近的模型可以直接使用Texture中Mipmap的最模糊层级即可,其他层级的贴图则可以暂时延后。除此之外,当蜘蛛侠在空中快速移动时,街边小店中的各种模型也都是看不见的,所以也可以一并进行Delayed Loading。这样的方法省下了相当大的带宽。

运动模糊效果

文件空间占用

对于很多PS平台游戏来说,Duplication是一种比较常用的一个IO性能优化方法。因为Play Station使用的是机械硬盘,机械硬盘在进行连续读取的时候有很好的性能,但当需要进行seek(寻址)的时候就会浪费很多时间。所以,游戏开发者通常会将同一份数据在磁盘中进行冗余存储,如果A地图区块和B地图区块都需要用到模型X,则他们会在自己的区块上各存储一份,这样一个区块的内容可以在一次连续读取中完成。

然而,因为纽约地图实在是太过巨大,在游戏还未完成的阶段,游戏的预估占用空间就已经超过了一张蓝光碟的存储容量。这一方面会增加发行成本,另外一方面对于游戏玩家来说,在多张光碟间切换也是十分糟糕的体验。

优化前的游戏空间占用

所以,我们需要重新审视Duplication。Duplication的初衷是什么?空间换时间。这也是程序开发中一个常见的优化思路。然而对于本游戏而言,因为使用了高度定制化的自研引擎,本身CPU性能是十分充裕的,而空间方面却是十分吃紧。所以,我们需要避免之前那种Naive的Duplication(即无论任何情况都Duplicate),而改为更加聪明的Duplication策略。

具体而言,就是不再Duplicate特定类型的文件。首先是可以进行Delayed Loading的文件类型,因为这类文件本身没有很高的及时性要求,例如Texture mipmap的精细层级和音效等。除此之外,文件体积大于4MB的,冗余次数大于400次的都不再进行冗余。

更聪明的Duplication策略

文件空间占用的另外一个优化方法,更加是“走火入魔”:本作在传统的模型存储格式上进行了修改。传统的模型存储格式中有很大一部分空间,存储的是每个三角面片对应的顶点index。而在本作中,这些顶点index被修改为与上一位置之间的差值。例如原本存储的是74,75,76,在修改后就成为了74,+1,+1。这样做的一方面原因,是因为三角面片中的顶点通常都是相连的,这使得如果我们存储差值的话,其中会有更多的重复数值;另一方面原因,就是本作使用了LZ4格式对文件进行压缩,而文件中的数据重复越多,就能取得更高的压缩率。最后经过计算,通过这样的压缩,模型文件的空间占用减少了多达20%。

魔改模型文件格式

大规模渲染

在纽约的帝国大厦天台,玩家可以纵览整个纽约城。如此庞大的城市,也带来了前所未有的渲染压力。

帝国大厦的风景

为了渲染如此宏大的场景,使用的第一个优化技巧,称为Imposter(假冒者)。Imposter是一些“轻量级”的场景物体,应用于视线远端的各类低精度建筑。他们的轻量,一方面在于对数据结构进行了简化,只存储必要的一些数据(Transform,模型等),另一方面则是使用更加简化的渲染管线(没有uv,没有法线,主要都靠离线烘培)。

另外一类物体称为Hibernates,主要用于建筑顶端的各类装饰品,例如太阳能板、烟囱等,这些小东西看上去并不起眼,但是对于提升城市的真实感而言却至关重要。

隐藏屋顶装饰物效果图

显示屋顶装饰物效果图

这些装饰性物品长期性的处于玩家视线之中。然而糟糕的是,整个城市中有60万个这类物体,每个物体的原始空间占用为384bytes,这意味着光是存储这些物体的信息就会占用600k*384bytes=230.4MB的内存。

这也是使用Hibernates的原因,和Imposter类似,Hibernates也对数据结构进行了精简,只存储极少的必要信息。例如位置信息,通过存储到地区区块中心的偏移量,可以使用一个16位的变量即可进行存储。在优化后,每个Hibernate只占用40 bytes,全体hibernates的内存占用也从230.4MB优化到了24MB。在角色足够靠近这些hibernates的时候,他们又会动态切换为正常的Game Object,以实现动画、玩家交互等功能。

Hibernates模型

Lighting(光照)

对于真实感渲染的游戏而言,一个必不可少的元素是环境探测球(Environment Probe),例如水面、金属等材质的镜面反射效果就离不开它。

环境探测球

环境探测球的生成并不复杂,只需要在探测球位置对上下左右前后六个方向进行快照即可获得。出于实时性能的考量,通常的做法是进行离线烘培并存储在磁盘中。但是,对于纽约如此庞大的城市来说,光是环境探测球就会占据8GB的空间,这是很难承受的。出于和之前Duplication部分的同样想法(CPU算力充足,而空间占用紧缺),本作采用了实时渲染的方法。当玩家进入新的区域后,如果当前有充裕的计算资源,则会异步的生成附近的环境探测球,并且为了减少重复计算量,会对生成的环境探测球进行适当的缓存。这样的做法完全是0磁盘占用。

环境探测球的生成方法

Pedestrian(行人)

只要是游戏中会动的东西,总会有玩家想去和它进行交互。

为了增加游戏的真实感及沉浸感,开发者们决定给行人们加上更加交互性的AI逻辑。然而,纽约城市中,来来往往的行人数量繁多,如果所有行人都使用同样复杂的AI逻辑,则会给游戏带来巨大的性能压力。

因此,AI逻辑部分也使用了LOD(Level of Detail)的优化思路。在游戏中,行人默认使用十分轻量级的AI逻辑。每隔30秒,玩家附近的一个行人会被切换为完全体AI(Full AI Bot),从默认的几类交互事件中随机选取一类并执行。默认的交互事件包括和玩家进行动作交互(如合照、签名等),向玩家报案(在本作中,玩家需要像警察一样到处去逞凶除恶),指引玩家前往附近的重要区域等等。当行人离玩家很远(例如只占据几个像素左右时),或者被遮挡的情况下,行人的动画、AI全都会暂停,只在原地进行左右的随机平移,给玩家带来他们仍然在移动的错觉。

行人AI动态调整策略

Photo Mode(拍照模式)

起初整个项目组只是打算将拍照模式做一个很简单的版本,这样就足以他们对外宣传“我们有拍照模式!”了。但随着用户反馈的收集,开发者们发现拍照模式的意义远比他们想象中的更大。玩家很享受在纽约城中的各个角落自拍、拍景色、甚至是拍遇到的BUG,并在各大社交平台上分享,这给游戏带来了相当多的曝光度。因此,开发者对拍照模式投入了额外的精力,加入了滤镜、贴花等功能,更好的满足玩家的拍照需求。

Photo Mode - 蜘蛛侠的自拍

Photo Mode - 玩家的分享

总结

在本次演讲中,演讲者Elan Ruskin总结出了四个技术实现的信条(Moral):

简单的实现就是好的实现(Simple is Good)。简单的实现有更低的开发成本和很高的可扩展度,在进行技术实现时,如果简单的实现方法可行,就不要一味去追求各类天花乱坠的高端方法。

因地制宜的采取技术方案(Fit Tech To Context)。本作中相当多的优化方法,都是根据游戏的具体情况,所采取的极度定制化的优化思路。并且从这些优化思路中也可以看出来,为什么极具创新性和开创性的游戏通常都会采用自研引擎。例如模型格式的修改、多级别的Game Object、以及Mipmap的分级分离存储,这些修改在没有游戏引擎源码并高度熟悉游戏引擎架构的情况下都是很难实现的。自研引擎的核心优势,就是在于可以根据自己游戏的需求,无限制的对引擎中的各个功能进行魔改。

重新进行空间和速度间的权衡(Revisit Speed-Space Tradeoffs)。传统游戏引擎中,有相当多的功能是以空间换时间的,例如各类预渲染和预计算。但随着游戏规模的不断扩大以及软硬件优化技术的提升,很多时候需要重新进行权衡,根据自身需求做出修改。

做游戏的首要目标是开心(If There's No Fun Then What is Even The Point)。做游戏其实就是做玩具,而玩具的首要任务就是让用户感受到快乐。一个无法带来快乐的游戏,还有什么意义呢?

参考

[1] Marvel's Spider-Man: A Technical Postmortem - Youtube. https://www.youtube.com/watch?v=KDhKyIZd3O8Hi

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×