第 8 章:世界操作
第 8 章:世界操作
掌握世界管理、区块加载、实体生成和结构生成的核心 API。
8.1 世界管理
获取世界
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.WorldCreator;
// 获取已加载的世界
World overworld = Bukkit.getWorld("world");
World nether = Bukkit.getWorld("world_nether");
World end = Bukkit.getWorld("world_the_end");
// 获取所有已加载的世界
List<World> worlds = Bukkit.getWorlds();
// 获取世界属性
for (World w : worlds) {
getLogger().info(w.getName() + ": "
+ w.getEnvironment() + " | "
+ w.getPlayers().size() + " 玩家");
}
动态创建世界
public class WorldManager {
/**
* 创建一个新世界
*/
public static World createWorld(String name, World.Environment env,
long seed, boolean structures) {
WorldCreator creator = new WorldCreator(name);
creator.environment(env);
creator.seed(seed);
creator.generateStructures(structures);
// 设置世界类型
creator.type(WorldType.NORMAL); // NORMAL, FLAT, LARGE_BIOMES
return creator.createWorld();
}
/**
* 创建超平坦世界
*/
public static World createFlatWorld(String name) {
WorldCreator creator = new WorldCreator(name);
creator.environment(World.Environment.NORMAL);
creator.type(WorldType.FLAT);
return creator.createWorld();
}
/**
* 创建虚空世界
*/
public static World createVoidWorld(String name) {
WorldCreator creator = new WorldCreator(name);
creator.environment(World.Environment.NORMAL);
creator.generator(new VoidChunkGenerator());
return creator.createWorld();
}
}
自定义空世界生成器
package com.example.myplugin.worlds;
import org.bukkit.World;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.generator.WorldInfo;
import org.jetbrains.annotations.NotNull;
import java.util.Random;
public class VoidChunkGenerator extends ChunkGenerator {
@Override
public void generateNoise(@NotNull WorldInfo worldInfo,
@NotNull Random random,
int chunkX, int chunkZ,
@NotNull ChunkData chunkData) {
// 不生成任何方块 = 虚空
}
@Override
public void generateSurface(@NotNull WorldInfo worldInfo,
@NotNull Random random,
int chunkX, int chunkZ,
@NotNull ChunkData chunkData) {
// 不生成地表
}
@Override
public void generateBedrock(@NotNull WorldInfo worldInfo,
@NotNull Random random,
int chunkX, int chunkZ,
@NotNull ChunkData chunkData) {
// 不生成基岩
}
@Override
public void generateCaves(@NotNull WorldInfo worldInfo,
@NotNull Random random,
int chunkX, int chunkZ,
@NotNull ChunkData chunkData) {
// 不生成洞穴
}
}
卸载和删除世界
/**
* 卸载世界(不删除文件)
*/
public static boolean unloadWorld(String name) {
World world = Bukkit.getWorld(name);
if (world == null) return false;
// 将玩家传送到主世界
World mainWorld = Bukkit.getWorlds().get(0);
for (Player player : world.getPlayers()) {
player.teleport(mainWorld.getSpawnLocation());
}
return Bukkit.unloadWorld(world, true); // true = 保存区块
}
/**
* 删除世界(包括文件)
*/
public static boolean deleteWorld(File worldFolder) {
if (!worldFolder.exists()) return false;
File[] files = worldFolder.listFiles();
if (files != null) {
for (File file : files) {
if (file.isDirectory()) {
deleteWorld(file);
}
file.delete();
}
}
return worldFolder.delete();
}
8.2 区块管理
区块基础
Minecraft 世界以 16×16 的区块(Chunk)为单位加载和卸载。理解区块管理对性能至关重要。
| 概念 | 说明 |
|---|---|
| Chunk | 16×16 区块,包含该区域所有方块和实体 |
| 区块加载 | 将区块数据从磁盘读入内存 |
| 区块卸载 | 将不再需要的区块从内存移除 |
| 生成半径 | 服务器自动加载的区块范围 |
加载区块
// 同步加载区块(阻塞主线程,慎用)
Chunk chunk = world.getChunkAt(100, 200); // 区块坐标
// 异步加载区块(推荐)
world.getChunkAtAsync(100, 200).thenAccept(chunk -> {
// 区块加载完成(可能在异步线程)
Bukkit.getScheduler().runTask(plugin, () -> {
// 回到主线程操作
doSomethingWithChunk(chunk);
});
});
// 检查区块是否已加载
Chunk chunk = world.getChunkAt(100, 200);
boolean loaded = chunk.isLoaded();
区块票(Chunk Ticket)
防止区块被自动卸载:
// 添加区块票(保持加载)
chunk.addPluginChunkTicket(plugin);
// 移除区块票
chunk.removePluginChunkTicket(plugin);
// 检查是否有票
boolean hasTicket = chunk.isPluginChunkTicket(plugin);
区块加载事件
public class ChunkListener implements Listener {
@EventHandler
public void onChunkLoad(ChunkLoadEvent event) {
Chunk chunk = event.getChunk();
// 区块刚加载时触发
}
@EventHandler
public void onChunkUnload(ChunkUnloadEvent event) {
Chunk chunk = event.getChunk();
// 区块即将卸载
}
@EventHandler
public void onChunkPopulate(ChunkPopulateEvent event) {
// 区块首次生成完成
Chunk chunk = event.getChunk();
// 可以在这里添加自定义结构
}
}
8.3 位置与向量
Location
import org.bukkit.Location;
import org.bukkit.World;
// 创建位置
Location loc = new Location(world, 100.5, 64.0, -50.5);
// 从已有位置获取信息
double x = loc.getX();
double y = loc.getY();
double z = loc.getZ();
float yaw = loc.getYaw(); // 水平视角
float pitch = loc.getPitch(); // 垂直视角
World world = loc.getWorld();
// 获取方块位置(整数坐标)
Block block = loc.getBlock();
int bx = block.getX();
int by = block.getY();
int bz = block.getZ();
// 位置运算
Location offset = loc.clone().add(0, 10, 0); // 向上 10 格
Location forward = loc.clone().add(loc.getDirection().multiply(5)); // 向前 5 格
// 距离计算
double distance = loc1.distance(loc2);
double distanceSquared = loc1.distanceSquared(loc2); // 更高效(不开方)
向量运算
import org.bukkit.util.Vector;
Vector v1 = new Vector(1, 2, 3);
Vector v2 = new Vector(4, 5, 6);
// 加法
Vector sum = v1.clone().add(v2);
// 标量乘法
Vector scaled = v1.clone().multiply(2);
// 标准化(长度为 1)
Vector normalized = v1.clone().normalize();
// 点积
double dot = v1.dot(v2);
// 叉积
Vector cross = v1.clone().crossProduct(v2);
// 应用到实体(击退效果)
Vector direction = player.getLocation().getDirection();
entity.setVelocity(direction.multiply(2).setY(0.5));
8.4 实体生成
基本生成
import org.bukkit.entity.EntityType;
// 在指定位置生成实体
Location loc = player.getLocation();
World world = loc.getWorld();
// 生成僵尸
Zombie zombie = (Zombie) world.spawnEntity(loc, EntityType.ZOMBIE);
// 生成苦力怕
Creeper creeper = (Creeper) world.spawnEntity(loc, EntityType.CREEPER);
creeper.setPowered(true); // 高压苦力怕
// 生成经验球
ExperienceOrb orb = world.spawn(loc, ExperienceOrb.class);
orb.setExperience(100);
使用 Consumer 设置属性
// 使用 spawn 方法的 Consumer 参数(原子操作)
world.spawn(loc, Zombie.class, zombie -> {
zombie.setCustomName(Component.text("§c精英僵尸"));
zombie.setCustomNameVisible(true);
zombie.setHealth(40); // 双倍血量
zombie.getEquipment().setItemInMainHand(
new ItemStack(Material.DIAMOND_SWORD)
);
zombie.getEquipment().setHelmet(
new ItemStack(Material.DIAMOND_HELMET)
);
});
掉落物生成
// 生成掉落物
ItemStack item = new ItemStack(Material.DIAMOND, 3);
world.dropItem(loc, item);
// 不拾取延迟的掉落物
Item itemEntity = world.dropItem(loc, item);
itemEntity.setPickupDelay(40); // 2 秒后才能拾取
// 生成自然掉落效果
world.dropItemNaturally(loc, item); // 有随机偏移
8.5 方块操作
基本方块操作
// 获取方块
Block block = world.getBlockAt(100, 64, -50);
// 获取方块类型
Material type = block.getType();
// 设置方块类型
block.setType(Material.DIAMOND_BLOCK);
// 设置方块(带方块数据)
block.setType(Material.OAK_STAIRS);
BlockData data = block.getBlockData();
// 设置朝向等属性
block.setBlockData(data);
批量方块操作
/**
* 填充一个立方体区域
*/
public static void fillRegion(World world,
int x1, int y1, int z1,
int x2, int y2, int z2,
Material material) {
int minX = Math.min(x1, x2);
int minY = Math.min(y1, y2);
int minZ = Math.min(z1, z2);
int maxX = Math.max(x1, x2);
int maxY = Math.max(y1, y2);
int maxZ = Math.max(z1, z2);
for (int x = minX; x <= maxX; x++) {
for (int y = minY; y <= maxY; y++) {
for (int z = minZ; z <= maxZ; z++) {
world.getBlockAt(x, y, z).setType(material);
}
}
}
}
/**
* 生成球体
*/
public static void createSphere(Location center, int radius, Material material) {
World world = center.getWorld();
int cx = center.getBlockX();
int cy = center.getBlockY();
int cz = center.getBlockZ();
for (int x = -radius; x <= radius; x++) {
for (int y = -radius; y <= radius; y++) {
for (int z = -radius; z <= radius; z++) {
if (x * x + y * y + z * z <= radius * radius) {
world.getBlockAt(cx + x, cy + y, cz + z).setType(material);
}
}
}
}
}
8.6 结构生成
放置结构
import org.bukkit.StructureType;
import org.bukkit.generator.structure.Structure;
/**
* 在指定位置放置结构
*/
public static boolean placeStructure(Location loc) {
World world = loc.getWorld();
if (world == null) return false;
// 使用 StructurePlaceSettings
Structure structure = world.locateNearestStructure(
loc, Structure.VILLAGE_PLAINS, 1000, false
);
// 手动生成村庄
return world.generateStructure(loc, Structure.VILLAGE_PLAINS);
}
/**
* 使用 NMS 或 API 放置结构
*/
public static void placeWithPaste(Location loc) {
World world = loc.getWorld();
if (world == null) return;
// 使用 StructureManager
var server = Bukkit.getServer();
// Paper 提供了结构放置 API
}
8.7 粒子与音效
粒子效果
import org.bukkit.Particle;
import org.bukkit.Color;
// 发送粒子
world.spawnParticle(Particle.FLAME, location, 50); // 50 个火焰粒子
world.spawnParticle(Particle.CLOUD, location, 20, 0.5, 0.5, 0.5); // 带偏移
// 自定义颜色粒子
world.spawnParticle(
Particle.DUST,
location,
30, // 数量
0.5, 0.5, 0.5, // 偏移
new Particle.DustOptions(Color.RED, 1.0f) // 红色,大小 1.0
);
// 玩家专用粒子(只对特定玩家可见)
player.spawnParticle(Particle.HEART, player.getLocation(), 5);
音效
import org.bukkit.Sound;
// 在世界播放音效
world.playSound(location, Sound.ENTITY_PLAYER_LEVELUP, 1.0f, 1.0f);
// 只对特定玩家播放
player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_PLING, 1.0f, 2.0f);
// 全服播放
for (Player p : Bukkit.getOnlinePlayers()) {
p.playSound(p.getLocation(), Sound.UI_TOAST_CHALLENGE_COMPLETE, 1.0f, 1.0f);
}
8.8 天气与时间
// 设置时间
world.setTime(0); // 日出
world.setTime(6000); // 正午
world.setTime(13000); // 日落
world.setTime(18000); // 午夜
// 获取当前时间
long time = world.getTime();
boolean isDay = world.isDayTime();
// 设置天气
world.setStorm(true); // 开启下雨
world.setThundering(true); // 开启雷暴
world.setWeatherDuration(6000); // 持续 5 分钟
// 清除天气
world.setStorm(false);
world.setThundering(false);
world.setClearWeatherDuration(6000);
// 设置游戏规则
world.setGameRule(GameRule.DO_DAYLIGHT_CYCLE, false);
world.setGameRule(GameRule.DO_MOB_SPAWNING, false);
world.setGameRule(GameRule.KEEP_INVENTORY, true);
8.9 业务场景:多世界竞技场
public class ArenaManager {
private final Map<String, Arena> arenas = new HashMap<>();
/**
* 创建竞技场
*/
public Arena createArena(String name, Location pos1, Location pos2) {
// 复制竞技场区域作为模板
String templateWorld = pos1.getWorld().getName();
String arenaWorld = "arena_" + name + "_" + System.currentTimeMillis();
// 创建竞技场世界
WorldCreator creator = new WorldCreator(arenaWorld);
creator.copy(Bukkit.getWorld(templateWorld));
World world = creator.createWorld();
Arena arena = new Arena(name, world, pos1, pos2);
arenas.put(name, arena);
return arena;
}
/**
* 重置竞技场(恢复原始状态)
*/
public void resetArena(Arena arena) {
// 卸载当前世界
unloadWorld(arena.getWorld().getName());
// 从模板重新复制
// ...(类似创建流程)
}
/**
* 传送玩家到竞技场
*/
public void teleportToArena(Player player, Arena arena) {
Location spawn = arena.getSpawnLocation();
player.teleport(spawn);
// 清空背包
player.getInventory().clear();
player.setHealth(20.0);
player.setFoodLevel(20);
}
}
8.10 性能注意事项
| 操作 | 风险 | 优化方案 |
|---|---|---|
| 同步加载区块 | 阻塞主线程 | 使用 getChunkAtAsync() |
| 批量修改方块 | 大量事件触发 | 使用 NMS ChunkSnapshot |
| 生成大量实体 | TPS 下降 | 限制实体密度,分批生成 |
| 频繁读写世界 | I/O 压力 | 使用区块票保持关键区域加载 |
8.11 扩展阅读
8.12 本章小结
| 要点 | 内容 |
|---|---|
| 世界管理 | 创建、卸载、删除世界,支持自定义生成器 |
| 区块管理 | 异步加载、区块票、监听加载/卸载事件 |
| 实体生成 | world.spawnEntity() 或 world.spawn() |
| 方块操作 | 批量修改需要注意性能,使用偏移优化 |
| 结构生成 | 使用 API 放置村庄、神殿等结构 |
| 粒子音效 | 丰富游戏体验,支持单人/全局播放 |
下一章: 第 9 章:实体 API — 深入实体操作、自定义生物 AI 和装备系统。