GUI con Inventari
Introduzione alle GUI
Le GUI (Graphical User Interfaces) in Spigot/Paper si basano sugli inventari di Minecraft. Puoi creare menu interattivi, shop, configurazioni e molto altro usando inventari custom con item cliccabili.
Creare una GUI Semplice
```java public class SimpleGUI { private final Inventory inventory;
public SimpleGUI() {
// Crea un inventario con 27 slot (3 righe)
inventory = Bukkit.createInventory(null, 27, "§6Menu Principale");
// Aggiungi item
initializeItems();
}
private void initializeItems() {
// Item di esempio
ItemStack infoItem = new ItemStack(Material.BOOK);
ItemMeta infoMeta = infoItem.getItemMeta();
infoMeta.setDisplayName("§aInformazioni");
infoMeta.setLore(Arrays.asList(
"§7Clicca per vedere le info",
"§7del server!"
));
infoItem.setItemMeta(infoMeta);
ItemStack closeItem = new ItemStack(Material.BARRIER);
ItemMeta closeMeta = closeItem.getItemMeta();
closeMeta.setDisplayName("§cChiudi");
closeItem.setItemMeta(closeMeta);
// Posiziona gli item
inventory.setItem(13, infoItem); // Centro
inventory.setItem(26, closeItem); // Angolo in basso a destra
}
public void open(Player player) {
player.openInventory(inventory);
}
} ```
Gestire i Click
```java public class GUIListener implements Listener {
@EventHandler
public void onInventoryClick(InventoryClickEvent event) {
// Controlla se è la nostra GUI
if (!event.getView().getTitle().equals("§6Menu Principale")) {
return;
}
// Impedisci di spostare item
event.setCancelled(true);
Player player = (Player) event.getWhoClicked();
ItemStack clickedItem = event.getCurrentItem();
// Controlla se l'item è null
if (clickedItem == null || clickedItem.getType() == Material.AIR) {
return;
}
// Gestisci i click in base all'item
if (clickedItem.getType() == Material.BOOK) {
player.sendMessage("§aServer creato da TuoNome!");
player.sendMessage("§aGiocatori online: " + Bukkit.getOnlinePlayers().size());
} else if (clickedItem.getType() == Material.BARRIER) {
player.closeInventory();
}
}
} ```
GUI con Pagine
```java
public class PagedGUI {
private final Player player;
private int page = 0;
private final List
public PagedGUI(Player player, List<ItemStack> items) {
this.player = player;
this.items = items;
}
public void open() {
Inventory inventory = Bukkit.createInventory(null, 54, "§6Shop - Pagina " + (page + 1));
// Item per pagina (45 slot, lasciando 9 per navigazione)
int startIndex = page * 45;
int endIndex = Math.min(startIndex + 45, items.size());
for (int i = startIndex; i < endIndex; i++) {
inventory.setItem(i - startIndex, items.get(i));
}
// Bottoni di navigazione
if (page > 0) {
ItemStack previousPage = new ItemStack(Material.ARROW);
ItemMeta meta = previousPage.getItemMeta();
meta.setDisplayName("§aPagina Precedente");
previousPage.setItemMeta(meta);
inventory.setItem(48, previousPage);
}
if (endIndex < items.size()) {
ItemStack nextPage = new ItemStack(Material.ARROW);
ItemMeta meta = nextPage.getItemMeta();
meta.setDisplayName("§aPagina Successiva");
nextPage.setItemMeta(meta);
inventory.setItem(50, nextPage);
}
// Chiudi
ItemStack close = new ItemStack(Material.BARRIER);
ItemMeta closeMeta = close.getItemMeta();
closeMeta.setDisplayName("§cChiudi");
close.setItemMeta(closeMeta);
inventory.setItem(49, close);
player.openInventory(inventory);
}
public void nextPage() {
if ((page + 1) * 45 < items.size()) {
page++;
open();
}
}
public void previousPage() {
if (page > 0) {
page--;
open();
}
}
} ```
GUI Manager Pattern
Per gestire multiple GUI in modo organizzato:
```java public class GUIManager { private final Map<UUID, GUI> activeGUIs = new HashMap<>();
public void openGUI(Player player, GUI gui) {
activeGUIs.put(player.getUniqueId(), gui);
gui.open(player);
}
public GUI getActiveGUI(Player player) {
return activeGUIs.get(player.getUniqueId());
}
public void closeGUI(Player player) {
activeGUIs.remove(player.getUniqueId());
}
}
public interface GUI { void open(Player player); void handleClick(InventoryClickEvent event); } ```
Implementazione GUI
```java public class ShopGUI implements GUI { private final Inventory inventory;
public ShopGUI() {
inventory = Bukkit.createInventory(null, 27, "§6Shop");
setupItems();
}
private void setupItems() {
// Sword - 100 coins
ItemStack sword = new ItemStack(Material.DIAMOND_SWORD);
ItemMeta swordMeta = sword.getItemMeta();
swordMeta.setDisplayName("§bSpada di Diamante");
swordMeta.setLore(Arrays.asList("§7Prezzo: §e100 coins"));
sword.setItemMeta(swordMeta);
inventory.setItem(11, sword);
// Armor - 200 coins
ItemStack armor = new ItemStack(Material.DIAMOND_CHESTPLATE);
ItemMeta armorMeta = armor.getItemMeta();
armorMeta.setDisplayName("§bArmatura di Diamante");
armorMeta.setLore(Arrays.asList("§7Prezzo: §e200 coins"));
armor.setItemMeta(armorMeta);
inventory.setItem(13, armor);
}
@Override
public void open(Player player) {
player.openInventory(inventory);
}
@Override
public void handleClick(InventoryClickEvent event) {
event.setCancelled(true);
Player player = (Player) event.getWhoClicked();
ItemStack clicked = event.getCurrentItem();
if (clicked == null) return;
if (clicked.getType() == Material.DIAMOND_SWORD) {
// Logica acquisto spada
if (hasCoins(player, 100)) {
removeCoins(player, 100);
player.getInventory().addItem(new ItemStack(Material.DIAMOND_SWORD));
player.sendMessage("§aHai acquistato una spada!");
player.closeInventory();
} else {
player.sendMessage("§cNon hai abbastanza coins!");
}
}
// ... altri item
}
private boolean hasCoins(Player player, int amount) {
// Implementa la logica per controllare i coins
return true; // Placeholder
}
private void removeCoins(Player player, int amount) {
// Implementa la logica per rimuovere coins
}
} ```
Listener Universale
```java public class UniversalGUIListener implements Listener { private final GUIManager guiManager;
public UniversalGUIListener(GUIManager guiManager) {
this.guiManager = guiManager;
}
@EventHandler
public void onClick(InventoryClickEvent event) {
Player player = (Player) event.getWhoClicked();
GUI gui = guiManager.getActiveGUI(player);
if (gui != null) {
gui.handleClick(event);
}
}
@EventHandler
public void onClose(InventoryCloseEvent event) {
Player player = (Player) event.getPlayer();
guiManager.closeGUI(player);
}
} ```
GUI con Input
Permettere al giocatore di inserire item:
```java public class TradeGUI implements GUI { private final Inventory inventory;
public TradeGUI() {
inventory = Bukkit.createInventory(null, 27, "§6Scambia Item");
setupBorders();
}
private void setupBorders() {
ItemStack border = new ItemStack(Material.GRAY_STAINED_GLASS_PANE);
ItemMeta meta = border.getItemMeta();
meta.setDisplayName(" ");
border.setItemMeta(meta);
// Riempie i bordi
for (int i = 0; i < 27; i++) {
if (i < 9 || i >= 18 || i % 9 == 0 || i % 9 == 8) {
inventory.setItem(i, border);
}
}
// Bottone conferma
ItemStack confirm = new ItemStack(Material.EMERALD);
ItemMeta confirmMeta = confirm.getItemMeta();
confirmMeta.setDisplayName("§aConferma");
confirm.setItemMeta(confirmMeta);
inventory.setItem(22, confirm);
}
@Override
public void open(Player player) {
player.openInventory(inventory);
}
@Override
public void handleClick(InventoryClickEvent event) {
int slot = event.getRawSlot();
// Permetti di modificare solo slot 10-16 (area centrale)
if (slot >= 10 && slot <= 16 && slot != 13) {
// Permetti l'interazione
return;
}
// Blocca altri slot
event.setCancelled(true);
if (slot == 22) { // Conferma
Player player = (Player) event.getWhoClicked();
processTradeItems(player);
player.closeInventory();
}
}
private void processTradeItems(Player player) {
List<ItemStack> items = new ArrayList<>();
for (int i = 10; i <= 16; i++) {
if (i == 13) continue;
ItemStack item = inventory.getItem(i);
if (item != null && item.getType() != Material.AIR) {
items.add(item);
}
}
// Processa gli item (es. converti in coins)
int totalValue = items.size() * 10; // Esempio semplificato
player.sendMessage("§aHai scambiato " + items.size() + " item per " + totalValue + " coins!");
}
} ```
Best Practices
- Cancella sempre i click: Usa
event.setCancelled(true)per impedire modifiche indesiderate - Controlla null: Gli item possono essere
null, controlla sempre prima di usarli - Usa titoli unici: Identifica le GUI tramite titoli univoci o usa un GUIManager
- Chiudi al logout: Rimuovi le GUI attive quando un giocatore si disconnette
- Ottimizza le texture: Usa
GRAY_STAINED_GLASS_PANEper bordi/decorazioni
Errori Comuni
Non cancellare l’evento
// SBAGLIATO: permette di spostare item
@EventHandler
public void onClick(InventoryClickEvent event) {
// ... logica ...
}
// CORRETTO
@EventHandler
public void onClick(InventoryClickEvent event) {
event.setCancelled(true);
// ... logica ...
}
Non controllare null
// SBAGLIATO
ItemStack item = event.getCurrentItem();
if (item.getType() == Material.DIAMOND) { } // NullPointerException!
// CORRETTO
ItemStack item = event.getCurrentItem();
if (item != null && item.getType() == Material.DIAMOND) { }
Identificare GUI solo per titolo
// PROBLEMATICO: più GUI con lo stesso titolo
if (event.getView().getTitle().equals("Menu")) { }
// MEGLIO: usa un GUIManager
GUI gui = guiManager.getActiveGUI(player);
if (gui instanceof ShopGUI) { }
Conclusione
Le GUI basate su inventari sono uno strumento potente per creare interfacce intuitive in Spigot/Paper. Con un buon design e gestione degli eventi, puoi creare shop, menu di configurazione, sistemi di trading e molto altro.