浅谈RPG Maker XP自动地图元件的绘制原理

来源:互联网 发布:sql注入实例 编辑:程序博客网 时间:2024/04/23 22:29

浅谈RPG Maker XP自动地图元件的绘制原理


序:最近刚好想写个类似RMXP的地图编辑器,遇到的第一个问题就是自动地图元件的绘制问题。老实说,我不知道这东西到底叫什么(特别是英文叫什么),只知道RMXP翻译过来就是自动地图元件,而且魔兽的地图编辑器也用到这种技术,但我在网上却始终找不到相关的资料。其绘制原理其实很清晰,就是用户给出指定规格的地图元件资源,编辑器将其拆分,在绘制时再根据绘图上下文来进行拼接。但具体的过程以及拆分和拼接原理却很少有人提及,这里我整理了一下。


1.RMXP的自动地图元件规格

首先介绍下RMXP的自动地图元件规格,其中第一格和第二格都是展示用的,并不用在具体的拼接上。话说我也是刚搞懂的。

图1

真正用在拼接的就是剩下的内容,这里先将其按下图拆分开

2

这里的每一个小格就是最后用于拼接的格子,我们暂且叫它小元件,如图每一个小元件都有一个编号

虽然编号为0,1,2,3,6,7,8,9的小元件都不会用到,但这样可以方便由编号来进行定位小元件的矩形

计算编号为i的小元件在图中的矩形的方法为:

size = 16       // 小元件的宽高row = i / 6     // 小元件所在的行col = i % 6     // 小元件所在的列(col * size, row * size, size, size)


2.分析所有拼接情况

RMXP中,双击自动地图元件,即可以打开自动展开元件的对话框。里面显示的就是所有拼接情况。

3

同样的,我们给每一种情况定一个编号

4

注意,最后2个情况是一样的,所以总共有47情况(编号从0开始)

每一种情况都是由4个小元件拼成的。这里先不急着确定每一种对应哪4个小元件。

先来分析下到底是哪47种情况。

假设我们现在正在用RMXP的地图编辑器来绘制自动地图元件,如下图,我们打算在位置4绘制一个自动地图元件

5

那么怎么判断绘制出来的是哪个情况呢?很简单,根据周围的1,2,3,5,6,7,8格子是否有自动地图元件来确定位置4的绘制情况。(这里要注意,影响位置4绘制情况的是周围8个格子是否有自动地图元件,而每个格子具体是哪个情况并不影响

按照排列组合公式,可以算出8个格子的出现组合有256种,这比47大多了。为什么呢?

这是因为RMXP并不支持对角连接,如下图

6

14位置的自动地图元件并不连接起来,也就是说此时1位置并不会影响4位置的绘制情况。那什么时候才会影响呢?

看下图

7

可以看到,当位置13同时存在时,位置0才会影响到位置4。也就是说:当左边和上边都存在时,左上角才有影响力。同理可得:当左边不存在时,左上角和左下角都将失去影响力。(注意这点,后面就是靠这条理论来列举情况的)

现在来列举所有情况。可以不考虑对角,只考虑边的组合。4边的组合有16种,可以分为5类:

1. 4边都没有(1种)

    此时对角全失去影响力。位置4只有1种情况,即情况编号46

2. 只有1边(4种)

    此时对角依然全失去影响力。位置4只有4种情况,即情况42,43,44,45

3. 2边(6种)

    3.1 对边情况(2种)

    即左边和右边同时出现,或者上边和下边同时出现,此时对角依然全失去影响力。位置4只有2种情况,即情况32,33

   3.2 临边情况(4种) 

   考虑左边和上边同时出现,此时左上角有影响力,有左上角出现与不出现2种情况。其他临边情况一样。位置4共有4*2=8种情况,即情况34-41

4. 3边(4种)

    考虑左上右同时出现,此时左上角和右上角有影响力,2个角有4种出现情况。所以共有4*4=16种情况。即情况16-31

5. 4边都有(1种)

    此时所有角都有影响力,4个角有16种出现情况。所以共有1*16=16种情况。即情况0-15

    

上面的解析有点绕口,大家可以参考图4理解一下。

所有情况加起来刚好47种。

分析好了所有情况,就可以列出每一种情况对应的哪4小元件

下面把我辛苦写出来的对应表给出:

等号左边为情况编号,右边为小元件表,注意小元件表里面小元件的顺序,是以

“左上,右上,左下,右下”的顺序的。

[0] = [26,27,32,33]

[1] = [4,27,32,33]

[2] = [26,5,32,33]

[3] = [4,5,32,33]

[4] = [26,27,32,11]

[5] = [4,27,32,11]

[6] = [26,5,32,11]

[7] = [4,5,32,11]

[8] = [26,27,10,33]

[9] = [4,27,10,33]

[10] = [26,5,10,33]

[11] = [4,5,10,33]

[12] = [26,27,10,11]

[13] = [4,27,10,11]

[14] = [26,5,10,11]

[15] = [4,5,10,11]

[16] = [24,25,30,31]

[17] = [24,5,30,31]

[18] = [24,25,30,11]

[19] = [24,5,30,11]

[20] = [14,15,20,21]

[21] = [14,15,20,11]

[22] = [14,15,10,21]

[23] = [14,15,10,11]

[24] = [28,29,34,35]

[25] = [28,29,10,35]

[26] = [4,29,34,35]

[27] = [4,29,10,35]

[28] = [26,27,44,45]

[29] = [4,39,44,45]

[30] = [38,5,44,45]

[31] = [4,5,44,45]

[32] = [24,29,30,35]

[33] = [14,15,44,45]

[34] = [12,13,18,19]

[35] = [12,13,18,11]

[36] = [16,17,22,23]

[37] = [16,17,10,23]

[38] = [40,41,46,47]

[39] = [4,41,46,47]

[40] = [36,37,42,43]

[41] = [36,5,42,43]

[42] = [12,17,18,23]

[43] = [12,13,42,43]

[44] = [36,41,42,47]

[45] = [16,17,46,47]

[46] = [12,17,42,47]

[47] = [12,17,42,47]

3.绘制

绘制的过程大概是以下伪代码

// 在row行,col列绘制一个自动地图元件void drawAt(int row, int col){    // 判断row和col是否越界        // 将[row,col]位置填为47号情况    // 这里可以填任意情况,只要确保[row,col]位置不为空即可    drawTileIndex(row,col,47);        // 更新[row,col]以及其周围8个格子的状态    updateTileState(row,col);    updateTileState(row-1,col-1);    updateTileState(row-1,col);    updateTileState(row-1,col+1);    updateTileState(row,col-1);    updateTileState(row,col+1);    updateTileState(row+1,col-1);    updateTileState(row+1,col);    updateTileState(row+1,col+1);}void drawTileIndex(int row, int col, int index){    // 根据index得到对应的小元件表    // 根据小元件表拼接成地图元件}void updateTileState(int row, int col){    // 如果该位置没有地图元件,则直接返回    if (!hasTileAt(row,col)) return;        // 判断其周围8个格子的状态state    // 根据判断的状态确定情况的编号index    drawTileIndex(row,col,index);}

这里最麻烦的就是“根据判断的状态确定情况的编号index

由于情况有47种之多,而且判断的过程也很纠结。这里提供我的方法,虽然也很麻烦,不过逻辑比较清晰。

核心就是用二进制表达,边角共有8个,用一个8位的char即可。首先定义如下:44角各占一位

left         = 0000 0001right        = 0000 0010top          = 0000 0100bottom       = 0000 1000left_top     = 0001 0000right_top    = 0010 0000left_bottom  = 0100 0000right_bottom = 1000 0000

那么当前44角的情况就可以上面的组合出来。

然后就可以用switch case表达47种情况,如

switch(state){case 1111 1111: /*即4边4角都有*/ index = 0; break;case 1100 1000: /*即左边和上边以及左上角都有*/ index = 38; break;...}

4.小结

没想到写这样一篇文章会这么费力,在写这家伙时,我总感觉自己的方法太笨了。我感觉会有好的方法的,所以还是坚持写下来,抛砖引玉了。

原创粉丝点击