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 }