今天我要谈的是资源管理的一个特别的方面:流(streaming)。
流的优点有二:
1、免除加载画面。
2、显示了比内存能装入的还多的细节/种类。
今年是2012年,如果玛雅人的预言成真,那么这就是所有人被迫看加载画面的最后一年。因为我没有直接参与游戏制作,而只是制作引擎,所以我可以大胆地泛泛而谈。
loading-screen(from gameaxis.com)
快速小结
在Bitsquid引擎中,资源(resource)是指根据名称和类型能特定识别的小块数据。例如:
类型 名称
unit units/beings/player
texture vegetation/grass/tall_grass_01
wav music/soft_jazz
资源文件是由我们的工具制作的,是人类可读的文件,以JSON式的格式书写。在运行时间内使用以前,资源文件必须经过编译。数据编译器将各种资源编译成平台特定的优化后的二进制大对象(以下称为blob):
streaming(from gamasutra)
为了便于装卸,资源被分类成数据包(package)。一个数据包(本身是资源)是一系列资源,可以同时装卸。
如果游戏非常小,整个游戏都可以装成一个数据包。而对于大游戏,一个数据包可能包含某个特定关卡所需的所有资源,或一套用于多个关卡的场景。由游戏程序决定何时装卸这些数据包。
至于游戏的最终释放,数据包被转换成数据束(bundle)。一个数据束包含所有在数据包中编译好的资源,而数据包被连结成一个单独文件,而这个文件由流压缩文件过滤器压缩而成。引擎一次装载整个数据束,无需搜索。这对于光纤媒介来说是很重要的,但它也确实加速了硬盘驱动的性能。
streaming(from gamasutra)
免除加载画面
为了免除加载画面,我们可以使用数据包流。我指的是在后台下载新数据包的能力,而引擎仍然做其他事。这意味着当玩家接近游戏中的一个新区域时,引擎就可以开始在后台下载那个区域的数据,同时玩家仍然在游戏。当玩家达到那个区域,数据已经存在内存里了,所以玩家不需要等待加载画面。
在Bitsquid引擎中,数据包总是在后台下载,所以数据包流是默认开户的。游戏程序可以告诉引擎何时开始下载数据包。下载是由一个独立的后台线程处理的,游戏程序可以测试引擎的每一帧,以决定下载是否完成。当下载完成,引擎就可以开始使用新数据。
在加载时间内,引擎应该做什么是由游戏程序决定的。游戏程序可以选择拖延引擎、显示加载画面或做其他更有趣的事。
对于成功的流,组织数据包的方式有很多,并且引擎允许你使用任何一种你偏好的方式。不同的方式可能对不同的游戏才能生效。线性进程的游戏可以把各个“阶段”归为一个数据包,在当前数据接近完成时再激活另一个数据包的下载。对于开放世界的游戏,各个章节的地图和其他“特殊的”场所的额外数据包可以归为一个数据包。具有随机战斗的游戏可以对玩家遇到的不同敌人形成单独的数据包。
事实上,引擎并没有固有的流模式,所以无法给设计师太多权力和灵活性。但有权力也伴随着更大的责任。设计师必须忍受巨大的压力,即正确地设置数据包和决定何时装卸数据包。
也许在将来,我们会选择一两种流模式作为“标准的方案”,然后为其提供一些功能性的便利。当然,如果有必要,你仍然可以选择“手动”模式。
显示更多细节
对于显示比内在能装入的还多的细节,我们使用了一种我也找不到更好的词来称呼的东西:资源流(resource streaming)。资源流指的就是,对于某种资源,当我们下载它的数据包时,我们不会将所有资源下载到内存中。我们只是分部分地把资源留在内存中,当需要时再一点一点地流进或流出。
流资源的最简单的例子也许就是视频文件。视频文件可以包含大量百万字节的数据,我们不想把所有这些数据作为一个大数据束下载到内存中。相反地,我们想在视频播放时,一帧一帧地将数据流进内存中。
记住我在小结中说的,各种资源被编译成平台特定的blob。其实,我说谎了。真相是,各种资源被编译成两种blob:
streaming(from gamasutra)
第一种是内存驻留blob(memory-resident blob)。这个我们已经知道了。它是一束一束地由后台线程下载进内存的。
第二种是流blob(streaming blob)。它包含的数据是不直接进入内存的,而是通过流管理器获取。流管理器负责在必要时将数据从内存中移进和移出。
并非所数据类型经过编译后都产生流blob。事实上,大多数都不产生。只有用资源流(如视频)输出流数据的类型才产生流blob。
由特定的内容类型的数据编译器决定什么进入内存驻留blob和什么进入流对象。例如,视频编译器将所有标题信息(游戏邦注:包括帧数、内容大小、帧率等)放入内存驻留blob,把原始帧数据放入流blob。这样,无需下载所有流数据,我们就可以知道视频的大小和其他有用的信息。
如果你想在此基础上制定MegaTexture的方案,那么你得把最低MIP(每秒百万条指令)与一些可以告诉你如何快速定位流中的数据的索引一起放入内存驻留数据。
对于声音数据,你可以将大约前100毫秒的声音放入内存驻留区域,其余的放入流中。这样,一旦触发,你就可以立即开始播放声音,而不必等任何数据流进。
特定数据包的所有流blob都被放入流束中,而这个流束紧接着下一个数据包的流束。各种资源的流数据的补偿和大小储存在一般的数据束中,与那种资源的内存驻留数据一起。这样,各种资源总是知道在哪里寻找它在流束中的流数据:
streaming(from gamasutra)
与一般的数据束不同,我们希望流束能够随机访问。因此,这应该依靠硬件驱动而不是光纤媒介。出于同一个原因,我们不压缩流束。你想流的大部分资源格式已经有内置压缩格式(视频、声音、贴图)。对于其他资源,当你编译数据时,你总是可以任意压缩。
当束被加载时,我们打开相应的流束,让文件句柄用于备查。任何想访问流数据的系统都可以使用这个句柄(异步)读取流束。
我们不提供任何通用系统来决定何时流进这个数据、在内存中贮存多少以及何时抛出。相反地,我们让各个支持资源流的独立系统来决定。视频流方案在如何贮存数据方面,与贴图流方案非常不同。迫使二者都遵守相同的模式会使事情变得复杂,且只能实现局部优化。
综合使用数据包流和资源流,你可以用一个简单又灵活的模式覆盖几乎所有流方案。
via:游戏邦/gamerboom.com
更多阅读: