失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 我的世界Bukkit服务器插件开发教程(十五)世界生成器

我的世界Bukkit服务器插件开发教程(十五)世界生成器

时间:2020-03-29 20:51:04

相关推荐

我的世界Bukkit服务器插件开发教程(十五)世界生成器

十五、世界生成器

如果你仔细观察,会发现有个叫saves的文件夹,这个文件夹是用来存放存档的,即我们平常说的世界

显然,服务器承担了创建世界、加载世界的任务。Bukkit 中也有专门生成世界的生成器,所以我们可以继承这些类写一个世界生成器。

世界生成器有很多种,但在这之前,我们需要了解一下生成的原理。

0.原理

Minecraft 地形生成是分为两部分:GenerationPopulationGeneration部分负责生成最基本的地形,Population部分是负责在这个地形上加装饰(花、草、树等等)。

区块是世界中长和宽为 161616,高为 256256256 的部分。两个部分都是以区块作为最基本的单位,Generation再使用柏林噪声(Perlin noise)算法生成基础地形。

柏林噪声算法有很多版本,从 1980 年始的原始算法,到如今的改进算法,各种各样呢。柏林噪声实质上是一个函数。省流就是,传入之间相差不大的参数,最后返回一个随机数,

public double perlin(double x,double y,double z);

如上,传入三个参数,最后返回一个 [0,1][0,1][0,1] 区间的一个浮点数,即柏林噪声值。传入相同的参数,最后得到的柏林噪声值也相同,这也就是为什么 Minecraft 中相同的种子总是生成相同的地形。

除此之外还有分形噪声,分形噪声将不同频率的噪声函数合并为一个更为复杂的噪声函数。

但这个函数图像是粗略的,我们可以将某个噪声值进行放大再生成,得到的地形就精细的得多了。

柏林噪声算法不是这一章的重要部分,如果你有兴趣,可以自行阅读其他文章。

1.装饰

在世界开始加载时会触发一个WorldInit事件,我们可以监听这一事件,然后来完成一次“点缀”。

public class DiamondGenerator implements Listener {@EventHandlerpublic void onWorldInit(WorldInitEvent e) {e.getWorld().getPopulators().add(new DiamondPopulator());}}

前面说过,Population就是给原本的地形上加点装饰,当然加的装饰不止一种,我们可以自己添加一种,重点在于实现DiamondPopulator类。

public class DiamondPopulator extends BlockPopulator {@Overridepublic void populate(World world, Random random, Chunk source) {}}

首先判断我们需要添加什么,比如随处可见的钻石块,假设我们一个区块需要5个钻石块,对于每一个钻石块而言,只需确定它的横纵坐标,遍历一遍所有 yyy 轴坐标判断是否符合我们要的条件即可。

int count = 5;for(int i = 0; i <= 5; i++) {//随机确定x,z坐标int x = random.nextInt(16);int z = random.nextInt(16);//遍历我们所要的y坐标for(int y = 128; y >= 0; y--) {//我们的钻石块要在天空上,即空气方块上if(source.getBlock(x, y, z).getType() == Material.AIR) {world.getBlockAt(x, y, z).setType(Material.DIAMOND_BLOCK);}}}

运用上面的例子你就可以加一些小型建筑来丰富你的插件。

只要你能够理解上面的Populator就差不多行了,但差不多达不到完美。实际我们一般很少很少会用到世界生成器,而且生成器较为复杂,需要一些基础。

2.地形

2.1.简单区块地形生成

前面说过,Generation以区块作为基本单位生成,所以我们可以用ChunkGenerator来生成一个最最简单的地形,比如说钻石大陆。

在你的主类中重写:

@Overridepublic ChunkGenerator getDefaultWorldGenerator(String worldName, String id) {return new DiamondGenerator();}

老规矩,重在实现DiamondGenerator

public class DiamondGenerator extends ChunkGenerator {@Overridepublic ChunkData generateChunkData(World world, Random random, int x, int z, BiomeGrid biome) {ChunkData chunkData = createChunkData(world);chunkData.setRegion(0, 0, 0, 16, 1, 16, Material.BEDROCK);chunkData.setRegion(0, 1, 0, 16, 16, 16, Material.DIAMOND_BLOCK);return chunkData;}}

setRegion方法是用于填充方块,传入两个三维坐标,然后对选中的部分进行填充。

如上,对于每个区块,我们只需将它填充为钻石块就行了。对于每个区块的最底层,我们将它填充为基岩就好了。

我们只是用一个小区块,然后用它拼合成一个大世界。这是最简单的地形生成。

2.2.噪声地形生成

接着就来到了本章的重头戏,对于噪声算法,我们提到了柏林(Perlin)噪声,但还有一个 2.0 版本——Simplex,是对 Perlin算法进行的优化。

private SimplexOctaveGenerator simplex;@Overridepublic ChunkData generateChunkData(World world, Random random, int x, int z, BiomeGrid biome) {ChunkData chunkData = createChunkData(world);if(simplex == null) {//传入种子simplex = new SimplexOctaveGenerator(world.getSeed(), 1);simplex.setScale(0.001D);}return chunkData;}

SimplexOctaveGenerator就是我们说的分形噪声。同时也可以用setScale调整噪声函数的频率,可以调的小一些,地形更平坦,或者调大一些,地形更陡峭,甚至完全混乱。

这次我们不需要随机取一个方块的横纵坐标了,我们只需改变一个方块的高度既可,因为最终形成的地形是跌宕起伏的,不可能有个大窟窿吧。

for(int i = 0; i < 16; i++) {for(int j = 0; j < 16; j++) {//设置x,z坐标int xx = x * 16 + i;int zz = z * 16 + j;//获取噪声值double noise = simplex.noise(xx, zz, 0.3D, 0.4D);//将噪声值放大int y = (int) (noise * 35D + 150D);//底层基岩chunkData.setBlock(x, 0, z, Material.BEDROCK);//1~y层全为钻石块for(int k = 1; k <= y; k++) {chunkData.setBlock(x, y, z, Material.DIAMOND_BLOCK);}}}

以上就是Generation部分,至于Populartion部分,我们可以用getDefaultPopulators方法:

@Overridepublic List<BlockPopulator> getDefaultPopulators(World world) {return ImmutableList.of(....);//你的装饰生成器}

3.结束了?

编者一开始想重点讲噪声算法,但是我觉得由于低龄化太过严重,噪声算法过于深奥,不利理解,并且现在随便搜搜都一大堆,也就偷个懒了。原定分为上下稿,现在硬是压缩成一稿,属实不易

基本上所有要讲的都讲完了,并且还额外扩充了一些。

当然现在不会草草了结这个系列的,应粉丝要求,还有实战呢(恼)。

上一篇:我的世界Bukkit服务器插件开发教程(十四)消息和命令补全器

下一篇:我的世界Bukkit服务器插件开发教程(实战一)起床战争

如果觉得《我的世界Bukkit服务器插件开发教程(十五)世界生成器》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。