一、分析如何实现一键挖矿
- 监听方块被破坏事件
- 通过广度优先搜索周边相同方块
- 通过默认配置和工具耐久确定连锁破坏范围
- 执行破坏方块指令
二、代码编写
@Mod.EventBusSubscriber(modid = Constants.MODID)
public class BlockBreakEvent {
private static final Integer MAX_SEARCH_NUM = 50;
@SubscribeEvent
public static void blockBreakEvent(BlockEvent.BreakEvent event) {
handleBlockBreakEvent(event);
}
/**
* 处理方块被破坏事件
*/
private static void handleBlockBreakEvent(BlockEvent.BreakEvent event) {
// 玩家实体
PlayerEntity player = event.getPlayer();
// 当前世界
World world = player.getCommandSenderWorld();
// 被破坏方块的坐标
BlockPos pos = event.getPos();
// 被破坏方块信息
BlockState state = event.getState();
// 玩家主手工具
ItemStack handItem = player.getMainHandItem();
// 查找可以连锁的方块
Set<BlockPos> blocksToMine = findBlocksToMine(world, pos, state, handItem.getDamageValue() == 0 ? MAX_SEARCH_NUM : handItem.getDamageValue());
for (BlockPos p : blocksToMine) {
// 破坏方块
world.destroyBlock(p, true, player);
}
if (handItem.getDamageValue() != 0) {
// 减少主手工具耐久值
handItem.setDamageValue(Math.min(handItem.getDamageValue() + blocksToMine.size(), handItem.getMaxDamage()));
}
}
/**
* 通过广度优先搜索获取可破坏的方块
*/
private static Set<BlockPos> findBlocksToMine(World world, BlockPos pos, BlockState originalState, int damageValue) {
Set<BlockPos> blocksToMine = new HashSet<>();
Queue<BlockPos> queue = new ArrayDeque<>();
queue.add(pos);
while (!queue.isEmpty()) {
BlockPos currentPos = queue.poll();
if (blocksToMine.contains(currentPos)) {
continue;
}
BlockState currentState = world.getBlockState(currentPos);
if (currentState.getBlock() == originalState.getBlock()) {
blocksToMine.add(currentPos);
for (BlockPos adjacentPos : getAdjacentPositions(currentPos)) {
if (!queue.contains(adjacentPos)) {
queue.add(adjacentPos);
}
}
}
if (blocksToMine.size() >= damageValue) {
return blocksToMine;
}
}
return blocksToMine;
}
/**
* 获取被破坏方块周围方块,共26个
*/
private static Set<BlockPos> getAdjacentPositions(BlockPos pos) {
Set<BlockPos> adjacentPositions = new HashSet<>();
adjacentPositions.add(pos.north());
adjacentPositions.add(pos.south());
adjacentPositions.add(pos.east());
adjacentPositions.add(pos.west());
adjacentPositions.add(pos.above());
adjacentPositions.add(pos.below());
adjacentPositions.add(pos.north().above());
adjacentPositions.add(pos.above().south());
adjacentPositions.add(pos.south().below());
adjacentPositions.add(pos.below().north());
adjacentPositions.add(pos.north().east());
adjacentPositions.add(pos.east().south());
adjacentPositions.add(pos.south().west());
adjacentPositions.add(pos.west().north());
adjacentPositions.add(pos.north().east());
adjacentPositions.add(pos.east().south());
adjacentPositions.add(pos.south().west());
adjacentPositions.add(pos.west().north());
adjacentPositions.add(pos.above().north().west());
adjacentPositions.add(pos.above().north().east());
adjacentPositions.add(pos.above().south().west());
adjacentPositions.add(pos.above().south().east());
adjacentPositions.add(pos.below().north().west());
adjacentPositions.add(pos.below().north().east());
adjacentPositions.add(pos.below().south().west());
adjacentPositions.add(pos.below().south().east());
return adjacentPositions;
}
}
注:上述代码只是一个简单的实现,如getAdjacentPositions()方法可以有更好的写法,方块的破坏逻辑和拣取逻辑也可以做更好的优化。
三、运行效果
四、优化实现
从上面的运行效果可以看出来物品掉落非常零散,拣取很不方便,因此可以通过如下代码替换实现物品掉落的合并以及更好的掉落效果。
private static void handleBlockBreakEvent(BlockEvent.BreakEvent event) {
PlayerEntity player = event.getPlayer();
Direction direction = player.getDirection();
World world = player.getCommandSenderWorld();
BlockPos pos = event.getPos();
BlockState state = event.getState();
ItemStack handItem = player.getMainHandItem();
List<ItemStack> drops = new ArrayList<>();
LootContext.Builder lootContextBuilder = new LootContext.Builder((ServerWorld)world)
.withRandom(world.random)
.withParameter(LootParameters.ORIGIN, Vector3d.atCenterOf(pos))
.withParameter(LootParameters.TOOL, handItem)
.withOptionalParameter(LootParameters.THIS_ENTITY, player);
Set<BlockPos> blocksToMine = findBlocksToMine(world, pos, state, direction, handItem.getDamageValue() == 0 ? MAX_SEARCH_NUM : handItem.getDamageValue());
for (BlockPos p : blocksToMine) {
BlockState blockState = world.getBlockState(p);
List<ItemStack> blockDrops = blockState.getDrops(lootContextBuilder);
drops.addAll(blockDrops);
world.removeBlock(p, false);
}
// 在中心位置生成所有掉落物
for (ItemStack drop : drops) {
ItemEntity itemEntity = new ItemEntity(world, pos.getX() + 0.1, pos.getY() + 0.1, pos.getZ() + 0.1, drop);
world.addFreshEntity(itemEntity);
}
if (!handItem.equals(ItemStack.EMPTY)) {
handItem.setDamageValue(Math.min(handItem.getDamageValue() + blocksToMine.size(), handItem.getMaxDamage()));
}
}
优化后的运行效果如下,具体的实现逻辑也比较简单,感兴趣的可以自己探索一番。
注:本文仅用于学习参考,不参与任何商业行为