BukkitScheduler任务无法重复执行——榴弹发射器动画异常求助
问题根源与修复方案
核心问题分析
- 成员变量
t未重置:t是类级别的成员变量,第一次发射后会持续累加,后续右键时t已不是初始值0,导致粒子轨迹直接从偏移极大的位置开始,看起来像没触发动画。 - 未取消重复任务定时器:每次右键都会启动新的
runTaskTimer任务,旧任务不会自动停止,多个任务同时运行会导致逻辑混乱,还浪费服务器资源。 - Location对象被重复修改:
x1.add(pos)会直接修改原x1的坐标,后续循环里的粒子位置计算会基于已经偏移后的位置,导致轨迹完全错误。 - 冗余的
timer变量:代码里的timer变量没有实际作用,属于无效代码。
修复后的完整代码
@EventHandler public void onRightClickWeapon(PlayerInteractEvent e) { Player p = e.getPlayer(); Action a = e.getAction(); if (a != Action.RIGHT_CLICK_AIR && a != Action.RIGHT_CLICK_BLOCK) { return; } ItemStack heldItem = p.getItemInHand(); if (heldItem.getType() != this.gMat.getType()) { return; } // 避免空指针,先判断ItemMeta是否存在 if (heldItem.getItemMeta() == null || this.gMat.getItemMeta() == null) { return; } if (!heldItem.getItemMeta().getDisplayName().equalsIgnoreCase(this.gMat.getItemMeta().getDisplayName())) { return; } BukkitScheduler scheduler = Bukkit.getServer().getScheduler(); EnumParticle trail = EnumParticle.CLOUD; // 复制初始位置,避免后续修改原对象 Location startLoc = p.getEyeLocation().clone(); Vector direction = startLoc.getDirection().clone(); double g = 4.72; // 用局部变量t,每次发射都从0开始 final double[] t = {0.0}; // 启动定时任务,并用变量保存任务ID,方便后续取消 int taskId = scheduler.runTaskTimer(Bukkit.getPluginManager().getPlugin("ShooterPlugin"), () -> { // 计算当前帧的偏移向量 double timeFactor = t[0] * 0.25; Vector pos = new Vector( direction.getX() * t[0], -(g * Math.pow(timeFactor, 2)) / 2 + direction.getY() * timeFactor + p.getEyeHeight(), direction.getZ() * t[0] ); // 基于初始位置计算粒子位置,每次都用clone避免修改原位置 Location particleLoc = startLoc.clone().add(pos); PacketPlayOutWorldParticles packetParticle = new PacketPlayOutWorldParticles( trail, false, (float) particleLoc.getX(), (float) particleLoc.getY(), (float) particleLoc.getZ(), 0, 0, 0, 0, 0, null ); CraftPlayer cp = (CraftPlayer) p; cp.getHandle().playerConnection.sendPacket(packetParticle); t[0] += 1; // 设定停止条件,比如t达到某个值后取消任务,避免无限运行 if (t[0] >= 20) { // 这里可以根据需求调整,控制轨迹时长 scheduler.cancelTask(taskId); } }, 0, 8L); }
关键修复点说明
- 使用局部变量
t:把t改成方法内的局部数组(适配lambda对final变量的要求),每次右键发射时都从0开始计数,保证轨迹从玩家位置起始。 - 克隆Location和Vector对象:用
clone()复制初始位置和方向,避免后续计算修改原对象导致的位置偏移错误。 - 保存任务ID并添加停止条件:启动任务时保存
taskId,当t达到设定值(比如20)时手动取消任务,避免任务无限运行,也防止多次右键导致多个任务同时执行。 - 优化物品判断逻辑:增加空指针判断,简化条件分支,提升代码健壮性。
- 移除冗余的
timer变量:删除无实际作用的timer和对应的runTaskLater任务。
内容的提问来源于stack exchange,提问作者Manewow
相关产品推荐
相关产品推荐

