Spigot 1.19插件问题:召唤的生物仍与玩家发生碰撞
Minecraft 1.19 Spigot插件:生物推动玩家问题修复
问题描述
我为Minecraft 1.19版本开发了一款基于Spigot的自定义插件,其中包含/mob命令。该命令可让玩家将指定生物传送到自身位置并让自己隐身,但目前存在问题:生物仍会推动玩家,导致玩家无法停留在原地。
原代码
package de.snoopti.commands.extra; import de.snoopti.Main; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; import org.bukkit.entity.EntityType; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerInteractEntityEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.plugin.Plugin; public class Mob implements CommandExecutor, Listener { private final Plugin plugin; private LivingEntity mob; private Player player; public Mob(Main main) { this.plugin = main; } @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { if (!(sender instanceof Player)) { sender.sendMessage(Main.PREFIX + "Dieser Befehl kann nur von einem Spieler ausgeführt werden."); return true; } if (mob != null) { mob.remove(); player.setInvisible(false); mob = null; player = null; } player = (Player) sender; if (args.length != 1) { player.sendMessage(Main.PREFIX + "Verwendung: /mob <Tier>"); return true; } EntityType entityType = EntityType.fromName(args[0]); if (entityType == null || !entityType.isAlive()) { player.sendMessage(Main.PREFIX + "§cDies ist kein gültiges Tier."); player.playSound(player.getLocation(), Main.commandSOUNDerror, (float) Main.commandSOUNDvolume, 1f); return true; } mob = (LivingEntity) player.getWorld().spawnEntity(player.getLocation(), entityType); mob.setAI(false); mob.setCanPickupItems(false); mob.setInvulnerable(true); mob.setCollidable(false); player.sendMessage(Main.PREFIX + "Du hast dich in ein §5" + args[0] + " §7verwandelt."); player.playSound(player.getLocation(), Main.commandSOUND, (float) Main.commandSOUNDvolume, 1f); player.setInvisible(true); mob.setCollidable(false); player.setCollidable(false); Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, new Runnable() { @Override public void run() { if (!player.isOnline()) { mob.remove(); player.setInvisible(false); mob = null; player = null; return; } if (!mob.isValid() || mob.isDead()) { player.setInvisible(false); mob = null; player = null; return; } mob.teleport(player.getLocation()); } }, 10L, 1L); return true; } @EventHandler public void onPlayerInteractEntity(PlayerInteractEntityEvent event) { if (mob != null && event.getRightClicked().equals(mob)) { event.setCancelled(true); } } @EventHandler public void onPlayerQuit(PlayerQuitEvent event) { if (player != null && event.getPlayer().equals(player)) { if (mob != null) { mob.remove(); mob = null; } player = null; } } }
问题分析
当前代码中虽然设置了mob.setCollidable(false)和player.setCollidable(false),但重复调用冗余,且单纯关闭碰撞无法完全避免生物与玩家位置重叠时的推动逻辑。另外定时任务中反复将生物传送到玩家位置,会触发短暂的实体位置同步,导致碰撞推动问题。
修复方案
核心思路是让玩家成为生物的乘客,从根本上消除两者之间的碰撞判定,同时优化状态管理逻辑:
修改后的代码
package de.snoopti.commands.extra; import de.snoopti.Main; import org.bukkit.Bukkit; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; import org.bukkit.entity.EntityType; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerInteractEntityEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.plugin.Plugin; public class Mob implements CommandExecutor, Listener { private final Plugin plugin; private LivingEntity mob; private Player player; private int taskId; public Mob(Main main) { this.plugin = main; } @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { if (!(sender instanceof Player)) { sender.sendMessage(Main.PREFIX + "Dieser Befehl kann nur von einem Spieler ausgeführt werden."); return true; } // 清理已有状态 if (mob != null) { cleanup(); } player = (Player) sender; if (args.length != 1) { player.sendMessage(Main.PREFIX + "Verwendung: /mob <Tier>"); return true; } EntityType entityType = EntityType.fromName(args[0]); if (entityType == null || !entityType.isAlive()) { player.sendMessage(Main.PREFIX + "§cDies ist kein gültiges Tier."); player.playSound(player.getLocation(), Main.commandSOUNDerror, (float) Main.commandSOUNDvolume, 1f); return true; } // 生成生物并配置基础属性 mob = (LivingEntity) player.getWorld().spawnEntity(player.getLocation(), entityType); mob.setAI(false); mob.setCanPickupItems(false); mob.setInvulnerable(true); mob.setCollidable(false); mob.setPersistent(false); // 让玩家成为生物乘客,消除碰撞 mob.addPassenger(player); // 设置玩家隐身 player.setInvisible(true); player.sendMessage(Main.PREFIX + "Du hast dich in ein §5" + args[0] + " §7verwandelt."); player.playSound(player.getLocation(), Main.commandSOUND, (float) Main.commandSOUNDvolume, 1f); // 启动定时任务维护乘客状态 taskId = Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, () -> { if (!player.isOnline()) { cleanup(); return; } if (!mob.isValid() || mob.isDead()) { cleanup(); return; } // 检查玩家是否仍为乘客,否则重新添加 if (!mob.getPassengers().contains(player)) { mob.addPassenger(player); } }, 10L, 2L); return true; } @EventHandler public void onPlayerInteractEntity(PlayerInteractEntityEvent event) { if (mob != null && event.getRightClicked().equals(mob)) { event.setCancelled(true); } } @EventHandler public void onPlayerQuit(PlayerQuitEvent event) { if (player != null && event.getPlayer().equals(player)) { cleanup(); } } // 统一清理逻辑 private void cleanup() { if (mob != null) { mob.remove(); mob = null; } if (player != null) { player.setInvisible(false); player = null; } if (taskId != -1) { Bukkit.getScheduler().cancelTask(taskId); taskId = -1; } } }
关键修改点
- 新增
cleanup()方法统一处理状态重置,避免代码冗余 - 使用
mob.addPassenger(player)替代单纯的 teleport,让玩家与生物绑定为乘客关系,彻底消除碰撞推动 - 定时任务改为检查乘客状态,确保玩家始终绑定在生物上,而非反复传送
- 新增任务ID管理,确保定时任务在清理时被正确取消
- 配置
mob.setPersistent(false)防止生物在服务器重启后残留
内容的提问来源于stack exchange,提问作者snoopti
相关产品推荐
相关产品推荐

