世界生成器
Minecraft 世界生成是分阶段的复杂过程。Bukkit 提供了在生成阶段介入的 API,让你往世界里塞自定义内容(比如草地上撒钻石块)。
世界生成的两个阶段
世界以**区块(Chunk)**为单位生成。每个区块经历两个阶段:
Generation 阶段(绘制地形)
- 取该 Chunk 所有生物群系,按群系绘制基本地形(以高度 63 为水平面,先只有空气、水、石头)
- 在高度 0~5 生成基岩,给各群系加特有方块(平原加草和泥土、沙漠加沙)
- 生成跨区块的大型地形(洞穴、村庄、矿井)
- 收尾准备
Population 阶段(点缀内容)
Generation 完成后世界结构定型,但还缺"点缀":
- 完善实体、生成方块实体(箱子等)
- 生成小型地形(水坑、岩浆池、地牢)
- 按规则生成矿物
- 加地面点缀(甘蔗、荷叶、蘑菇、树木)和基础生物
两个阶段结束后,Chunk 数据存储并显示。
介入 Population 阶段
世界初始化时触发 WorldInitEvent。监听它,给世界加 BlockPopulator:
java
public class WorldListener implements Listener {
@EventHandler
public void onWorldInit(WorldInitEvent e) {
if (e.getWorld().getName().equals("world")) {
e.getWorld().getPopulators().add(new DiamondPopulator());
}
}
}BlockPopulator
BlockPopulator 在每个区块的 Population 阶段被调用,可往区块里塞方块:
java
class DiamondPopulator extends BlockPopulator {
@Override
public void populate(World world, Random random, Chunk chunk) {
// 在这个区块的草方块上随机撒钻石块
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
int y = world.getHighestBlockYAt(
chunk.getX() * 16 + x,
chunk.getZ() * 16 + z
);
Block b = chunk.getBlock(x, y - 1, z);
if (b.getType() == Material.GRASS_BLOCK && random.nextInt(10) == 0) {
b.setType(Material.DIAMOND_BLOCK);
}
}
}
}
}只对新区块生效
BlockPopulator 只在区块首次生成时跑。已经生成过的区块不会重新 populate——要改已存在区块得用别的方式(如命令或定时任务)。
注册监听
别忘了在 onEnable 注册 WorldListener:
java
@Override
public void onEnable() {
Bukkit.getPluginManager().registerEvents(new WorldListener(), this);
}ChunkGenerator 完全自定义生成
若要彻底替换世界的地形生成逻辑(如做一个平坦世界、空岛世界),用 ChunkGenerator:
java
public class FlatGenerator extends ChunkGenerator {
@Override
public ChunkData generateChunkData(World world, Random random, int chunkX, int chunkZ, BiomeGrid biome) {
ChunkData data = createChunkData(world);
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
data.setBlock(x, 0, z, Material.BEDROCK);
data.setBlock(x, 1, z, Material.STONE);
data.setBlock(x, 2, z, Material.GRASS_BLOCK);
}
}
return data;
}
}创建世界时指定:
java
WorldCreator wc = new WorldCreator("myworld");
wc.generator(new FlatGenerator());
World world = wc.createWorld();用哪个?
- 只想往原版世界加点料(撒矿、加结构)→
BlockPopulator - 想完全控制地形(自定义世界类型)→
ChunkGenerator
注意事项
- 性能敏感:生成器每个区块都跑,逻辑要轻,避免重计算
- 坐标换算:
chunk.getX()*16 + x才是世界坐标,别直接用区块内坐标 - BiomeGrid:
generateChunkData的BiomeGrid参数可设置每个格子的生物群系 Material.GRASS(旧版)vsMaterial.GRASS_BLOCK(1.13+)注意版本差异
