创建一组文件的打包二进制表示?

时间:2009-09-12 21:29:34

标签: c# directx storage packaging

所以我正在努力研究自己的小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]
.
.

现在我只想存储,简单的图片和文本文件。我做了一些环顾四周,我真正找到的最好的事情是一个关于如何存储厄运的旧例子。

有没有人有经验?

5 个答案:

答案 0 :(得分:3)

没关系。如果您可以将所有内容加载到虚拟内存中并让交换处理它,那么您可以使用任何格式。如果你想随机访问一条记录(例如,你可以懒得加载,虽然未压缩的memmap也很懒),那么你可能想把索引保留在内存中。

大多数人都使用一个库来访问.zip,.jar,.pak(地震格式)或其他类似(压缩或非压缩)的归档格式,就像它是文件系统的一部分一样(也就是说,记录由字符串键访问)。如果你能找到一个已经建成的图书馆,我肯定会这样做。 Java truezipApache Commons有一个,但我不知道整合w / .NET是多么容易(我相信它是一个很大的C代码库)。 ZipFS看起来像是一个实际的.NET压缩文件安装程序,它只将标头保存在内存中。

或者,可能只是方便一点,你可以直接使用DotNetZip

答案 1 :(得分:1)

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

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

答案 2 :(得分:1)

你的设计看起来不错,不过我认为你的大小是32 而不是32 字节

我认为您的设计最适合您想要一次性加载所有资产的情况,因为它是一种顺序设计。如果你想一次只加载一些资产(可能是因为每个游戏级别只使用资产的一个子集),那么它的效率会有所降低,因为你必须依次读取每个资产来查找你想要的。

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

[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]
.
.

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

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


编辑以回复评论...

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

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

  1. 打开包文件。
  2. 读入固定大小的标题。
  3. 从标题中提取索引偏移量和条目数。
  4. 寻找索引的开头。
  5. 将索引读入数组(固定索引条目大小乘以条目数)。
  6. 在索引中搜索名为“TankTexture.png”的资产。
  7. 从索引条目中提取资产抵消和大小。
  8. 寻求资产的开始。
  9. 读入资产大小给出的字节数。
  10. 当然,对于后续资产,您只需要步骤6-9。

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

答案 3 :(得分:0)

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

我还建议你的TOC中有一个额外的32位'标志'条目,允许你使用位域来启用压缩类型,加密等选项

希望有所帮助

答案 4 :(得分:0)

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

如何拆分数据应该取决于数据之间的依赖关系(例如,如果脚本需要某个模型,它们应该都在同一个包中)以及您需要多大程度地进行读取(例如,可以你可以一次性读取所有关卡数据吗?然后你就可以把你的敌人放在关卡包中了。但是如果你的游戏在世界上流淌,也许你需要为敌人提供单独的包裹。)

真的,跟踪数据依赖性是很难的部分。在构建时,您希望了解所引入的每个数据的依赖性。在运行时,您只需要阅读包中并将资产显示在内存中。您还需要在运行时跟踪依赖项,因为您需要知道在任何给定时间对UNLOAD的安全性。