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 个新的库存点击事件。
要解决此问题,我建议您:
- 制作一个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 实例,其中可能包含玩家当前编辑的某些对象
- 不要创建多次 GUI 实例。对我来说,这不是一个好方法,我认为这样做更好:
MainGUI mainGUI = GuiManager.getMainGUID(); // here get the alone main GUI instance
// now use it
我有一个抽象的 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 个新的库存点击事件。
要解决此问题,我建议您:
- 制作一个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 实例,其中可能包含玩家当前编辑的某些对象
- 不要创建多次 GUI 实例。对我来说,这不是一个好方法,我认为这样做更好:
MainGUI mainGUI = GuiManager.getMainGUID(); // here get the alone main GUI instance
// now use it