You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 18:43:08