【问题标题】:Creating a packed Binary Representation of a Set of Files?创建一组文件的打包二进制表示?
【发布时间】:2010-11-27 19:42:52
【问题描述】:

所以我正在尝试开发自己的小型 3d 游戏。现在我或多或少地这样做是为了学习 C#。我想知道,打包纹理/脚本等资产的最佳方法是什么?

一般来说我想做的是这样的:

[header]
[number of records]
[Offset to Record 1 from End of header]
[Offset to Record 2 from end of Record 1]
.
.
[Offset to record N from Record N-1]
[record 1]
[256 bytes represent the filename]
[32 byte size]
[binary data]
[record 2]
.
.

现在我只想存储简单的图片和文本文件。我环顾四周,我真正发现的最好的东西是一个关于如何存储厄运一团的旧示例。

有人有经验吗?

【问题讨论】:

    标签: c# directx storage packaging


    【解决方案1】:

    没关系。如果您可以将所有内容加载到虚拟内存中并让交换处理它,那么您可以使用任何格式,真的。如果您只想随机访问一条记录(例如,您可以延迟加载,尽管未压缩的 memmap 也是延迟加载),那么您可能希望将索引保留在内存中。

    大多数人使用的库允许他们访问 .zip、.jar、.pak(地震格式)或其他类似(压缩或非压缩)存档格式,就好像它是文件系统的一部分(即,记录通过字符串键访问)。如果你能找到一个已经制作好的图书馆,我肯定会走那条路,例如truezip 用于 Java。 Apache Commons 有一个,但我不知道与 .NET 集成有多么容易(我相信这是一个很大的 C 代码库)。 ZipFS 看起来像是一个实际的 .NET zip 文件挂载器,它只将标头保存在内存中。

    或者,可能只是不太方便,您可以直接使用DotNetZip

    【讨论】:

      【解决方案2】:

      不要浪费时间发明自己的存储格式。

      您可以使用 SharpZipLib 或其他免费的 .net 压缩库。 有了它,您还可以将多个文件打包到一个存档中,并根据需要单独提取所需的文件。

      【讨论】:

      • 我这样做是为了学习。不是每个人都讨厌重新发明轮子。
      • 这很好,但即使是专业人士也经常使用现成的压缩。除此之外,您将在游戏本身上进行大量学习! :)
      • 能够有效地加载数据会对游戏产生重大影响。它是游戏本身的一部分。从磁盘加载数据是您可以执行的最慢操作,有充分的理由对其进行优化。
      【解决方案3】:

      您的设计对我来说看起来不错,尽管我认为您的意思是 32 bits 而不是 32 bytes

      我认为您的设计最适合您想要一次性加载所有资源的情况,因为它是一种顺序设计。如果你想一次只加载几个资源(可能是因为每个游戏关卡只使用资源的一个子集),那么效率会低一些,因为你必须依次通读每个资源才能找到这些资源你想要的。

      在这种情况下,您可能想尝试更多索引设计,可能是这样的:

      [HEADER]
      [Miscellaneous header stuff]
      [Offset to index from start of file]
      [Number of entries in index]
      [RECORD 1]
      [Asset data]
      [RECORD 2]
      [Asset data]
      .
      .
      [RECORD N]
      [Asset data]
      [INDEX]
      [ID or filename of asset 1]
      [Size of asset 1]
      [Offset to asset 1 from start of file]
      [Other asset 1 flags or whatever]
      [ID or filename of asset 2]
      [Size of asset 2]
      [Offset to asset 2 from start of file]
      [Other asset 2 flags or whatever]
      .
      .
      

      这将允许更好地随机访问资产,因为现在您只需搜索索引(您将加载到内存中)而不是搜索整个文件(可能不适合内存)。如果您想变得花哨,可以使用树或哈希表作为索引。

      将索引放在文件末尾而不是前面的原因是,它可以更轻松地将另一个资产添加到您的包文件中,而无需重新构建整个文件。否则,索引中的额外条目会丢弃所有偏移量。


      编辑:回复 cmets...

      我的想法是您只能通过索引访问资产,因此希望您在阅读资产时永远不会跑到资产的末尾。也许一个典型用例的例子会有所帮助。

      假设您想读取名为“TankTexture.png”的纹理。我认为你会这样做:

      1. 打开包文件。
      2. 读入固定大小的标头。
      3. 从标头中提取索引偏移量和条目数。
      4. 寻找索引的开头。
      5. 将索引读入数组(固定索引条目大小乘以条目数)。
      6. 在索引中搜索名为“TankTexture.png”的资产。
      7. 从索引条目中提取资产偏移量和大小。
      8. 寻找资产的开头。
      9. 读取资产大小给定的字节数。

      当然,对于后续资产,您只需要步骤 6-9。

      我希望这有助于解释我的想法。如果您有任何其他问题,请告诉我。

      【讨论】:

      • 我怎样才能让程序知道何时停止读取资产数据?
      【解决方案4】:

      如果您想出于学习目的这样做,那么 WAD 格式是一个不错的起点。但是,我建议使用分块文件格式。
      所以它基本上会遵循您建议的格式(即标题、TOC 等),但对于每个数据条目,您都有一个块 ID,用于标识它是什么类型的数据。
      这有很多好处,主要是您可以通过将代码设置为跳过它不理解的块来根据代码格式改变数据格式 - 这允许您的工具开发继续进行,同时保持游戏中数据的向后兼容性。

      我还建议您在 TOC 中添加一个额外的 32 位“标志”条目,这将允许您使用位域来启用压缩类型、加密等选项

      希望有帮助

      【讨论】:

        【解决方案5】:

        我说你的格式是一个不错的选择。理想情况下,您希望一次读取所有资产。例如,您希望将级别 3 的所有数据放在同一个包中,这样您就可以在一次读取中加载所有级别数据而无需查找。在多个包中拥有单一资产真的没问题。您只需要处理资产已加载的情况并跳过它。

        如何拆分数据应取决于数据之间的依赖关系(即,如果脚本需要某个模型,它们应该都在同一个包中)以及读取数据所需的粒度(例如,可以你一口气读完了所有的关卡数据?然后你可以把你的敌人放在关卡包里。但是如果你的游戏在世界上播放,也许你需要单独的敌人包。)

        确实,跟踪您的数据依赖关系是最困难的部分。在构建时,您想知道您提取的每条数据的依赖关系。在运行时,您只想读取包并将资产显示在内存中。您还需要在运行时跟踪依赖项,因为您需要随时了解 UNLOAD 的安全性。

        【讨论】:

          猜你喜欢
          • 2023-03-03
          • 2020-06-09
          • 2010-12-07
          • 1970-01-01
          • 2019-02-28
          • 2013-12-13
          • 1970-01-01
          • 2020-06-05
          • 2021-09-09
          相关资源
          最近更新 更多