我的世界Bukkit服务器插件开发教程(十五)世界生成器
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
十五、世界生成器(上)
如果你仔细观察会发现有个叫saves
的文件夹这个文件夹是用来存放存档的即我们平常说的世界。
显然服务器承担了创建世界、加载世界的任务。Bukkit 中也有专门生成世界的生成器所以我们可以自己写一个世界生成器。
世界生成器有很多种但在这之前我们需要了解一下生成的原理。
0.原理
Minecraft 地形生成是分为两部分Generation
和Population
。Generation
部分负责生成最基本的地形Population
部分是负责在这个地形上加装饰花、草、树等等。
区块是世界中长和宽为
16
16
16高为
256
256
256 的部分。两个部分都是以区块作为最基本的单位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 {
@EventHandler
public void onWorldInit(WorldInitEvent e) {
e.getWorld().getPopulators().add(new DiamondPopulator());
}
}
前面说过Population
就是给原本的地形上加点装饰当然加的装饰不止一种我们可以自己添加一种重点在于实现DiamondPopulator
类。
public class DiamondPopulator extends BlockPopulator {
@Override
public void populate(World world, Random random, Chunk source) {
}
}
首先判断我们需要添加什么比如随处可见的钻石块假设我们一个区块需要5个钻石块对于每一个钻石块而言只需确定它的横纵坐标遍历一遍所有 y y y 轴坐标判断是否符合我们要的条件即可。
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
来生成一个最最简单的地形比如说钻石大陆。
在你的主类中重写
@Override
public ChunkGenerator getDefaultWorldGenerator(String worldName, String id) {
return new DiamondGenerator();
}
老规矩重在实现DiamondGenerator
public class DiamondGenerator extends ChunkGenerator {
@Override
public 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;
}
}
确实这么简单对于每个区块我们只需将它填充为钻石块就行了。对于每个区块的最底层我们将它填充为基岩就好了。
我们只是用一个小区块然后用它拼合成一个大世界。这是最简单的地形生成。
2.2.噪声地形生成
接着就来到了本章的重头戏对于噪声算法我们提到了柏林(Perlin)噪声但还有一个 2.0 版本——Simplex是对 Perlin算法进行的优化。
private SimplexOctaveGenerator simplex;
@Override
public 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
方法
@Override
public List<BlockPopulator> getDefaultPopulators(World world) {
return ImmutableList.of(....);//你的装饰生成器
}
3.结束了
编者一开始想重点讲噪声算法但是我觉得由于低龄化太过严重噪声算法过于深奥不利理解并且现在随便搜搜都一大堆也就偷个懒了。原定分为上下稿现在硬是压缩成一稿属实不易。
基本上所有要讲的都讲完了并且还额外扩充了一些。
当然现在不会草草了结这个系列的应粉丝要求还有三篇实战呢恼。
最后
没有人会将 Bukkit 讲得全面、透彻。过去如此现在如此将来也如此唯有 API文档 涵盖一切内容。
上一篇我的世界Bukkit服务器插件开发教程十四消息和命令补全器
下一篇我的世界Bukkit服务器插件开发教程实战一BedWars