十五、世界生成器


如果你仔细观察,会发现有个叫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来生成一个最最简单的地形,比如说钻石大陆。

在你的主类中重写:

@Override
public 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;
@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服务器插件开发教程(十四)消息和命令补全器
下一篇:我的世界Bukkit服务器插件开发教程(实战一)起床战争

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

  1. 我的世界Bukkit服务器插件开发教程(五)方块Blocks

    五.方块Blocks 我怕不更新会被骂,所以更了. 坐标系 小伙纸,求一下∑i=0∞i2\sum_{i=0}^\infty i^2∑i=0∞​i2(bushi 是的,你没猜错,我们要涉及到史上最NB超 ...

  2. 我的世界Bukkit服务器插件开发教程(十一)粒子、药水效果与音效

    十一.粒子.药水效果与音效 太简单了吧! 1.粒子效果 1.1.基本操作 播放粒子,首先需要确定粒子需要播放的位置.需要注意,我们一次只能播放一个粒子,如果需要播放多个粒子的话--也不麻烦. 播放粒子 ...

  3. 我的世界Bukkit服务器插件开发教程(十三)资源包与玩家资料

    十三.资源包与玩家资料 1.资源包(Resource Pack) 早期的 Minecraft 并没有资源包一说,而是被叫做材质包.有些服务器为了让玩家拥有更好的游戏体验,一般会在自己特制的客户端中存放 ...

  4. 我的世界Bukkit服务器插件开发教程(七)计分板

    七.计分板 写在前面 对于计分板,相信大部分人会认为这是一个计分的玩意.但不然,它的用处远远不止计分那样简单,计分板在服务器中起着重要的作用,在大部分插件中都有着它的身影. 另注:本章节有大量干货,请 ...

  5. 我的世界Bukkit服务器插件开发教程(一)第一个插件

    一.第一个插件 这一章节,我们会开发第一个属于自己的插件 在这之前,我们还需了解plugin.yml和config.yml,并大概了解插件的原理 plugin.yml 这个文件必须存在,否则读不出来. ...

  6. 我的世界Bukkit服务器插件开发教程(八)进度条与自定义合成表

    八.进度条与自定义合成表 写在前面 本来我想将进度放入本章节当中,但无奈进度牵扯到json文件,估计我又要叽叽喳喳讲一堆json,出于人最基本的性格,这章节暂且不讲. 进度条 说起进度条,打个最简单的 ...

  7. 我的世界bukkit服务器开发教程第一章——开发环境

    提示:如果图片看不见或者文字有乱码,请到我的网站:www.xiaoguaimao.cn 前言: 本教程共分为两个部分: 1.服务器插件开发 2.服务器专属启动器开发 如果你学过编程,那你会很轻松,如果 ...

  8. 我的世界虚拟服务器架设,我的世界Minecraft服务器架设教程_服务器怎么建立

    [导读]我的世界Minecraft服务器架设教程. 下面给大家带来<我的世界>Minecraft服务器的架设教程,让大家对我的世界服务器建立有一个不错的认识,从怎么建立,到怎么维护做一个系 ...

  9. 【Visual C++】游戏开发笔记四十七 浅墨DirectX教程十五 翱翔于三维世界 摄像机的实现

    分享一下我老师大神的人工智能教程.零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow 本系列文章由zhm ...

最新文章

  1. MacBook Air写代码真香!包邮送一台!
  2. 计算机英语基础课程论文,计算机专业英语课程教学论文
  3. C/C++浮点数在内存中的存储方式
  4. 二值图片连续区域检测
  5. Java集合和泛型练习及面试题——博客园:师妹开讲啦
  6. 简记用ArcGIS处理某项目需求中数据的步骤
  7. 文件上传java逻辑_Java 文件上传 实例
  8. form表单属性名相同java_java – 将表单字段注入具有相同属性名称的多个bean中
  9. php cksql,金蝶KSQL规范
  10. C# 图片处理之:旋转图片任意角度
  11. python中的itemgetter函数
  12. Vue.js的IoC容器模式探索
  13. markdown 书写代码
  14. mac系统历史版本汇总_苹果发布会 WWDC20 主要更新汇总
  15. IDEA使用教程汇总
  16. 51单片机之一套完整的实验流程
  17. 深度学习mindspore --- rescale(rescale, shift)
  18. [技术讨论]从ERP免费开始到做人做事的讨论
  19. 科学的失控与范式的超越——《侏罗纪公园》读后感
  20. 解决viewer.js预览PDF文件 无法展示PDF水印的问题

热门文章

  1. Tekla二次开发的各个模块介绍
  2. 2021年kali-linux壁纸[超清]
  3. loj 6197. 法克 最小路径覆盖 好题
  4. 软件设计师备考笔记(二)计算机组成与体系结构
  5. 网盘系统|基于SpringBoot的网盘系统的设计与实现
  6. Android在ImageView上直接显示网络图片
  7. Fedora17硬盘安装
  8. PyCharm 添加 外键的时候 报错 TypeError:__init__()missing 1 required positional argument:‘on_delete‘
  9. ZYNQ使用10G Ethernet Subsystem万兆网光通信
  10. 江南大学计算机专业考研压分吗,考研专业课压分,不保护一志愿考生?20考研的我该如何避坑?...