8

主要这似乎是游戏使用的一种技术,它们将所有声音放在一个文件中,将纹理放在另一个文件中等。这些文件通常达到 GB 大小。

这样做的原因是什么,而不是将所有子目录中的所有内容都作为小文件进行维护——每个纹理一个,许多小游戏都使用它,而大公司更喜欢单片系统?

大量小文件是否存在一些文件系统开销?他们是否试图保护他们的财产——尽管大多数似乎只是一个带有新扩展名的压缩文件?

4

5 回答 5

7

我们在我工作的地方(一家游戏开发公司)使用这样的“存档”系统的原因:

  • 查找速度:我们很少需要遍历目录中的文件;我们更经常直接按名称查找它们。通过使用本质上只是一个序列的自定义“文件分配表” ,我们可以非常快速地hash( normalized_filename ) -> [ offset, size ]查找文件。我们还可以将此索引保存在 RAM 中,可能会将其与其他索引表交错,等等。
  • (当我们确实需要迭代时,我们可以轻松地迭代 a 中的所有文件.arc,或者我们可以在某处存储文件名列表、文件名哈希列表或 [offset, size] 对列表 - - 甚至可以作为存档中的文件。这通常比 FS 上的目录遍历更快。)
  • metadata:我们可以很容易地塞入我们想要的任何文件元数据。例如,“大小”字段中的单个位指示文件是否被压缩(如果是,它有一个标题,其中包含有关如何解压缩的更多详细信息)。如果我们提前对文件的结构有足够的了解,我们甚至可以对文件的各个部分进行不同的压缩(我们对 sprite 档案这样做)。
  • size:我们使用的设备之一具有“文件大小必须是 X 的倍数”的要求,其中 X 与我们的某些文件相比很大。例如,我们的一些 lua 脚本在编译时最终只有几百字节;每个 .luc 文件的额外开销很快就会增加。
  • 对齐:另一方面,有时我们浪费空间。为了利用来自文件系统的更快的流(例如后台 DMA),我们的一些文件确实希望遵守某些对齐/大小要求。我们可以在工具中解决这个问题,我们所拍摄的对齐/大小不一定要与底层 FS 对齐,这样我们就可以只在需要的地方浪费空间。

但这些都是世俗的原因。更有趣的东西:

每个.arc寄存器都在一个列表中,并尝试打开一个知道要查看弧的文件。我们首先搜索 RAM 中的存档,然后是设备 FS 上的存档,然后是实际的设备 FS。这给了我们很大的灵活性:

  • 文件系统的动态添加:在任何时候,我们都可以将新文件或存档流式传输到相关机器(通过网络等),并将其作为“逻辑”文件系统的一部分出现;当实际的 FS 驻留在 ROM 或 CD 中时,这非常棒,并且允许我们比其他方式更快地迭代。
  • (Doom 的.wad系统就是上述的一个例子,它允许模组制作者更容易地覆盖游戏中内置的资产和脚本。)
  • 没有底层 fs的可能性:可以使用在链接时bin2obj将整个弧直接嵌入到可执行文件 ( .rodata) 中,此时您无需查看设备 FS - 我们为某些小型演示版本执行此操作之类的。我们也可以通过这种方式通过网络发送关卡或保存游戏-运动鞋网。=)
  • 组织和加载/卸载:因为我们可以随时加载、卸载和覆盖我们文件系统的虚拟“部分”,所以我们可以做一些性能技巧,让 FS 中的文件数量在任何给定时间都非常小。我们还可以指定将整个存档加载到内存、索引表和数据中;我们的文件加载代码足够聪明,可以知道如果文件已经在内存中,除了移动指针之外不需要做任何事情来读取它。一些更高级别的代码实际上可以检测到文件在 ram 中,并且直接询问可能已经看起来像结构的指针。
  • 可移植性:我们只需要弄清楚如何在我们使用的每个新设备上获取几个文件,然后 FS 代码的其余部分或多或少相同。=) 我们偶尔会稍微更改一下工具输出(出于对齐原因),但大部分处理保持不变。
  • 重复数据删除:使用更智能的档案,例如我们的精灵档案,我们可以(并且确实)对数据进行重复数据删除。如果“跳跃”动画的第五帧和“踢”的第三帧相同,我们可以拉开文件,只存储该帧的一个副本。我们可以对整个文件做同样的事情。

我们最近将 PC 游戏移植到了 FS 访问速度慢得多的系统。我们没有改变数据格式,结果证明遍历原始设备 FS 上的一个目录来加载一百个小的 XML 文件绝对会浪费我们的加载时间。我们使用的解决方案是获取每个目录,使其成为自己的subdir.arc,然后将其粘贴在game.arc压缩的主目录中。当需要 dir 时(调用 opendir 之类的东西),我们将整个 subdir.arc 解压缩到 RAM 中,将其添加到文件系统中,然后超快速地迭代它。

能够在几个小时内将这样的东西放在一起,并减轻跨系统移植的痛苦,这使得这样的东西变得有价值。

于 2010-05-22T22:27:48.607 回答
1

文件系统确实有开销。通常,一个文件占用的磁盘空间向上取整为 2 的某个幂(例如,最多 4 KB),所以很多小文件会浪费空间。一些现代文件系统试图缓解这种情况,但 AFAIK 尚未普及。此外,文件系统在访问多个文件时通常非常慢。例如,复制一个 400 MB 的文件通常比复制 4000 个 100 KB 的文件要快得多。

当您必须修改文件时,文件系统会派上用场,因为它们比任何简单的本土解决方案都能更好地处理文件大小的变化。但是,对于恒定的游戏数据,情况肯定不是这样。

于 2010-05-22T20:26:45.733 回答
0

在 Apple 系统上,最常见的方法是按照您的建议使用目录。它们被称为 Bundle,在 Finder 中仅表示为一个文件,但如果您进一步探索它们,它们实际上是目录。这使得在从这个包中加载单个项目时编写代码和节省内存变得非常容易。:-) 此外,这使得对巨大数据库进行增量备份变得容易,例如您的 iPhoto 数据库只是一个捆绑包,因此您只需备份更改的文件和新文件

然而,在 Windows 上,我相信这更难做到,它看起来像一个“无论如何”的目录(我相信聪明的人已经找到了一个解决方案,可以让资源管理器将某些目录视为单个文件,但它是不常见)。

从游戏开发人员的角度来看,您不会处理如此小的文件,以至于磁盘空间开销是您非常关心的事情,所以我怀疑@doublep 的建议,因为它会带来如此麻烦,但确实如此如果用户要将整个游戏复制到某个地方,使用单个文件会容易得多,那么很容易检查整个集合是否正确。

而且,当然,对于不应该访问它的人来说,它更难阅读。但它也更难修改,这意味着更难修补,更难编写扩展。经常使用扩展的人更喜欢目录结构:The Sims。

如果我是游戏开发者,我会喜欢单独的文件。再说一次,我会像为 Mac 写东西一样使用捆绑软件 ;-)

干杯

尼克

于 2010-05-22T20:32:18.810 回答
0

我可以想到多种原因。

正如 doublep 建议的那样,文件在磁盘上占用的空间比它们需要的要多。所以存档可以节省空间。10k 个文件(任何大小)在打包到存档时应该可以节省 20MB。现在不是很大的空间,但仍然。

我能想到的另一个原因是磁盘碎片。我怀疑在访问碎片空间上的数千个单独文件时,碎片严重的磁盘会表现得更差。但我不是这个领域的专家,所以如果有经验丰富的人证实这一点,我将不胜感激。

最后,我认为这也可能与限制对单独游戏文件的访问有关。你可以暴露一堆 Lua 脚本,弄乱它们并破坏一些东西。或者,您可以将结尾的电影/声音/文本/任何内容暴露出来,并通过访问它而被宠坏。我自己也这样做:我使用多通道 XOR 密钥加密图像,将文本文件和配置变量打包成一个整体文件(压缩以提高安全性),并且只让音乐自由访问。这样一来,游戏的秘密将不会被发现更长时间:)。

或者可能还有另一个我从未想过的原因:D。

于 2010-05-22T21:10:44.613 回答
0

如你所知,游戏,尤其是大公司试图尽可能多地压缩性能。一种技术是将所有数据放在一个大文件中,然后将其 DMA 到内存(将其视为从 CD 到 RAM 的 memcpy)。由于所有文件都在一个大文件中,因此不会进行磁盘搜索,并且由于该技术,您可以快速加载大量文件(这可能会导致大量搜索)。

于 2010-05-22T21:18:19.383 回答