《DFQ》开发随录——图集

  欢迎参与讨论,转载请注明出处。

前言

  在游戏开发的领域里,图集(SpriteSheet)是一个很重要的概念,其好处在链接处也已言明。但若是引擎没提供相关的支持,那么便需要自己搞个解决方案了。而LÖVE也恰好是没有提供相关支持的,那么只好自己动手丰衣足食了,本文便记录其中心得。

装箱问题

  要实现图集的核心便是对图片进行拼合打包,其实类似的工具市面上亦有存在(如TexturePacker)。从功能上而论,TexturePacker完全可以满足需求(有提供命令行模式,可实现自动化)。可惜TexturePacker的免费版根本不堪使用,而破解也相继失败。而其他类似的工具要么无法满足需求,要么不支持macOS。只好自己手写一套了。
  实现的图集的难点无非在于拼合时图片排列的算法,由Claris告知得这种属于装箱问题,目前并无最优解。由装箱问题为关键字进行展开搜索,发现一种名为MaxRectsBinPack的算法可解决问题,我将之翻译成了Python版。如此装箱问题便解决了。

拼合问题

  接下来的问题便是“谁和谁拼合成一张图”了,我对此立下三个原则:

  • 关联性不高者不拼(拼成大图的代价便是成为资源共同体,如果关联性不高的拼合一块则会造成极大的内存浪费)
  • 黑底与透明者不拼(黑底图拼成大图必须得保证全图无透明点,否则游戏里会出现奇怪的线条)
  • 拼合后过大者不拼(需保证图片大小在4096*4096及以下,否则恐怕出现上限问题)

  以这三原则来看,是无法做到以文件夹为单位进行粗暴的拼合了。所以采用了编写配置的方式进行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
"effect": {
"map": {
"lorien": [
"lorien",
"/actor/article/lorien/pathgate",
"/actor/article/lorien/largegrass"
]
},
"death": [
"death",
"dieFlash"
],
"buff": {
"freeze": "freeze"
}
}

  配置以JSON形式存储,配置中的key代表着合图文件夹的层级,value则为欲拼合的图片文件夹,若无/开头则代表以当前文件夹层级为路径,反之则为全路径。以这套方案便可很自由地选择拼合的方案了。

配置问题

  图片的拼合问题解决后,便是游戏要如何以最低的代价去兼容新的图片形式了。解决方案自然是为原图片生成路径一致的配置文件,游戏通过读取配置文件以无缝对接新的图片形式。配置文件格式如下:

1
2
3
4
5
6
7
return {
image = "ui",
x = 0,
y = 151,
w = 45,
h = 41
}

  配置文件记录了所属合图的路径以及在合图中的坐标宽高,如此便可清晰无比地取得了。由于Python的lupa模块装不上,为此还专门写了个Lua与JSON的转换器

大小问题

  一般而言,因为光栅化需要对纹理采样进行快速取值,图片大小需要遵循2的N次幂(256、512、1024…)。这种符合的图片被称为POT(Power-Of-Two),同理不满足的称为NPOT(Non-Power-Of-Two)。在早期POT纹理可以说是必须的,而今在OpenGL ES2.0后支持了NPOT。但为了能满足ETC压缩以及兼容性,个人推荐还是对合图进行POT化。

后记

  其实从这个问题来看,选择流行的大引擎的确会更为方便。在Unity里可以由后台自动完成的事情现在却要一篇文章来总结,不过贼船已经上了,就只能走到黑了。