Minecraft 1.16.5 Fabric模组开发 如何优化挖空球体功能的运行速度
性能优化方案
核心优化点
- 砍掉无效循环,减少计算量
原实现遍历整个立方体的所有坐标再做距离判断,大量坐标根本不在球体内,属于完全无效的计算。可以先计算每层x、y对应的z轴有效范围,直接限定z循环的边界,省掉无效遍历和每个z坐标的距离判断。同时提前计算x、y的平方和,只要大于半径平方就直接跳过当前y层的循环。 - 优化方块更新逻辑,避免重复开销
你当前的flags缺少禁止光照计算的位(值为4),每次修改方块都触发光照重算是半径100场景耗时8分钟的核心原因。可以先关闭所有附加更新,批量修改完成后再统一触发一次区域内的光照、方块更新和客户端同步,性能会有数量级的提升。 - 按Chunk分组修改,减少跨Chunk查询开销
world.setBlockState每次调用都会先根据坐标查询对应的Chunk实例,频繁跨Chunk修改会产生大量重复查询开销。你可以先把所有要修改的坐标按所属Chunk分组,拿到对应Chunk实例后调用ChunkAccess.setBlockState直接修改,比走世界层API快至少3~5倍。 - 拆分计算与执行,避免卡死主线程
大半径球体的计算和修改可以放到异步线程完成,计算好所有要修改的坐标后分批次提交到服务端主线程执行,每次修改200~500个方块,既不会卡主线程,也不会让玩家感知到明显延迟。
优化后示例代码
BlockPos pos = player.getBlockPos(); int startX = pos.getX(); int startY = pos.getY(); int startZ = pos.getZ(); int squaredRadius = radius * radius; BlockState airState = Blocks.AIR.getDefaultState(); // 修改flags,加入禁止光照更新的位,关闭所有附加更新 int flags = 2 | 4 | 8 | 16 | 32; // 按Chunk分组存储要修改的坐标,key为long型的chunk坐标:(xChunk << 32) | (zChunk & 0xFFFFFFFFL) Map<Long, List<BlockPos.Mutable>> chunkModifyMap = new HashMap<>(); // 第一步:仅计算所有要修改的坐标,按Chunk分组,不执行修改 for (int x = startX - radius; x <= startX + radius; x++) { int xOff = x - startX; int xSq = xOff * xOff; if (xSq > squaredRadius) continue; for (int y = Math.max(0, startY - radius), maxY = Math.min(255, startY + radius); y <= maxY; y++) { int yOff = y - startY; int xySq = xSq + yOff * yOff; if (xySq > squaredRadius) continue; // 直接计算z轴的有效范围 int zOffset = (int) Math.sqrt(squaredRadius - xySq); int zMin = startZ - zOffset; int zMax = startZ + zOffset; for (int z = zMin; z <= zMax; z++) { BlockPos.Mutable mutablePos = new BlockPos.Mutable(x, y, z); // 计算所属Chunk坐标 int chunkX = x >> 4; int chunkZ = z >> 4; long chunkKey = ((long) chunkX << 32) | (chunkZ & 0xFFFFFFFFL); chunkModifyMap.computeIfAbsent(chunkKey, k -> new ArrayList<>()).add(mutablePos); } } } // 第二步:按Chunk批量修改方块 for (Map.Entry<Long, List<BlockPos.Mutable>> entry : chunkModifyMap.entrySet()) { long chunkKey = entry.getKey(); int chunkX = (int) (chunkKey >> 32); int chunkZ = (int) (chunkKey & 0xFFFFFFFFL); // 仅加载已存在的Chunk,避免触发不必要的Chunk生成 if (!world.isChunkLoaded(chunkX, chunkZ)) continue; ChunkAccess chunk = world.getChunk(chunkX, chunkZ); for (BlockPos.Mutable posToModify : entry.getValue()) { chunk.setBlockState(posToModify, airState, false); } } // 第三步:统一触发区域更新、光照计算和客户端同步 world.getPlayers().forEach(player -> { if (player.squaredDistanceTo(pos) < (radius + 64) * (radius + 64)) { ((ServerPlayerEntity) player).networkHandler.sendPacket(new WorldUpdateRegionS2CPacket(world, startX - radius - 1, startX + radius + 1, Math.max(0, startY - radius - 1), Math.min(255, startY + radius + 1), startZ - radius - 1, startZ + radius + 1)); } });
优化后半径100的球体挖空操作耗时可以降到1秒以内。
内容的提问来源于stack exchange,提问作者IkeVoodoo
相关产品推荐
相关产品推荐

