1- RPG MAKER的自动砖块拼接
在目前比较流行的游戏制作工具RPG MAKER里面,做地图编辑的时候,有一种特殊的砖块,不需要手动拼接,而直接用鼠标绘制,就自动帮你拼好地块。
如下图所示
而这一切是使用下面这张图片来拼接而成的。RPG MAKER把它保存在RGSS/Standard/Graphics/Autotiles里面。
RPG MAKER本身砖块是使用32*32的规格来进行组合的,这张AUTOTILE资源,是3*4排列的32*32的图片。
经过观察发现,上面的图片是使用下面这个图片中的元素按照16*16的大小分割来拼接而成的。
为了验证这个想法,我做了下面这个图片,替换掉RPG MAKER原来这张图片。
再用RPG MAKER察看刚才的地图,变成了这样
从中可以看出,RPG MAKER果然是用16*16的小块来进行分割和拼接。
2- 实现方法
决定一个32*32的图块由哪4个16*16的小图块拼接的关键,是这个图块的周围的同类块的位置和数量。下面介绍一个拼接调色板的方法。
步骤1- 把图块周围的邻接块位置编号,如下图
图中标记了8个BIT,每个BIT表示这个位置上是否有同类块,有就是1,没有就是0。这样,我们就得到了一个0-255的索引。
步骤2- 建立图块拼接调色板
我们针对这个索引,建立一个256元素的表。
在这个256元素的表里面,把每个索引对应的目标块的样子描述一下。
描述一个目标块,需要4个16*16的小块。而表示这个小块,可以使用他们在AUTOTILE图片中的索引。
做完这一步,我们得到了256个4元组。
因为AUTOTILE的图片规格都是一样的,所以,这个调色板可以用在每一种自动拼接的砖块上。
这两个简单的步骤结束后,我们就能够正确的绘制每一个自动拼接的图块了。因为它索引的数据是拼接方法,所以叫它是拼接调色板。
1- 分析
通过对照观察,我们会发现,在这个自动拼接元素上,小元素可以分为以下几种:
A- 向内转角(也就是0,1,6,7,以及12, 17, 42, 47,这里只取一组即可 )(FIXED:这里选择12,17,42,47这组,因为部分特殊元素这四个才是真实的向内转角)
B- 向外转角(4,5,10,11)
C- 上下连接(24,29,30,35)
D- 左右连接(14,15,44,45)
E- 填充物(26,27,32,33)
通过观察组合图块,我们会发现下面的几条规律
A- 斜方向的同类块对目标块的影响当且仅当斜方向相邻的两个垂直方向上都有同类块的时候才会出现。也就是将目标块的对应小图块变成填充物。
B- 当两个相邻的垂直方向上都有同类块,且他们之间的斜方向上没有同类块的时候,目标快对应的小图块变成向外转角
C- 当某个垂直方向上有同类块,且相邻的一个垂直方向上没有同类块,无论相邻的斜方向上有没有同类块,目标块对应的小图块变成连接(向上下或者向左右连接)
D- 当某个垂直方向上没有同类块,且相邻的垂直方向上也没有同类块,无论相邻的斜方向上有没有同类块,目标块对应的小图块变成向内转角
2- 生成算法(C#代码)
2 // 向外转角 4 5 10 11
3 // 上下连接 24 29 30 35
4 // 左右连接 14 15 44 45
5 // 向内转角 12 17 42 47
6
7 // 各方向比特 1 2 4 8 16 32 64 128
8
9 // 4 个小图块对应的索引
10 // 0 1 小图块在大图块里
11 // 2 3 按照这种顺序排列
12 byte[] mTile = new byte[4 ];
13 // 调色板数组,每个元素是一个包含4个索引的DWORD
14 UInt32[] mAutoTilePal = new UInt32[256 ];
15
16 for (uint nIndex = 0; nIndex < 256; nIndex++ )
17 {
18 // 初始化成向内转角
19 mTile[0] = 12 ;
20 mTile[1] = 17 ;
21 mTile[2] = 42 ;
22 mTile[3] = 47 ;
23 // 如果上方有同类块
24 if ((nIndex & 2) > 0 )
25 {
26 // 如果左方有同类块
27 if ((nIndex & 128) > 0 )
28 {
29 // 如果左上方有同类块
30 if ((nIndex & 1) > 0 )
31 {
32 // 目标块左上为填充物
33 mTile[0] = 26 ;
34 }
35 else
36 {
37 // 目标块左上为向外转角
38 mTile[0] = 4 ;
39 }
40 }
41 else
42 {
43 // 目标块左上为向上连接
44 mTile[0] = 24 ;
45 }
46
47 // 如果右方有同类块
48 if ((nIndex & 8) > 0 )
49 {
50 // 如果右上方有同类块
51 if ((nIndex & 4) > 0 )
52 {
53 // 目标块右上为填充物
54 mTile[1] = 27 ;
55 }
56 else
57 {
58 // 目标块右上为向外转角
59 mTile[1] = 5 ;
60 }
61 }
62 else
63 {
64 // 目标块右上为向上连接
65 mTile[1] = 29 ;
66 }
67 }
68 else
69 {
70 // 如果左方有同类块
71 if ((nIndex & 128) > 0 )
72 {
73 // 目标左上为向左连接
74 mTile[0] = 14 ;
75 }
76 // 如果右方有同类块
77 if ((nIndex & 8) > 0 )
78 {
79 // 目标右上为向右连接
80 mTile[1] = 15 ;
81 }
82 }
83
84 // 如果下方有同类块
85 if ((nIndex & 32) > 0 )
86 {
87 // 如果左方有同类块
88 if ((nIndex & 128) > 0 )
89 {
90 // 如果左下方有同类块
91 if ((nIndex & 64) > 0 )
92 {
93 // 目标块左下为填充物
94 mTile[2] = 32 ;
95 }
96 else
97 {
98 // 目标块左下为向外转角
99 mTile[2] = 10 ;
100 }
101 }
102 else
103 {
104 // 目标块左下为向下连接
105 mTile[2] = 30 ;
106 }
107
108 // 如果右方有同类块
109 if ((nIndex & 8) > 0 )
110 {
111 // 如果右下方有同类块
112 if ((nIndex & 16) > 0 )
113 {
114 // 目标块右下为填充物
115 mTile[3] = 33 ;
116 }
117 else
118 {
119 // 目标块右下为向外转角
120 mTile[3] = 11 ;
121 }
122 }
123 else
124 {
125 // 目标块右下为向下连接
126 mTile[3] = 35 ;
127 }
128 }
129 else
130 {
131 // 如果左方有同类块
132 if ((nIndex & 128) > 0 )
133 {
134 // 目标块左下为向左连接
135 mTile[2] = 44 ;
136 }
137 // 如果右方有同类块
138 if ((nIndex & 8) > 0 )
139 {
140 // 目标块右下为向右连接
141 mTile[3] = 45 ;
142 }
143 }
144
145 mAutoTilePal[nIndex] = (UInt32)mTile[0 ];
146 mAutoTilePal[nIndex] |= ((UInt32)mTile[1]) << 8 ;
147 mAutoTilePal[nIndex] |= ((UInt32)mTile[2]) << 16 ;
148 mAutoTilePal[nIndex] |= ((UInt32)mTile[3]) << 24 ;
149 }