在minecraft里生成一个迷宫!

灵动MC又开派对了,Neboer这次带来了全新的项目——用代码在MC中生成一个漂亮的迷宫!

大家好,Neboer又来整活了。今天我们来研究一些在我能力范围之内的、不复杂的问题——在MC里做一个大型迷宫出来。 还是按照惯例先说一点事情的起因,主要的技术说明从第二节开始,最后会记录一些灵动服务器的派对实况,让我们开始吧!

灵动服务器的派对

Neboer和一些志同道合的小伙伴们,一起搞了一个叫做灵动Minecraft的MC服务器。这个服务器已经运行好久了。为了庆祝服务器六周年,Neboer和其他灵动运营团队的成员打算策划一个“六周年庆典派对”。派对上有各种各样的活动,比如烟花互射、划船大战、玩家抽奖等等。当然奖品由另一位热心的OP负责了,事后我们还寄明信片给各位玩家,我觉得非常的贴心和周到。

派对上有一个很有趣的项目,就是“迷宫探险”。玩家需要在迷宫里四处探索,寻找收集散落在角落里的物品,同时还需要保护自己的安全,因为死亡后玩家所持有的战利品都将会掉落,归其他人所有。实际在玩的时候,战利品就被设计成了凯露头(玩家kiruya的头)——可怜的猫猫()

所以最后的准备工作——设计和制造迷宫的任务,就落在了Neboer的头上。所以Neboer就简单写了点程序,在MC里生成了一个大型迷宫。文章的头图就是最终生成的迷宫的艺术照。

生成迷宫的路径

在编写可以自动生成迷宫的计算机程序之前,我们需要回答一个问题——迷宫是什么?对于最简单的情况来说,迷宫指的就是矩形迷宫——这时你们的脑海里可能瞬间浮现出早些年的各种杂志彩页里的“智力题”中的迷宫,这些迷宫可以理解成由许多个小的单元组成的,一个小单元是一个正方形,有0-4条边,两个相邻的正方形可能是连通的。这个迷宫可以理解成是在直角坐标系下,连接一些相邻整数点组成的图形。而设计迷宫的第一个步骤,就是设计迷宫里的通路。

迷宫的通路有什么特点呢?我想应该有两个,一个是连通性,指迷宫的路径应该完全连通,也就是说从路径中任何一点出发,都可以抵达路径中的任何的另外一点,迷宫不存在无法抵达的无用路径;还有一个特点就是覆盖性,指的是迷宫的路径应该覆盖迷宫的所有格子,不存在无法抵达的格子。这两点都是保证迷宫的趣味性和可玩性,最大限度的利用迷宫的每个空间,当然也为我们布置奖励提供了方便。

所以这个路径要怎么生成呢?方法已经不言而喻了:DFS!对整个迷宫的空间进行深度优先搜索,并设置规则:每探索一个格子,就对这个格子进行标记,然后继续(随机)探索与这个格子相邻但还没有被探索的格子。从一个格子探索到另一个格子后,就把这两个格子组成的短路径添加在整个通路中。最后,直到递归从顶层退出,说明所有能探索的格子都已经探索到了,这生成的便是迷宫的通路。

本来我还为自己设计的算法非常自信,不过进行一番搜索之后便傻眼了——原来迷宫生成算法还是一个很值得研究的课题。生成迷宫通路的方法有很多,去搜索“maze generation algorithm”可以得到许多结果。不过维基百科中的这个词条就显得稍微有点不专业——明明就是个dfs,竟然讲了那么多,而且好多内容都在说dfs本身的特点。我觉得这些内容可以直接用“dfs本身易于实现,但是性能较差,复杂度会随着规模的扩大而增长。”来代替。上面还介绍了很多其他生成迷宫的方法,这里我们就用最易于实现和理解的dfs算法生成迷宫了。

顺便一提,Github项目mazelib也提供了不错的对迷宫生成的支持,如果是C++库的话可能已经用上了(笑)(所以为什么要用C++写生成器)。

安排方块的位置

既然已经设计好了路径,接下来就是设计一个二维方块结构,实现这个迷宫了。

薄壁迷宫

薄壁迷宫是一种容易理解的迷宫,迷宫完全由方块组成,方块之间用线条分隔,两个方块之间有无路径可以直接用线条的有无来表示,非常方便,既容易用程序实现也方便人眼辨别。

薄壁迷宫示意图

上图就是一个薄壁迷宫。随机dfs可以生成任意规模和数量的迷宫,但是需要在壁上自己开口。这个开口的工作就不交给程序了,可以在游戏里自行完成。甚至可以装饰一下迷宫的出入口,这个课题就不在本文的讨论范围之内了。

但是薄壁迷宫并不是我们需要的最终的迷宫,因为Minecraft里的方块是有体积的,我们需要设计一个厚壁迷宫。

厚壁迷宫

厚壁迷宫示意图

生成厚壁迷宫的方法和薄壁迷宫略有不同,厚壁迷宫的墙壁厚度和路径的宽度相当,对厚壁迷宫来说,迷宫的基本单元不再是一个空心的正方形框,而是变成了一个3*3的9方格组合体。每两个相邻的组合体共享一组邻边,每两个相邻的组合体之间有没有通路是通过两个组合体交界的墙壁上正中方块是否为空来表示的。实际生成的时候,需要提前计算出实际厚壁迷宫的总长度和总宽度,然后从第1, 1格开始,以2格为单位依次向右滑动一个3*3的窗口,然后根据实际两个格之间是否存在相连的路径来决定交界3格的中心格是否为空。上图中的厚壁迷宫和上一节的薄壁迷宫的路径是一样的。

用这种方法生成的厚壁迷宫就可以作为蓝图,在mc里生成迷宫结构了。

展示迷宫图像

这些迷宫的图像是怎么生成的呢?生成这种图像的方法当然很多,但是为了方便交互以及实时生成,Neboer使用了游戏库raylib来生成这些图。好了好了,我知道你想说什么,你想说SVG/PNG直接画一定比这个更好,而且用游戏库来操作迷宫是高射炮打蚊子对吧?其实这就是另外一个故事了——Neboer最初其实并没有打算在MC中生成迷宫的,Neboer想的是制作一个双人迷宫冒险小游戏,和朋友一起玩的,但是由于各种各样的原因吧,最终改成了制作MC中迷宫结构的蓝图。这个程序是用C++写的,这个输出的迷宫还需要转换成Python可读的一种文本格式,总之非常麻烦。生成迷宫本身又不是什么需要高度性能的工作——不过也说不定,如果用Python来dfs那效率肯定感人——可不一定!Python里有大量的轮子可以高效率地解决这个需求,比如leafy,或者是我们之前提到的mazelib,不过都不重要了。

MC中的结构

接下来我们讨论怎么把生成的厚壁迷宫变成mc里的真实建筑。因为服务器就是Java版的,所以我们讨论的方法仅限于Java版本。

Minecraft中的结构

Minecraft是一个非常有趣的游戏,而且一直在不停的更新。我写这篇文章的时候,最新的版本是1.19.2。Minecraft是一个沙盒类游戏,它提供了一个模拟世界的方法。所以为了更好的模拟世界,mc必须引入各种游戏的机制。本文中使用的structure便是其中之一。

Minecraft的structure代表mc的建筑结构。MC有许多建筑结构,比如要塞、沉船、丛林府邸等等。这些结构是内置的结构,你当然也可以在mc中添加自己的建筑结构。添加自己的建筑结构的最主要方法就是利用一个叫做结构方块的工具。使用结构方块,可以快速的加载和保存结构,持久化你在mc中的建筑。

使用结构方块保存的建筑会被存储在一个文件中。在Java版里这个文件的位置在.minecraft\saves\xxxx\generated\minecraft\structures\xxx.nbt,在服务器中这个文件保存在world下,文件名就是你命名的结构名称,后面加上"nbt"的扩展名。你可以随时加载自己保存的结构,你甚至可以从其他的地方拷贝结构,加载进自己的世界里。即使在游戏运行过程中也可以进行拷贝操作,非常方便。

所以,我们的目标明确了:制作一个nbt文件,描述我们刚刚生成的迷宫。

NBT文件

众所周知,如果想要持久化一个程序运行中产生的内存信息,就必须将这个信息序列化后存储进硬盘,在需要使用的时候再反序列化读入,MC当然也不例外。Minecraft存储二进制数据使用的是一种叫做“nbt”的数据格式。根据百科的介绍,NBT又叫named binary tag,是Minecraft用于存储二进制数据的方法。NBT数据是一种树形结构,和JSON有点相似,但它的编码方法是二进制的,人类无法直接阅读。

MC中使用nbt存储的数据多种多样,包括玩家信息和地图数据等等。当然,mc中的建筑结构也是nbt保存的,这就给我们制作自己的结构提供了理论基础。那么,mc是怎么用nbt存储结构的呢?

直接看wiki之类的都有点雾里看花的感觉,并不直观。最简单的方法当然是拆开一个mc自己生成的结构的nbt文件看看里面有什么。好,那么下个问题来了,nbt并不是很通用的文件格式,我应该怎么读取nbt里的数据呢?答案也是很直接:你需要安装一个nbt查看器。

nbt查看器在mc历史上可能出现过许多,但是现在Neboer最推荐的就是NBTexplorer,它可以把结构化的信息清晰的展示出来,供你阅读和修改,是最适合查看nbt的整体结构和细节数据的软件。同时NBTexplorer也是开源软件,非常社区的mc服主之必备工具。

软件的用法也非常简单,只需要下载安装,把你要查看的文件拖动到窗口里,它就可以显示其中的内容了。

nbtexplorer截图

结构的储存

好,关键的问题来了,结构是怎么储存在nbt文件里的呢?结合示例nbt文件的内容和wiki提供的信息,很容易弄懂这个内容——非常简单和直观。

根据Structure Block file format wiki中提供的信息,我们可以看到描述结构数据中方块信息的关键,在于“palette”和“blocks”这两个属性上。前者起到“调色板”的作用,后者记录真正的方块数据。其实仔细一想,这种存储方法也非常合适,因为minecraft中方块只有那么多种,但是block却是数量庞大,所以用一个“调色板”来描述方块,然后再用一个结构去描述每个位置上是调色板里的哪个方块,无疑是非常适合的。

palette是一个数组,里面的每个元素都是一类方块。每个元素都是一个由name和state两个部分组成。name就是block的名字,也叫做“blockID”,比如minecraft:grass_block。而state则是方块的state数据,描述了方块的metadata,比如对于一个原木来说,它朝向哪个方向就存储在state信息中。

blocks也是一个数组,里面的每个元素都是独立的block。每个block有3个属性:state、pos和nbt。state是这个block所对应的方块在palette中的index,比如0,1,2...;pos就是很正常的xyz坐标,描述了这个方块在空间中的位置;而nbt属性是可选的,描述了方块在掉落后对应的掉落物entity的数据,我们一般用不到这个,所以可以留空。

至此,我们已经了解了关于nbt以及结构存储的基础内容,可以根据这个来在mc里生成迷宫了。

制作迷宫结构的nbt文件

目标已经十分明确了,我们需要编写一个程序,根据之前输出的厚壁迷宫生成一个三维的迷宫结构,然后在minecraft里部署这个结构即可。听起来确实很简单,实际也不难,但是需要解决一些问题。

NBT文件的读写

如果写一个程序自动读写nbt,那NBTexplorer就派不上用场了,我们需要一个带编程接口的工具,一个程序库或者什么别的东西,来让我们对nbt数据的操作更加容易。这无疑是整个流程中最困难的部分了,因为合适的工具永远都那么难找。NBT文件是MC存储信息的专用格式,关于nbt编程是一个比较小众的需求,当然存在一些可以直接操作nbt信息的库,但是遗憾的是大多数库都已经年久失修,不再维护了。在wiki.vg中关于nbt的介绍页面里我们可以看到很多关于nbt的library,我们好奇的点开“Python”,结果:twoolie/NBT这个仓库已经有好久没有更新了,这真令人扫兴。难道我们要用C++来写程序吗?

不需要的!我们为什么一定要操作一个库呢?既然我们已经决定最合适的语言就是Python了,我们当然可以用工具把nbt转换成Python可读的形式,也可以找工具把Python中的结构变成nbt,我真是天才!(???)所以,我们的目光一下子聚焦到了下面的一个工具上:nbt2json

nbt2json是一个读写NBT数据的绝佳工具:它可以把nbt信息一对一地转换成一种它定义的JSON,还可以把符合它的标准的JSON转换成nbt信息,非常方便。软件本身更新积极,golang保证了性能,使用的MIT协议也很自由,这个软件给我的第一印象很不错,就决定是它了!

nbt2json

我们已经决定了使用nbt2json来间接读写nbt信息。那么接下来我们需要弄懂两个问题: 1. nbt2json的命令行用法 2. nbt2json操作的json文件格式

针对第一个问题,直接阅读nbt2json的Github README文件就可以获得详细的信息。因为直接阅读Github上的信息可能比较困难,所以Neboer在这里详细说一下这个软件的用法。

单刀直入,直接提供最后的命令:

json->nbt nbt2json.exe -b -r -z -i xxx.json -o xxx.nbt

nbt->json nbt2json.exe -b -i xxx.nbt -o xxx.json

解释一下各个参数:

  1. -i 指定输入的文件路径
  2. -o 指定输出的文件路径
  3. -b “大端序”,这个是专门为java版minecraft准备的,如果你想为基岩版生成nbt文件,则不加这个选项。
  4. -r “反向转换”,如果你想从json转换成nbt,则需要加这个选项,反之则不需要加。
  5. -z “gzip压缩”,添加这个选项,输出的nbt文件就会经过gzip压缩。注意:在1.19.1上(其他版本未测),必须开启压缩,否则nbt结构不能正常读取。

现在你已经掌握了nbt2json的用法,接下来让我们看看nbt2json输出的文件是什么样子的。

还是,单刀直入,直接上最终的文件内容。我在minecraft里创建了一个2*2*2的结构,保存为structure后用nbt2json转换成json是这样子的:

{"name": "Named Binary Tag to JSON","version": "0.4.0","nbt2JsonUrl": "https://github.com/midnightfreddie/nbt2json","conversionTime": "2022-09-03T14:04:01+08:00","nbt": 
	[
	{"tagType": 10,"name": "","value": 
		[
			{"tagType": 9,"name": "size","value": {"tagListType": 3,"list": [2,2,2]}},
			{"tagType": 9,"name": "entities","value": {"tagListType": 0,"list": null}},
			{"tagType": 9,"name": "blocks","value": {"tagListType": 10,"list": 
				[
					[{"tagType": 9,"name": "pos","value": {"tagListType": 3,"list": [0,0,0]}},{"tagType": 3,"name": "state","value": 0}],
					[{"tagType": 9,"name": "pos","value": {"tagListType": 3,"list": [0,0,1]}},{"tagType": 3,"name": "state","value": 0}],
					[{"tagType": 9,"name": "pos","value": {"tagListType": 3,"list": [1,0,0]}},{"tagType": 3,"name": "state","value": 0}],
					[{"tagType": 9,"name": "pos","value": {"tagListType": 3,"list": [0,1,1]}},{"tagType": 3,"name": "state","value": 0}],
					[{"tagType": 9,"name": "pos","value": {"tagListType": 3,"list": [1,0,1]}},{"tagType": 3,"name": "state","value": 1}],
					[{"tagType": 9,"name": "pos","value": {"tagListType": 3,"list": [0,1,0]}},{"tagType": 3,"name": "state","value": 1}],
					[{"tagType": 9,"name": "pos","value": {"tagListType": 3,"list": [1,1,0]}},{"tagType": 3,"name": "state","value": 1}],
					[{"tagType": 9,"name": "pos","value": {"tagListType": 3,"list": [1,1,1]}},{"tagType": 3,"name": "state","value": 1}]
				]
			}},
			{"tagType": 9,"name": "palette","value": {"tagListType": 10,"list": 
				[
					[{"tagType": 10,"name": "Properties","value": [{"tagType": 8,"name": "snowy","value": "false"}]},{"tagType": 8,"name": "Name","value": "minecraft:grass_block"}],
					[{"tagType": 8,"name": "Name","value": "minecraft:air"}]
				]
			}},
			{"tagType": 3,"name": "DataVersion","value": 3117}
		]
	}
	]
}

JSON和NBT是几乎完全不同的两个数据存储格式,nbt的json的数据类型完全不能一一对应,所以为了能用json精确的表达nbt信息,就必须给每个nbt的基本数据单元指定一个“类型”属性。这样一来,为了唯一的确定nbt中的一个数据,就需要同时知道它的json对应以及它的类型这两个信息,后文我们会提到nbt2json生成的json数据是如何保存这些信息的。这种存储方法使整个json的结构变得臃肿,对人类可读性有不小的影响,但好处就是精确描述了nbt的内容,使其与json的互相转换变得可能。注意为了阐述清楚,类型两个词如果被用于表示一个nbt的值所对应的两个json中的存储部分时,会被加粗。我们用“Tag”来表示nbt的基本数据单元。

这些tagType都是什么含义?可以参考wiki.vg上的介绍,1-6属于是基本数据类型,比如整数和字节,这些基本类型都是定长的。类型7是字符数组,类型8是字符串,以上这些Tag的种类都可以称为“值类型”,这些类型完全可以和json中的数据类型互相转换,是最基本的组成部分。

剩下的类型里,主要介绍9和10。类型9是列表,类型10是Compound对象,这两个类型是“结构类型”,它们才是把nbt转换成json真正需要考虑的问题,nbt2json输出的json文件使用完全不同的json策略来存储这两个类型。注意,下面关于Tag的存储方法的详细介绍与主题无关,不理解这些内容一样可以生成迷宫的nbt结构。如果你不想理解这些内容,完全可以跳过,对理解不会有任何影响。

列表存储了一系列具有相同类型的Tag,这些Tag的类型存储在列表标签对象的value属性中的"tagListType"属性中,而列表真正的内容可以通过同级的list属性(键)对应的值获取到:list的值是一个json列表,里面的每个元素都是一个,而这个对应的Tag的类型由前文中的tagListType属性的值来决定,这样一来列表中存储的所有Tag都被完全定义了。

Compound是一个特殊的类型,因为整个nbt标签的本质上就是一个巨大的Compound类型的Tag。Compound对象的value属性是一个json列表,这个列表中的每个元素都有tagType、name和value属性,描述了name名字所对应的标签的值和类型,这样一来name名字所对应的Tag就被完全定义了。

为了帮助大家更好的理解这种数据的存储方法,我在这里举一小段例子,这段例子在后面的“编写脚本生成json文件”部分中也是有用的,所以用它做例子再合适不过。

[{"tagType":9,"name":"pos","value":{"tagListType":3,"list":[0,1,0]}},{"tagType":3,"name":"state","value":0}]

这个结构出现在一个list里,说明它是一个。它所属的Tag的类型是10,说明这个Tag已经被完全定义了,这个Tag表示了一个位置上存在一个方块,这个方块的表示方法是利用它在palette中的index来描述的。这个Compound Tag有两个命名子元素,它们中的一个叫做pos,一个叫做state,pos是一 个列表,里面的值都是整数,用来表示xyz坐标;state只是一个整数,用来描述index信息。这个值所在的列表里记录着许多同类值的信息,这些信息共同表达了mc structure中的方块数据,每个方块的位置以及对应的platte index。

在mc中安放生成的结构

既然已经弄懂了json2nbt的json文件格式,接下来的事情就非常简单了,只需要编写脚本生成json文件就可以了。首先我们在mc中搭建一个简单的结构做为示例,然后将这个结构保存为nbt数据,再用nbt2json转换成json格式。这里可以简化一下:我们最终希望用石头来生成这个迷宫,所以就做一个只有石头的示例结构,这样就不需要自己手动去配置palette了。

编写一个Python脚本,读取json文件里的内容,找到其中存储方块信息的位置,先清除所有方块,然后添加自己的方块。由于调色板已经确定了,所以每个方块只需要提供一个state和pos就可以存储,非常方便。把我们设计的厚壁迷宫里所有的方块遍历一遍,然后对每个方块,添加3层高度的石头,对每个不存在方块的位置,添加三层空气,于是就大功告成了。

json写好了,那么剩下的工作就是把json转换成nbt,然后在mc里安放这个结构。命令在上文中也已经给出了,就是nbt2json.exe -b -r -z -i xxx.json -o xxx.nbt。然后,我们把这个nbt文件拷贝到.minecraft\saves\xxxx\generated\minecraft\structures下,注意nbt的文件命名,推荐全用英文字母小写,防止minecraft无法识别。

你都不需要重新启动游戏服务器,你可以直接用结构方块或者使用命令:/place template minecraft:xxx,其中xxx就是你给nbt文件起的名字。

最终生成的迷宫长这个样子:

如果想要实现更复杂的效果,当然非常容易,比如文章开头更漂亮的迷宫,它的墙壁是由“盛开的杜鹃树树叶”、“杜鹃树树叶”随机组成的,它的底部是由“橡木原木”组成的,每到一个突出的部分,它都会放一排荧石以提供照明,在视觉效果上非常不错,很适合用来做景观。

至此,我们已经成功的完成了在minecraft里生成迷宫结构的目标,搞定,收工。

给你的“大作”拍张照片

花园迷宫是一种很热门的游览景观,在Minecraft里生成的迷宫——也可以具有很高的鉴赏性和艺术价值。那怎么在Minecraft里拍出漂亮的照片呢?你首先需要安装一个可以使用光影包的Minecraft客户端,在这里我就很推荐fabric+optfine的组合。如果你觉得手动配置依赖非常麻烦,你可能需要一个优秀的Minecraft启动器——Neboer在使用的启动器是PCL2,使用启动器可以根据个人需要快速安装好所需的全部依赖,一键运行游戏,非常方便快捷。

好,然后就到重点了。应该选择哪个光影包呢?虽说我们主要是用来截图,不需要很高的帧率,但是如果你希望在Minecraft里随时欣赏到这样的美丽景色,那一款高运行速度、高画面质量的光影包它不香吗?如果你和Neboer一样,电脑上安装了一张入门级的独立显卡,那就完全足够了。Neboer非常推荐大家尝试一下Sildur's Vibrant Shaders,这个光影包的Medium版本在Neboer的笔记本上可以以很快的速度运行,平均性能可以达到45fps,已经可以说是完全足够了。并且这个光影包提供了丰富的配置功能,你可以自由定义从光照到雾效的一切,最终呈现的画面也是非常绝伦,早上朝阳升起,晚上彩霞漫天,这些场景都被精细地呈现了出来,美轮美奂,让人流连忘返。当然,你可以根据自己的电脑性能选择最适合自己的光影,这一点不再赘述。

选择一个合适的时间,站在一个合适的角度,把视角调整到刚刚好的位置,然后按下F1键隐藏HUD,按下F2截图。然后再按F1打开HUD,按T打开聊天栏,在聊天栏里点击最新的消息:已保存屏幕截图,就可以查看到自己刚刚截得的屏幕截图了。

写在后面

Minecraft,一个传奇的游戏,大概是21世纪最开放的游戏了,没有之一。整个Minecraft相关的全部内容,几乎完全由众多的社区内爱好者所创造,官方在这里起到的最大的作用似乎只是提供了一份游戏的源代码——并且这份代码写得还不怎么样,需要社区来专门维护一些编程接口,才能让各种mod可以方便的编写和运行。Minecraft官方提供的服务器实现实在是太过拉跨,社区又围绕这个服务器做了很多工作,制作了各种性能优异的服务器软件,使得你和你的小伙伴们能够用足够低的性能畅玩联机MC,并且社区还为这些服务器提供了海量的插件支持,这些插件也是由来自社区的热心用户开发的……所以Minecraft虽然并不是一个开源游戏,更不算是个自由游戏,但它却创造了游戏历史上的一个奇迹——原来一个游戏的源代码还是可以公开的,并且可以授权社区来使用,一个游戏的游戏内容更可以几乎完全来自社区,而官方只需要赚名声和买断制的钱以及提供一个所谓“正版账号”就可以了,这无疑不是很好的开始,虽然Neboer希望未来有更多的自由和开源游戏出现,但是考虑到全人类还没有解放,游戏开发者用自己制作的游戏来赚金币的行为当然是没问题的,希望开发者们能够全心全意地为自己的玩家和用户服务,创造更多更好的软件、游戏来造福人类,不要太拘泥于自己赚了多少钱啊😂

说回生成迷宫的这个问题:如果不是fandom Minecraft wiki、wiki.vg的优秀文档,如果不是来自Github上的优秀项目,如果不是活跃的灵动Minecraft游戏社区,这份工作可能就无法完成了。Minecraft真正迷人的地方就在于你可以自由选择你在这个游戏中的角色——是开发者,是玩家,是管理员,是服主,是活动策划,是厨师,是红石大佬,是生存专家,是旅行者,是摄影师,是建筑党,还是只是单纯的自闭游戏人?当你决定游玩Minecraft的那一刻,你已经在心中给自己找好了位置,每个人都可以按照自己的意愿来进行游戏,前提是遵守社区和MC的规则,这就让整个游戏变得非常有意义——它帮助你成就了你自己。就比如说Neboer我,我在灵动MC里做OP,帮助玩家解决各种各样的问题,策划各种各样的活动,尝试用自己微薄的力量保障和改善服务器玩家的游戏体验,同时积极想办法让服务器热闹起来,希望能够吸引到更多志同道合的伙伴,这就是我给自己在游戏中的定位。感谢Minecraft让Neboer能有机会为大家服务,同时也欢迎大家积极加入灵动MC,为我们服务器的社会主义建设添砖加瓦!

Neboer对Minecraft整个游戏的理解也随着时间的增长和整活的增加越来越深入,希望这个趋势可以持续下去,虽然我知道自己没什么Jvav水平(?),但是也在努力学习!希望可以成为独当一面的开发者,和大家一起进步,共勉。

感谢你们的阅读,Neboer好久不发文章了,希望大家不要觉得Neboer不见了(悲),可以去NerChat!上找我啊()