时之虫
本帖最后由 时之虫 于 2020-3-18 23:02 编辑

大家好,我又来了。
上次在旷工茶馆水了一个帖子,简单说了说自己编写了一个简单的地图结构生成器,当时说后面会放出具体的教程和生成器。但是由于实在太懒以及太忙一直没有发,考虑到地图这里比较复杂(其实还是懒),先发一些前置的、需要了解的东西。

一.地图是怎么被存储的?
地图的文件存储在.minecraft文件夹下的\saves\存档名称\data\map_i.dat中,其中i为大于等于0的整数,这个数字就是地图的id,游戏中玩家手中的地图物品(filled_map)本身只存储了它对应的地图id,而更多的更全面的信息都在map_i.dat中。我称之为地图文件。

那么我们来剖析一下,地图文件里都有什么呢?
这里以我自己生存存档里的第一个地图文件map_0.dat为例。
地图的样子如图所示

我们用NBT Explorer打开这个地图文件,其所有NBT标签是这样的:

下面稍微介绍一下各个nbt的用处:
banners列表:存储地图上旗帜标志的信息;
frames列表:加入了地图的地图标记者(被地图标记的玩家,这里没用);
dimension整数/byte值:存储地图区域所在的维度,0为主世界,-1为下界,1为末地;
locked byte值:地图是否被锁定。如果被锁定则为1,否则为0;
scale byte值:地图被缩放的程度(0~3),无缩小则为0;
trackingPosition byte值:是否在地图上显示玩家的位置;
unlimitedTracking byte值:玩家距离地图中心过远时是否以边界上的小圆点显示;

xCenter整数:地图绘制区域的中心位置x坐标;
zCenter整数:地图绘制区域的中心位置z坐标;
colors数组:存储地图颜色的阵列。(这个就是我这篇帖子的主角)
DataVersion整数:地图创建时的版本。

二.地图内容的提取
我们打开colors,看到它的内容是这样的:

这是以16进制显示的数组内容,不是我们想要的,所以我们点击左上角的Text View,查看十进制的信息:

如图,它是一个显示的很不对齐的矩阵。不要紧,我们把ctrl+A再ctrl+C,再把它粘贴到word文档里:

然后连续应用两个替换,把所有的双空格和换行都换成英文逗号(注意是英文逗号,不要中文逗号!)



完成替换之后,它变成了这个样子:

如果你的处理正确,那么它应该替换了1024个换行和15360个双空格,这会让电脑稍微卡一会。
接下来把这些数据复制,导入到matlab里:

先打开matlab,输入如下代码:
  1. Raw=[
复制代码
然后按下ctrl+V粘贴;
不要按回车,再继续输入
  1. ];
复制代码
按下回车,我们成功创建了一个叫Raw的行向量,它存储了colors列表中所有的信息。
实际上你相当于输入了这样一条超级长的命令:


如果操作无误,工作区应该是这个样子:



三.地图是如何存储数据的
这一段的内容分析自喂鸡百科的地图物品格式条目,但深度一条部分是我自己解析得来的。

首先,地图基色
地图不同于像素画。1.12之后,地图共有52种基色,其基色id分别为0~51。具体对应的内容详见上面说的wiki条目。
这52种基色中,0是透明,实际绘制时只要沿着y轴不是只有基色为0的方块,mc就不会绘制透明色,而如果没有方块会绘制为虚空。所以这种颜色极为罕见。
这里我将地图基色称之为Base。

其次,是深度(高度差)
Minecraft为了让地图能表现出地形的高低,共设计了四种深度,代号分别为0~3。深度只取决于这个方块和它北方(z轴负方向)方块的高度差。如果它北侧的方块比它低,那么这个方块深度为2,这会让这个点显得比较亮;如果一样高,那么深度为1;如果北侧方块比它高,那么深度为0,这个方块会显示的更暗。其中第四个深度,也就是3,会让这个点变得比0还要暗。它不能在通常的地图绘制中获得,但可以被正确的显示。
这里我将深度称之为Depth。

最后,是地图色
直接存储在地图中的数据是地图色组成的128*128的矩阵,而不是基色或者深度。但是地图色可以完好的存储地图基色和深度这两个数据。
我将地图色称之为MapColor或者Color。
minecraf计算地图色具体的算法为:
每一个点的地图色
Color=4*Base+Depth       (Base≤31)
Color=4*(Base-64)+Depth  (Base≥32)
同样的,地图上每一个点的Base和Depth也可以从Color中逆向解析出来,通过它的逆运算,或者叫反函数。

四.解析地图数据的方法
继续回到matlab,刚才我们已经得到了Color矩阵变成的1*16384的行向量,现在首先要把它整理成128*128的矩阵。
在matlab中输入如下代码
  1. for u=1:1:16384
  2.         x=mod(u,128);
  3.         if x==0 x=128;end
  4.         z=ceil(u/128);
  5.         Color(z,x)=Raw(u);
  6.     end
复制代码


这样就得到了Color的128*128的矩阵,它的行是x轴正方向,列是z轴正方向,不需要转置,它和玩家看地图的方向一样。它的内容如图所示。

但是地图色并不是可以直接理解的数字,我们仍然需要从中计算出对应的深度Depth矩阵和基色Base矩阵。
深度相对比较好计算,直接对4求余数就行。

  1.     Depth=mod(Color,4);
复制代码
Depth矩阵内如如图所示。

可见Depth矩阵中最大的数也不过是2,并没有出现3,说明这是一个正常绘制的地图。一些生成器导出的地图画中会有深度3。

然后就可以计算出基色矩阵:
  1. Base=(Color-Depth)/4;
  2.             for i=1:1:128;
  3.                 for j=1:1:128;
  4.                     if Base(i,j)<0
  5.                         Base(i,j)=64+Base(i,j);
  6.                     end
  7.                 end
  8.             end
复制代码
Base矩阵的内容如图所示

通过Base矩阵的左上角部分可以看到,这一区域的基色主要是1和7。查上面给出的喂鸡百科,发现1对应的方块是草方块或粘液块;7对应的是各种植物,包括树叶方块。
再回顾Depth矩阵,可以看出这一区域的高度差变化比较频繁,且不单调。

对照地图在游戏中显示的样子:

左上角是树林,符合基色1和7频繁出现的特点。每棵树都带来高度差的变化,符合Depth矩阵显示的高度差变化频繁的特点。
这说明对数据的处理是正确的,可以用来进行下一步的操作。

至于下一步的帖子什么时候更……我也不知道。

时之虫
ruhuasiyu问我为什么用word……emmmm因为要替换掉换行的段落符号,而且看上去那个段落符号不像是普通的回车,我不知道怎么用txt的替换功能来做这个……

Nner
我一直也有这个疑问

xin_gefly
你就是java大佬,666

时之虫
xin_gefly 发表于 2020-3-24 15:56
你就是java大佬,666

emmmm实话说我没学过java

q1019035584
这个教程很不错,我应该可以学到点什么

第一页 上一页 下一页 最后一页