InventoryClickEvent 被多次触发

InventoryClickEvent gets fired multiple times

我有一个抽象的 GUI parent class 处理点击事件。

public abstract class GUI implements Listener {

    private final Inventory inventory;

    public GUI(Player player) {
        Bukkit.getPluginManager().registerEvents(this, NPCs.getPlugin());

        ItemStack fillerItem = new ItemStack(getFiller());
        ItemMeta fillerItemMeta = fillerItem.getItemMeta();
        fillerItemMeta.setDisplayName("");
        fillerItem.setItemMeta(fillerItemMeta);

        int inventorySize = (getFunctionalItems().size()>=54) ? 54 : getFunctionalItems().size()+(9-getFunctionalItems().size()%9)*Math.min(1, getFunctionalItems().size()%9);

        inventory = Bukkit.createInventory(player, inventorySize, getName());

        for(int i = 0; i < inventory.getSize(); i++) {
            inventory.setItem(i, fillerItem);
        }
        for(int i = 0; i < getFunctionalItems().size(); i++) {
            inventory.setItem(i, getFunctionalItems().get(i));
        }
    }

    @EventHandler
    public void onClick(InventoryClickEvent event) {
        handle(event);
    }

    public abstract ArrayList<ItemStack> getFunctionalItems();

    public abstract String getName();

    protected abstract void handle(InventoryClickEvent event);

    public Material getFiller() {
        return Material.GRAY_STAINED_GLASS_PANE;
    }

    public Inventory getInventory() {
        return inventory;
    }

    protected final ItemStack createFunctionalItem(String name, Material material) {
        ItemStack itemStack = new ItemStack(material);
        ItemMeta itemMeta = itemStack.getItemMeta();
        itemMeta.setDisplayName(name);
        itemStack.setItemMeta(itemMeta);

        return itemStack;
    }
}

在我的 child class 中,它的处理方式如下

    @Override
    public void handle(InventoryClickEvent event) {
        ItemStack clicked = event.getCurrentItem();
        Player player = (Player) event.getWhoClicked();

        MainGUI mainGUI = new MainGUI(player);
        NameGUI nameGUI = new NameGUI(player);
        SkinGUI skinGUI = new SkinGUI(player);

        //Main GUI
        if(Arrays.equals(event.getClickedInventory().getContents(), mainGUI.getInventory().getContents())) {
            switch(clicked.getItemMeta().getDisplayName()) {
                case "Set Name" -> player.openInventory(nameGUI.getInventory());
                case "Set Skin" -> player.openInventory(skinGUI.getInventory());
            }
            event.setCancelled(true);
        }
    }

但是如果我测试它会在第一次点击时被调用 2 次,在接下来的多次点击中它甚至会迫使我的游戏崩溃。我知道我可以推迟,但我真的很想知道为什么会这样。

谢谢

它是 运行 多次,因为您要将每个新的 GUI 添加到侦听器。

例如,这里:

MainGUI mainGUI = new MainGUI(player);
NameGUI nameGUI = new NameGUI(player);
SkinGUI skinGUI = new SkinGUI(player);

您正在创建 3 个 GUI,因此在侦听器中注册了 3 个新的库存点击事件。

要解决此问题,我建议您:

  1. 制作一个class接收InventoryClickEvent事件,并调用你被调用的GUI。

例如,您有一个包含所有 GUI 的列表:

public static List<GUI> ALL_GUIS = new ArrayList<>();

那么,你应该有一种方法来确定玩家点击了哪个库存,例如:

  • 库存名称。如果这可以更改,则不是很好的解决方案,但更容易创建和使用
  • 这样的库存持有人:
public class MainGUIHolder implements InventoryHolder {

    @Override
    public Inventory getInventory() {
        return null;
    }
}

现在这样使用:

Inventory mainInv = Bukkit.createInventory(new MainGUIHolder(), 9, "My inv");

您可以检查 inventory instanceof MainGUIHolder,然后获取 holder 实例,其中可能包含玩家当前编辑的某些对象

  1. 不要创建多次 GUI 实例。对我来说,这不是一个好方法,我认为这样做更好:
MainGUI mainGUI = GuiManager.getMainGUID(); // here get the alone main GUI instance
// now use it