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

BukkitScheduler任务无法重复执行——榴弹发射器动画异常求助

问题根源与修复方案

核心问题分析

  1. 成员变量t未重置:t是类级别的成员变量,第一次发射后会持续累加,后续右键时t已不是初始值0,导致粒子轨迹直接从偏移极大的位置开始,看起来像没触发动画。
  2. 未取消重复任务定时器:每次右键都会启动新的runTaskTimer任务,旧任务不会自动停止,多个任务同时运行会导致逻辑混乱,还浪费服务器资源。
  3. Location对象被重复修改:x1.add(pos)会直接修改原x1的坐标,后续循环里的粒子位置计算会基于已经偏移后的位置,导致轨迹完全错误。
  4. 冗余的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 20:15:22