侧边栏壁纸
博主头像
落叶人生博主等级

走进秋风,寻找秋天的落叶

  • 累计撰写 130562 篇文章
  • 累计创建 28 个标签
  • 累计收到 9 条评论
标签搜索

目 录CONTENT

文章目录

大世界技术浅析,WOW的梦幻影歌——客户端资源加载

2023-01-21 星期六 / 0 评论 / 0 点赞 / 80 阅读 / 26437 字

序 2005年开始做MMORPG的时候,国内游戏行业还刚刚起步,市场上大部分游戏出产自韩国,唯一一款火爆的3D游戏奇迹,正因为外挂猖獗而摇摇欲坠。主流的游戏市场上由盛

.



2005年开始做MMORPG的时候,国内游戏行业还刚刚起步,市场上大部分游戏出产自韩国,唯一一款火爆的3D游戏奇迹,正因为外挂猖獗而摇摇欲坠。主流的游戏市场上由盛大代理的传奇还光焰照人,但陈天桥在那一年不吝宣发费用所推广的却不是什么复制传奇的MMO,而是一款纯粹女性向的游戏——《梦幻国度》。伴随着这款游戏商业化的失败,玩家和从业者都在骂,骂盛大眼光短浅骂的震天响。”女性向游戏”这一母题尚需更多时日在国内游戏行业里酝酿发酵,而后分裂出以Q萌、美型吸引女性玩家渗入再而吸引男性玩家的MMO,或是以宫斗、Avatar、恋爱为主旋律而满足女性玩家日常情感消费之需“她游戏”。时至今日,大概不再有人怀疑“女性向游戏”是伪命题,而陈天桥的眼光前瞻性和壮为先烈这一事实,却在后来的机顶盒战略上,还会继续重演。那一年的《梦幻西游》和《大话西游 II》,做为网易最好成绩的网游,它们的成绩不过PCU 75万。

那年5月,魔兽世界(WOW)测试,技术出身的PM要求人人都魔兽世界必须40级,不限职业,公司全额报销点卡。年初1月刚进公司的时候,用的还是自制脚本,等在WOW中发现他们用的是Lua的时候,项目组不惜成本的丢掉了自己做的一整套脚本和编辑器,转向Lua。Lua之于国内游戏行业首席脚本的地位,当由WOW所确立。其世界观自魔兽争霸以下,大多承接自DND的种族设定,其叙事却因史诗般的宏大而震撼人心。WOW在体验上不光带来了40人低容错的超大团队副本,也带来了它巨大的野外世界及野外世界来自部落和联盟的血腥厮杀,从荆棘谷到南海镇,从阿拉希到黑石山口无乎处处都是热血疆场,这一巨大的世界在体验上和传统MMO不同,它在场景连续行进过程中不需要切出Loading界面,它是一个体验中无缝连接的世界,无缝世界。一个游戏,影响了一个行业,跨越一个时代。

MMO史不遑多论,我想扒的是大世界的基础技术框架。而既已题名梦幻,是因为诸多因由看法为一家之言,有些做法多次上线验证,但因团队和个人能力有限,或未及远虑而漏洞百出,另一些不过是一些闲下来几个好友小斟微酌,茶余饭后的日常YY,其或无实用之功,或离真实的问题南辕北辙而失之万里,权且聊作笑谈。


客户端——地图加载


游戏的技术问题,从整体上来说大体上可以归结于一些子问题集合,问题本身的解决方式就对应着这个子集的求解。这些问题的求解一方面依赖于对游戏系统本身的理解及其模型建构的契合度,另一方面又依赖工程师对于数学、算法和软硬件的熟悉程度。而做为游戏客户端程序员尤其是引擎程序最大的苦恼无外乎两条:一条是实时图形只求解各类问题的次优解或看起来像的解,但这些求解又不具备基本的艺术性,从而不具备永恒存留的可能,另一条则是硬件厂商遮遮掩掩总是让人把大量的时间花在对问题解决毫无意义的硬件工作原理的猜测验证上。

大世界最容易被注意到又最早被广泛以各种方式解决的技术挑战是地图加载问题。显然,当地图上资源总量超过运行内存总大小的时候,我们就没办法一次性把地图资源加载到内存。资源按需加载的需求应运而生。

第一个被广泛应用的地图按需加载策略是:


九宫格地图加载策略





九宫格




如上图所示,红点表示当前玩家所在地图块,周围8块浅灰色区域表示同时加载在内存中但不显示的地图块,深灰色的区域表示的是存储在磁盘上的地图块。



当角色从红块移动到橙色区域,右侧的3块蓝色区域会被加载到内存,左侧的3块黑色区域会被从内存中卸载,从而保持内存中一直只持有9块地图。

上述过程是九宫格加载核心的思想,在实际应用中需要平衡分块大小、内存占用量和磁盘IO的速度和频繁程度。

大多数2D游戏中九宫一格等于一屏的大小,而3D游戏中则总是取一个和视距相关的值,而且块数也不限于九块,也可能变种为加载5*5或7*7格,格子的边界也可能从硬边界扩张为软边,这样当角色在边界上来回晃动的时候不反复触发加载和卸载。

UE4所提供的大世界分块组件:


WorldComposition地图加载



Epic的UE4提供了一个大世界加载策略——WorldComposition,它的功能简述如下:首先把世界划分许多层,每一层包含一些关卡也包含一个配套的加载策略——XY平面的加载距离;它的关卡划分和面积无关,也无需等比。



如上图所示,世界地图被拆分为7个关卡,3个红色关卡(A,B,E)和4个橙色关卡(C,D,F,G)表示的是每个关卡所覆盖的范围,容易看出他们的面积不对等。

WorldComposition还允许把不同的关卡设置到不同的层中,而因为每层可以有不同的加载距离,这就可以实现不同的类型的子关卡加载距离不同的策略。比如地形,建筑、大型植被、结构性地图物体放到500米就加载的层中,而花草、小物体则放到200米才加载的层中,就可以很容易实现只加载近处的花草和小物体,而远处则只加载影响整体观感的地图结构性组件的策略。

当我们把WorldComposition只使用一层,而且每个关卡的划分都是等面积的,它就退化成了经典的九宫格地图加载方式。


基于触发器的地图加载



对于地图两点间大多数存在通路的情况下,WorldComposition和九宫格不失去良好的加载策略,但对于两块区别之间间只存在极少通道和可见性的情形,则上述加载方式就存在很大的资源浪费,这一类的地图如地下城、室内地图、山洞、管道等迷宫式的设计。



如上图所示的地图,红蓝区域相互之间既不可见也不存在快速通道,所以当玩家活动在约色区域时不需要加载蓝色区域;当玩家从红色区域走到绿色箭头处的时候才开始加载蓝色区域,同样,红色区域相对于蓝色区域来说亦是如此。这样就可以在上边的绿色箭头处放一个触发器,当玩家触发该触发器时,加载蓝色关卡,在下边的绿色箭头的过道放一触发器用于加载红色关卡。

触发器除了可以用于地图加载,也同样可以用于地图卸载。

UE4同样也提供了一个触发式地图加载功能组件——LevelStreamingVolume。


资源加载的基本假设


从抽象层面上看,分层分类的地图加载策略 + 触发式的地图加载似乎已解决大世界的地图加载问题。但在实际的资源加载应用中,其表现如何呢?

答案是远远不够,第一个碰到的问题是:世界上没有两片相同的树叶,一个人也不能两次步入同一条河流,而资源加载在不同的运行环境(包括硬件和软件)下的时间也大相径庭。而在上述加载策略中,关卡的划分是静态的,所以他们在同一输入条件下,其加载的资源总量不会动态发生改变。

要使玩家在移动过程中远处不出现空洞和突然刷出物体,需要满足以下不等式

即需要在物体显示之前,完成对它的加载。式中左端和可用加载距离成正比,和加载速度成正比,和玩家移动速度成反比,而左端需要加载的资源总量静态确定。加载速度则依赖运行环境。

要满足上述不等式,不外乎改变上述三方的值:要么加大加载时长,要么加快加载速度,要么缩减必须加载的资源量。事实上这三条就是资源加载优化的出发点。

  • 在内存可控的情形下,可以适当提前进行资源加载,即内存中保持一定的冗余加载量。

    这可以在关卡设计时,就保持在更远的距离进行资源加载。

    最理想的情况下:

    保证即使支持的最低硬件配置也能在加载时间内可以加载完所有必须的数据。

  • 加快资源加载速度,资源加载链路可用的优化包括:

    优化数据存储、IO模型,尽可能减少数据解析和复制的开销、并发执行等。

  • 减少必需资源总量,这一部分可用的优化包括:

    优化资源重用、减少不必要的资源冗余、资源加载优先级、未加载完成时使用占位资源等等。

    实际上不管在使用何种方式划分关卡,总存在资源冗余的情况,而如果使用九宫格式的资源加载,其冗余资源占有量和格子大小的成正比。

地图加载的常见优化手段


资源代理



故名思义,就是当资源未完成加载的时候使用一个全局的资源占位符做为它的代理对象存在。早年的WOW在其它角色未完成加载的时候,先显示了角色名称和他脚下的一个椭圆形的小黑影用于标识该角色的存在。

对于大多数显示数据而言,其几何数据相对于纹理和Shader数据加载往往更快,也存在有游戏在加载完几何Mesh先显示白模的做法。

而对于UE4来说,其做法更进一步,它支持了一个叫做Level Lod的特性——它允许你将一整个关卡使用一个简单的模型做为它的代理。这个模型在几何表示和材质上可以随意简化,就可以达到快速加载显示而不出现场景空缺一块的问题。


加载优先级



和代理资源一样,加载优先级同属于减少必需加载资源总量的范畴,它认为资源的高级LOD一定比低级别的LOD数据量更小加载更快,故它优先加载显示资源的最高一级LOD的几何数据和纹理数据并提交给显示层。而后继续加载当前所需要的最恰当的这一级LOD的几何数据和纹理数据,当它们都加载完成之后才切换到正常的LOD级别。

因为这一过程往往耗时在毫秒级,所以对用户的体验来说顺滑无感知。


IO模型



对于大数据量的文件读取来说,使用内存映射大多比直接文件IO要快50%以上。而对于写文件来说,是否使用带缓冲的文件读写API,单次写入文件的Buffer Size不同,其效率也可能会有倍数级的差距,大多数设备的文件写入API在写入Buffer大小约为内存页面大小时效率最佳。


数据存储——格式与顺序



因为现代计算机硬件结构的原因,文件的顺序读取速度远超随机读取速度。如果资源在使用中的关联性和其在磁盘中存储关系的临近性相关,则其命中页面缓存的可能性就高,读取性能就高,反之则可能出现缺页的情形。针对这一情形,可以把资源在存储中的临近性按到其在地图中的空间临近性进行存储。但这样会带来一个权衡,如果一个资源在多处或多张地图中同时被使用,则如此存储可能带来一定的磁盘资源冗余。

数据存储的另一个可考虑点是选取恰当的数据格式,数据格式的优化至少包含两部分:一是压缩和加密方式的选取,另一个是单个资源存储格式的选取。

传统引擎大多采用zip文件压缩整体资源包,但实际上zip在压缩比率和解压效率上都比不上zstd。而如果适当损失一些压缩比率,则lz4的解压效率则数倍碾压zip。商业压缩库oodle号称速度更快,但其价格贵让人自惭形秽。

而对于资源本身说,其格式解析过程也往往会占有很大一部分耗时。最优解是不需要解析,存储的数据格式就是使用时的内存结构,其次是解析的是二进制的数据,其效率要远优于解析文本格式数据,这一点在配置文件和显示数据格式的设计中,对性能影响尤为可观。


并发——利用多核



现代游戏运行的硬件已消灭了单核的存在,资源加载过程有效的利用多核是现代游戏资源管理系统的基本要求。

一般来说因为IO总是慢的,所以IO大多在工作线程中执行。

大多数逻辑相关的数据、配置文件都不存在多线程依赖关系,他们的数据读取、对象序列化都可以在工作线程里完成。

显示数据的显示资源初始化在很多设备上需要在单独的渲染线程进行初始化,所以它们加载往往需要分成三步:IO,对象序列化和初始化、渲染线程的显示资源初始化。谨慎处理好三者的关系既能提高加载速度,又可以减少游戏卡顿的发生。


减少加载过程中的数据拷贝



在最理想的情形之下,数据格式和驱动层所需要的格式一致,至多只需一次内存映射和一次数据拷贝到显示专用内存(或显存)即可达成数据初始化之目的。

但大多数游戏或游戏引擎的处理都存在这样或那样的冗余步骤。如UE4加载一个模型的顶点数据,从IO层到真正创建VertexBuffer还可能经历高达4次数据拷贝。

早在10几年前,做高性能服务器的网络工程师就在尝试Zero-Copy方式的网络消息收取和转发……



往期精选

Unity3D游戏开发中100+效果的实现和源码大全 - 收藏起来肯定用得着

Shader学习应该如何切入?


喵的Unity游戏开发之路 - 从入门到精通的学习线路和全教程



声明:发布此文是出于传递更多知识以供交流学习之目的。若有来源标注错误或侵犯了您的合法权益,请作者持权属证明与我们联系,我们将及时更正、删除,谢谢。

作者:Jiff

原文:https://zhuanlan.zhihu.com/p/256342436



More:【微信公众号】 u3dnotes


https://zhuanlan.zhihu.com/p/256342436

.

本文分享自微信公众号 - Unity3D游戏开发精华教程干货(u3dnotes)。
如有侵权,请联系 [email protected] 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

广告 广告

评论区