【译】自动适配Tiles地图的倍数计算方法
吸血喵 发表于 2014-06-26 11:52:11 2545

本帖最后由 吸血喵 于 2014-6-26 23:53 编辑

[align=center]  自动适配Tiles地图的倍数计算方法[/align][align=left]
  Posted on?January 26, 2010
  原文:http://www.saltgames.com/2010/a-bitwise-method-for-applying-tilemaps/ 译:喵
  
  
  这是根据周边情况自动选择正确tile的一个技术[align=left]
  我最初写这个文章是为了回复“a question on TIGSource”的一个问题,但是后来我觉的它值得去扩展并在这里重写一下。
  问题点:现在我已经可以生成一个整洁的平台关卡了,但是我想自动处理相邻的tiles,使其自动适配。[/align]
  相邻变化的Tiles
  像是游戏“超级玛丽”,它没有相邻tiles的变化,所有的石头,无论是单独的还是处于两块石头中间,看起来都是一样的图像。
  http://www.saltgames.com/wp-content/uploads/2010/01/NES_Super_Mario_Bros.gif
  对于很多游戏来说这也不错,只是,如果你想让游戏关卡看起来更“自然”一些,或者仅仅是想不让这些砖块反复重复的话,可能需要注意一下相邻tiles的适配问题。

  一元地图(0/1)
  让我们想象一下,使用某些不错的技术,我们已经可以为平台游戏生成一个关卡布局,包括一些石头以及空气(空的区域)。它可以被简单的看做是一张一元图像,每个位置都可以被看做是一个二进制数(0或1),来记录是空气还是石头,大致如下图:
  http://www.saltgames.com/wp-content/uploads/2009/12/filledGrid.png

  Tileset
  Tileset是一个图像集合,包括了在地图中需要使用的图像部分,例如马里奥游戏的tileset就非常小(这里仅指一代),仅包括一些有微弱变化的障碍物以及背景,如果我们要制作更加自然的场景则需要更多的图像:
  http://www.saltgames.com/wp-content/uploads/2009/12/tileset.png

  相邻格评估
  要想判定哪些tile会被使用,我们需要检查相应格子周围的“邻居”(本篇文章我们先忽略对角线的邻居)状态,相比写下庞大的if/else-if语句来检测邻居状态,这里我们使用一个分配值的方式来进行检测。
  http://www.saltgames.com/wp-content/uploads/2009/12/edge-values.png
  根据格子周围邻居的位置不同,我们给格子分配一些值来记录其状态。如上图示意,如果我们看到一个格子在上方有石头时,那么给予一个值,为1。如果上下都有石头的话,则是1+4=5。
  你可能已经注意到了,根据方向来分配的这些值都是二的倍数,它记录了四个方向上的砖块的组合状态(是石头还是空气)。
  这里有一张图,是已经分配好所有值的。你可以手工的来计算下这些值的由来,进而更清晰的了解其计算方法。
  http://www.saltgames.com/wp-content/uploads/2009/12/numberedGrid.png

  加上Tiles
  Tileset图像的排列不是随意的,它的排列方式应该能够使每个tile都能和地图中相应的数字对应上。一旦我们在地图上为一个格子上计算出它的数值,我们仅需要寻找tileset中相对应位置的tile就可以(见上文中的tileset贴图):
  http://www.saltgames.com/wp-content/uploads/2009/12/floatingIsland.png
  我们把之前计算出的数值填充上了tiles。OK~ 看起来棒极了!
  
  再给力点
  小节1 – 移除空的区域
  上述方法来处理平台游戏中浮在空中的平台会很好(平台的周围都是空气),但是当有两个tile类型存在时,它就无法处理了。
  换掉平台游戏的思路,让我们想象一下俯视角状态的2D RTS游戏。在这里我们用“草地”和“水”来代替之前的“砖块”和“空气”。在在这种情况下我们希望地图上的每一个格子都能呈现出合适的图像,它不像平台游戏那样有“空”的区域。这意味着通过计算格子周围的邻居关系,在地图上的每一个格子都需要被赋予一个值来完成tile的适配。
  我们可以完全照抄之前说过的“邻居值”系统,但是现在我们需要一个方法来分辨“草地”和“水”的不同。恩这非常的简单,只需要给这个位置加上一个额外值就好,它通常会是2^n(接触的格子数目):
  http://www.saltgames.com/wp-content/uploads/2010/01/extendedValues.png
  (注:上图中,a=2(地形的种类),b=4(上下左右四方向),因此中间格子“水”的值为2^4(a^b)=16)
  让我们确定一下上图的含义,周围有“水”的存在意味着我们需要给它加上一个位置值,“草地”则不用加(因为所有的格子不是草地就是水)。因此草地tile的值就是0,一个草地格子的上方和左方有水格子的时候,那么值为1+8=9.周围全是草地的水的格子的值是16,一个水的格子周围全是水时,则值为1+2+4+8+16=31。

  小节2 – 更加多样化
  我们如何来加入更多种类的地形?
  假设我们有三种不同的地形在俯视角游戏中:水、草地、森林。和水和草地接触时一样,我们必须要处理水+森林、草地+森林的关系。
  先前我们处理两种地形的关系(水+草地)时,用到了一个二倍计数系统,现在当我们要处理三种地形时,我们则使用三倍计数系统。我们的邻居值系统需要调整至适配新的计算系统:
  http://www.saltgames.com/wp-content/uploads/2010/01/trinaryValues.png
  和二倍计数系统一样,现在数值的增加是3^n。
  使用三倍系统时,每个格子都有三种可能的状态:草地、水、森林,或者记录为0、1、2。如果当前位置时草地,则忽略数值(乘以0),水则加上给定值(乘以1),森林则加倍(乘以2)。
  因此,当我们计算一个森林格子,上方是水,右边是水,下方是森林,左边是草地时,数值为:81 * 2 + 1 * 1 + 3 * 1 + 9 * 2 + 27 * 0 = 184
  (原文为81 * 3 + 1 * 2 + 3 * 1 + 9 * 3 + 27 * 0 = 275,喵不知道这是怎么算的……也许有地方喵没理解清楚_(:з」∠)_)
  你可能已经注意到了,在这种情况下你需要绘制324个tile图像来涵盖三种地形所有可能的搭配情况。手工绘制的话这是个相当庞大的工作量。因此我强烈建议至少一部分图像使用自动生成的方式来产生。
  当然,随着地形种类的增加,这个系统可以再次进行扩展,但是随之而来的是大量所需的tile图像。我建议给tiles的彼此接触设置一些限制,例如森林和水不允许直接接触,这样会省掉不少图像资源。


  附:自动生成过渡图像的一些技术http://www.gamedev.net/page/resources/_/technical/game-programming/tilemap-based-game-techniques-handling-terrai-r934

最新回复 (18)
  • Autukill 发表于 2014-06-27 07:13:21
    0 2
    恩呢,谢谢喵的翻译。

    大家熟知的魔兽争霸,星际争霸等也是同样的原理。

    我把它加入到了WIKI:http://wiki.gamemake.org/index.php?doc-view-18
  • a438305619 发表于 2014-06-28 12:50:29
    0 3
    {:soso_e153:} 看晕了
  • kzhang9 发表于 2014-06-29 08:33:48
    0 4
    有实例可以参照吗?
  • Autukill 发表于 2014-06-29 09:10:12
    0 5
    Quotekzhang9 发表于 2014-6-29 08:33
    有实例可以参照吗?

    实现较为容易。

    你可以参考官方DEMO:http://pan.baidu.com/s/1eQIeo3g

    DEMO代码很清晰的操作了贴图和碰撞

    有问题可以看:http://wiki.gamemake.org/index.php?doc-view-6
  • leesoar07 发表于 2014-06-29 09:57:33
    0 6
    学习了,制作主题医院或者过山车大亨之类的模拟经营游戏也用得着:)
  • BloveStorm 发表于 2014-06-29 01:08:25
    0 7
    喵的教程很实用!
  • kzhang9 发表于 2014-06-30 12:44:39
    0 8
    QuoteAutukill 发表于 2014-6-29 09:10
    实现较为容易。

    你可以参考官方DEMO:http://pan.baidu.com/s/1eQIeo3g


    非常感谢!
  • Autukill 发表于 2014-06-30 04:36:35
    0 9
  • 新手指导员 发表于 2014-07-14 12:08:07
    0 10
    {:soso_e142:}好贴,不过其中有错误貌似,小节2里面的例子数值对应的应该是,3^0*0+3^1*1+3^2*1+3^3*2+3^4*2=228,总tiles应该为3^5=243,,实际上就是从左开始顺时针编号1、2、3、4,中间是5,然后有几种类型就是几进制,这样想就简单多了。写了个最简单的情形的实现过程,链接:http://pan.baidu.com/s/1pJJPNWB ,素材和部分代码来源于官方demo。
  • 最爱宝贝菲 发表于 2014-08-14 02:27:23
    0 11
    QuoteAutukill 发表于 2014-6-29 09:10
    实现较为容易。

    你可以参考官方DEMO:http://pan.baidu.com/s/1eQIeo3g


    对于官方的DEMO的碰撞检测有点小问题,如果贴图的大小小于最大速度时,物体在最大速度下可能会越过第一次碰撞检测
  • Autukill 发表于 2014-08-14 02:37:04
    0 12
    Quote最爱宝贝菲 发表于 2014-8-14 14:27
    对于官方的DEMO的碰撞检测有点小问题,如果贴图的大小小于最大速度时,物体在最大速度下可能会越过第一次 ...


    http://www.gamemake.org/forum.php?mod=viewthread&tid=516&extra=page%3D2
  • 最爱宝贝菲 发表于 2014-08-29 05:24:25
    0 13
    QuoteAutukill 发表于 2014-6-29 09:10
    实现较为容易。

    你可以参考官方DEMO:http://pan.baidu.com/s/1eQIeo3g


    wiki上的"~ 32 +1 十六进制就是 FF FF FF E0,换成十进制就是4294967264"
    这是64位的算法吧,

    32位算出来的是-32
    即68 & $ffffffe0和68 & -32的效果是一样的。
  • Autukill 发表于 2014-08-30 07:30:34
    0 14
    是的
  • 某电子 发表于 2015-10-03 06:53:01
    0 15
    Quote新手指导员 发表于 2014-7-14 12:08
    {:soso_e142:}好贴,不过其中有错误貌似,小节2里面的例子数值对应的应该是,3^0*0+3^1*1+3^2*1+3^3*2+3^4* ...


    “实际上就是从左开始顺时针编号1、2、3、4,中间是5,然后有几种类型就是几进制”
    看到这句话后再思考,果然想的清楚多了,谢谢。
  • MikuScarlet 发表于 2015-10-04 08:45:22
    0 16
    完全没看懂…
  • Autukill 发表于 2015-10-06 08:25:26
    0 17
    QuoteMikuScarlet 发表于 2015-10-4 20:45
    完全没看懂…


    比如,你会发现魔兽争霸3中的地形纹理是过渡变化的,而这篇文章就是讲解这个效果是怎样实现的。
  • MikuScarlet 发表于 2015-10-11 11:52:55
    0 18
    QuoteAutukill 发表于 2015-10-6 08:25
    比如,你会发现魔兽争霸3中的地形纹理是过渡变化的,而这篇文章就是讲解这个效果是怎样实现的。 ...

    对啊,完全没看懂怎么实现的
    那个算法,什么加什么的。