Bukkit中EntityExplosionEvent#blockList()失效问题求助
解决EntityExplosionEvent获取blockList全为AIR的问题
嘿,我之前正好碰到过一模一样的问题!让我给你捋清楚原因和解决办法:
问题根源
你遇到的核心问题是:EntityExplosionEvent触发时,游戏已经完成了方块的摧毁逻辑,此时event.blockList()返回的是爆炸后会被替换成空气的方块实例(也就是已经被标记为AIR的对象),而不是爆炸前的原始方块。这就是为什么你拿到的全是AIR类型。
正确解决思路:手动获取爆炸范围内的原始方块
我们需要绕过blockList(),直接通过爆炸的中心点和半径,遍历范围内的所有方块,筛选出会被爆炸破坏的目标,记录它们的状态后再进行恢复。下面是具体的代码示例:
@EventHandler public void onTNTExplosion(EntityExplosionEvent event) { // 如果你想完全自定义爆炸效果,先取消默认的破坏行为 event.setCancelled(true); // 获取爆炸的核心参数:中心点和半径 Location explosionCenter = event.getLocation(); float explosionRadius = event.getRadius(); // 存储需要恢复的方块快照(包含所有状态信息) List<BlockSnapshot> blocksToRestore = new ArrayList<>(); // 遍历爆炸覆盖的立方体范围(取半径的整数上限) int radiusInt = (int) Math.ceil(explosionRadius); for (int x = -radiusInt; x <= radiusInt; x++) { for (int y = -radiusInt; y <= radiusInt; y++) { for (int z = -radiusInt; z <= radiusInt; z++) { Location blockLoc = explosionCenter.clone().add(x, y, z); Block block = blockLoc.getBlock(); // 跳过空气方块和防爆方块(比如基岩、黑曜石) if (block.getType().isAir() || !block.getType().isDestroyableByExplosion()) { continue; } // 判断方块是否在爆炸有效半径内(用平方距离减少计算开销) double distance = blockLoc.distanceSquared(explosionCenter); if (distance > explosionRadius * explosionRadius) { continue; } // 记录方块的完整快照(包含类型、数据、NBT等) blocksToRestore.add(block.getSnapshot()); } } } // 模拟爆炸的视觉和音效(如果取消了默认爆炸) explosionCenter.getWorld().createExplosion(explosionCenter, explosionRadius, false, false); // 延迟恢复方块(示例为10秒后,20ticks=1秒) Bukkit.getScheduler().runTaskLater(yourPluginInstance, () -> { for (BlockSnapshot snapshot : blocksToRestore) { // 将快照恢复到原位置 snapshot.update(true, false); } }, 200L); }
额外注意事项
- 用BlockSnapshot保存状态:这个类能完整保存方块的所有状态(比如箱子里的物品、熔炉进度、告示牌文字等),比单纯记录
Material要可靠得多。 - 性能优化:用
distanceSquared代替distance可以减少开根号的计算开销,提升遍历效率;如果爆炸半径很大,可以考虑缩小遍历范围。 - 灵活调整逻辑:如果不需要取消默认爆炸,直接去掉
event.setCancelled(true)即可——游戏会先显示方块被炸毁,之后再自动恢复,视觉效果更自然。 - 主线程操作:所有方块操作必须在主线程执行,所以恢复逻辑要用
runTaskLater而不是异步任务。
内容的提问来源于stack exchange,提问作者Kiligo
相关产品推荐
相关产品推荐

