GameRes游资网

 找回密码
 立即注册

Sunset Overdrive 大地图管理:以最小的代价实现新功能

发布者: 小篱 | 发布时间: 2018-11-29 13:50| 评论数: 0

游戏程序
平台类型:  
程序设计: 设计思想/框架 算法逻辑/智能AI 网络通讯 输入控制 
编程语言:  
引擎/SDK:  
v2-2ee5112e9482cc2ecdac0510f598dcd7_1200x500.jpg

文/刘源

image001.jpg

Sunset Overdrive为Insomniac公司的首个开放大世界作品。主角在世界上快速漫游射击,大部分情况下没有加载界面。引擎主要基于之前的瑞奇与叮当、抵抗和导火索(Fuse)这三部作品的技术,主要是平台动作冒险,以及线性流程射击游戏。

新作的各方面规模达到了原来的5~10倍,但是,开发者人数只多了大概10%。

image003.jpg

本演讲的一个母题就是,如何以最小的代价,在现有引擎基础上实现新的功能。

视频https://www.gdcvault.com/play/1022268/Streaming-in-Sunset-Overdrive-s

PPT https://www.gdcvault.com/play/1022269/Streaming-in-Sunset-Overdrive-s

在讲解Sunset Overdrive的方案之前,演讲者先回顾了其前作的技术方案。

瑞奇与叮当的方案:完整加载静态关卡

image004.jpg

每个星球是一个关卡(level),资源一次性读取。关卡切换时,彻底删除上一关并进行加载。为了遮蔽这个加载过程,这过程会播放瑞奇的太空船飞行动画。

为了方便多人编辑,关卡划分为了多个zone,例如地形/光照/装饰/脚本,每个zone为一个独立编辑单元,可以想象成photoshop中的图层(按:本讲座中,zone和图层相同概念)。不同人可以同时编辑关卡的不同zone。

image005.jpg

关卡的内存管理非常直接。由于zone是静态的,所以完全可以预先设计好zone的内存占用。将资源存为光盘镜像,加载时直接memcopy到内存。其中actor所在的堆内存空间也直接预留好了(按:因为actor的数量和类型也是确定的,所以可以直接预分配。这对内存预算来说是多么友好啊)。

Fuse的方案:气闸室(air lock)

Fuse在连续加载世界方面更进了一步。所谓气闸室方案,可以想象人进出太空船的隔离减压加压舱。

按照这个模式,在Fuse中,关卡由走廊或电梯连接,并且在这些封闭区域进行关卡切换。

image006.jpg
玩家在路的尽头破开箱子,发现一个通风管道,走向下一个区域

image007.jpg

上图就是气闸室的实例。地图X和Y共享zone B,玩家从A先撞到B左侧的触发体,脚本加载新地图C,再撞到右侧的触发体,脚本卸载旧地图A。

SunsetOverdrive的方案

image009.jpg
左下:六边形的遮挡。右下:矩形布局的长视距

我们来看看Sunset Overdrive如何实现开放世界。

首先,该游戏地图使用六边形地块。若是普通矩形地块,很容易产生直线路,对于渲染来说非常灾难。而六边形地块则天然避免了这一点,路线都是120度的拐角,容易遮挡视线。

image010.jpg

世界只有一个level,由平铺的六边形地格组成,每个六边形地块含有多个zone图层。

image012.jpg

地格210的4个zone图层,文件名分别为:hex210_ground,hex210_art_1,hex210_art_4,hex201_gameplay地块210加载效果

image013.jpg

上图展示了地块210多个zone图层中的4个,以及其所有zone重叠加载结果。zone的内容并不需要严格限制在六边形内,美术会让部分模型故意超出六边形边界,这样不但制作方便,同时也能有效隐藏一些接缝。

其中,gamplay zone(图中4个zone的右下)用于布置玩法内容,除了怪物之外,还会放置绘制为蓝色方块的抽象物件,包括触发器、标记点等。

全局奇遇系统

奇遇系统依赖于刚才提到的标记点。用标记点描述当前位置的环境约束,在全局任务触发时,会根据匹配规则生成奇遇,例如下图这样的游戏对象组。

image014.jpg

流式加载


玩家会加载自己所在地块周围一圈的7地块,移动中最多为11个。实际的实现中,地块不是六边形,而是半径60米的圆,表示地块的内容范围。所以,开发者只是简单地将前作Fuse的触发器脚本,改写为了按距离加载地块,就在Fuse的基础上实现了新游戏的大世界加载。

地块是2d的,没有做高度方向上的动态分割。因为,在水平方向上玩家的移动速度有限,可以做到一边移动一边加载,但是在垂直方向上却很难控制下落速度,下落可以非常快。

地形LOD和内存

每个地格都有一个合并的低精度模型,由Simplygon生成。整个世界的所有低精度模型永远全部加载,占500m内存,为机器总内存的1/10,其中贴图400m,模型40m。同一地块,高低精度切换时会使用淡入淡出动画。

image016.jpg

如果高模的建筑损毁,低模不会有相应变化。但是没有任何玩家察觉。

游戏有一个后台加载器,按优先级持续加载。为了节省内存,地块内容中,所有贴图的最高一级LOD是不能常驻内存的,会单独拆分出来,动态根据距离换入换出。

image018.jpg

AI LOD

新一代平台虽然内存提高了10倍,但是CPU并没有变快10倍。所以,游戏中做了AI的LOD,根据距离增加逐步关闭各种行为。

任务的实现:影子zone

每个任务都包含全局zone的数据,同时还拥有专门的影子zone图层,如下图黄色区域。任务激活时,对应地块会加载对应影子zone。影子zone中,不但可以放置建筑,还可以通过脚本关闭原有地块的部分内容。

image019.jpg
蓝色为已加载的地块,黄色为单个任务的影子区域

image020.jpg
左上:地块118,右上:118上任务OFD_DOG_LURE的影子区域,左下:还是地块118,,右下:叠加了最终Boss的影子区域

不过该游戏有一个内存问题未彻底解决,就是理论上,玩家可以接取全游戏支线任务,并且一个都不完成。这就需要保证所有任务全开时,在内存压力最大的区域上内存足够。QA经常因此报告偶现的out of memory崩溃。从讲座看,开发人员虽然认真地解决了每个问题,但是没有提供彻底的解决方案。

传送

本游戏尽量的避免了传送。为了去掉传送,本作的任务需要遵循特殊的设计准则。也就是,让任务正好在确定的地块结束,接着播放预设的过场动画,随后下个任务也在同一地块开启。

做不到的情况下,只能传送。视频38:57为一个满世界追杀杀龙的任务,任务完成时将玩家和龙传送到一个特效小房间,播放一段7秒的漫画风格的搏斗动画,遮掩加载,龙倒下的时候就到了指定的结束地块。

image022.jpg

寻路和碰撞

寻路网格的动态加载比较棘手:动态缝合太困难了。最终寻路网格保持全部加载,它占1%的内存。

未加载的地块是没有碰撞的。为了避免AI自己走进去摔死,会动态关闭未加载地块的寻路。另外,很特别的是,当远离玩家的地块卸载后,地块上所有NPC会直接下落到死亡平面摔死。作者很得意没有任何玩家发现/抱怨这个设计,问“Did you notice?”。

image024.jpg
火车任务的全局zone,包含铁轨和部分地板碰撞

加载系统并没有严格保证碰撞体和NPC的创建顺序。有一个任务以极限速度跟踪火车,沿途碰撞体上的脚本依次创建扑向火车的怪物。开发人员发现,路上的怪物有概率直接从地板掉下去。最终,该任务的全局zone中直接放置了整个铁路,加上额外的保护性地板碰撞体。

文件管理:打包版

image026.jpg

为了最高IO性能,每个zone包含了其依赖的全部资源,包括所有模型、贴图。所以各zone间有大量冗余资源,如上图。这个决策是专门为了机械存储而优化的,因为:

  • 硬盘和光盘有寻道时间
  • 加载并丢弃资源远快于跳转


有一些例外,并不在成块加载的资源集中,包括:

  • 贴图的最高级Mipmap,前面我们提到过,他们会换入换出
  • 背景音乐音轨和对话音轨,使用wwsie流式播放
  • 个性化装扮,这个无法预测,纯动态加载


此外,他们全局的加载器,并会做简单的寻道距离排序。

文件管理:研发时的LunaServer

为了提高开发人员的工作效率,关卡的修改是实时推送到各个开发客户端的。

所有文件全在网页服务器上,称为LunaServer。客户端以http格式通信、上传下载,本地有文件缓存。开发人员提交的关卡和资源修改直接推送服务器,服务器会在线通知各客户端更改,在游戏机中不需要重新启动,就能热更新任何资源。

如果修改导致大量资源重新编译(例如修改某些脚本),只需要修改者一个人提交重新编译的资源,其他人会自动从cache下载。

加载性能和分摊

按玩家的平均移动速度,每2.6秒就需要加载一个地块的数据,包含多至2000个实体。通过Profiler发现瓶颈不在IO,而在区域资源的初始化。

image028.jpg

这时就需要分摊创建实体了。绝大多数实体是固定的,可以分摊到多帧创建;少数实体相互依赖,他们或者由脚本/AI相互引用,或者站在其他实体上面,这类实体保持原策略直接创建。

完成创建的分摊之后,帧率就可以保持在30帧了。

很容易从Profiler观察到,卸载也同样性能不佳,但是这里就不再做复杂化。作者强调:在研发中需要注意“不是所有事都应当做”。

内存碎片

由于从来不重置内存,游戏有严重的内存碎片问题。

研发者实现了一个碎片查看器,如下图。每个圆点是一个完整的内存页,半径代表着利用率。如果出现大量的小圆点,说明那些页碎片过多。在充斥内存碎片的情况下,虽然总的空闲内存非常多,但是一个大的连续内存申请就会导致游戏崩溃。

通过记录调用栈,可以分析碎片的产生原因。但是从讲座来看,他们没有找到彻底解决问题的分配器算法。

image030.jpg

开发者经验:够用就行

这个讲座的母题是,用最小的代价实现所需的功能。作者分享道:

  • 绝对不应当仅仅因为你能做,就将你能实现的最好的技术加入项目。人力资源非常昂贵。
  • 应当总是抵制改写旧技术,尽量保持简单算法。例如:


o为什么不实现分帧卸载?

o为什么不做navimash的动态加载?

o为什么低模场景总是常驻内存?

o为什么没有提供场景的多级LOD?

  • 有些更聪明的算法甚至会损害创造力


o例如:理论上可以指定多地区共享的资源模板,比如定义一批地块为日本城,然后让美术在其中一律使用对应素材。这个方案肯定能提高性能,但是对创造性是一个制约

(按:即使是全新引擎,或者是有经验者二次设计的引擎,这些教诲仍然生效。《人月神话》里面有一个故事,说第二次研发时,经常因为过度设计导致工程灾难)

来源:GDC2015
知乎专栏:https://zhuanlan.zhihu.com/p/36287535

最新评论

  • App Store力荐,Supercell刚上线的第5款游
  • 游戏开发中最常见的5个错误
  • 王信文:穿越那座绝望山谷
  • 丁磊打碟了,丁磊养猪了 然而,丁磊没有人
  • 请不要让Roguelike成为独立游戏的遮羞布
  • IGDA:2018,游戏最好的时代,也是最糟糕的

小黑屋|稿件投递|广告合作|关于本站|GameRes游资网 ( 闽ICP备05005107-1 )

GMT+8, 2018-12-19 22:25

快速回复 返回顶部 返回列表