110 lines
3.9 KiB
Markdown
110 lines
3.9 KiB
Markdown
---
|
||
front:
|
||
hard: 进阶
|
||
time: 20分钟
|
||
---
|
||
|
||
# Bukkit类与箱子GUI的实现
|
||
|
||
服务器里经常会利用箱子的GUI做“按钮菜单”功能. 有些服务器可能利用`ChestCommand`插件做出了各种花样的菜单.
|
||
如何写一个插件来实现这样的箱子GUI呢?
|
||
|
||
# Bukkit类
|
||
|
||
我们早在事件监听注册监听器时就已经见过`Bukkit`类了.
|
||
|
||
```java
|
||
Bukkit.getPluginManager().registerEvents(this,this);
|
||
```
|
||
|
||
`Bukkit`类是服务器的单例. 我们可以通过它操作服务器.
|
||
例如, 你可以用`Bukkit.banIP("某个IP")`来封禁某个IP号. 更多的用法可以在JavaDoc上查到.
|
||
|
||
你也可以利用`Server`对象操作服务器, 二者几乎没有差别(`Bukkit`类内部就是操作`Server`对象).
|
||
插件主类提供`getServer()`方法, 返回值就是一个`Server`对象.
|
||
|
||
# Inventory的使用
|
||
|
||
箱子GUI本质是一个Inventory界面. 首先我们需要创建一个Inventory对象出来.
|
||
但我们不必直接`new Inventory(...)`, 这是因为查阅JavaDocs可以得知,Inventory是一个接口,无法直接实例化。
|
||
但是`Bukkit`类给我们提供了创建`Inventory`对象的方法:
|
||
|
||
```java
|
||
Inventory inv = Bukkit.createInventory(player, 6*9, "URARA!");
|
||
//第一项是主人, 在这里可以设打开界面的玩家Player对象(还记得Inventory和箱子或玩家背包等一一对应吗)
|
||
//第二项必须是 9n (n是1≤n≤6的正整数)
|
||
//第三项是标题
|
||
ItemStack item_bk = new ItemStack(Material.DIAMOND);
|
||
|
||
//在四周设置钻石边框
|
||
//这里用这样脑残的写法是为了告诉你一个大概的意思
|
||
//我相信你实际写的时候不会这么简单粗暴解决问题的, 应该会用上循环解决, 对吧
|
||
inv.setItem(0,item_bk);
|
||
inv.setItem(1,item_bk);
|
||
inv.setItem(2,item_bk);
|
||
inv.setItem(3,item_bk);
|
||
inv.setItem(4,item_bk);
|
||
inv.setItem(5,item_bk);
|
||
inv.setItem(6,item_bk);
|
||
inv.setItem(7,item_bk);
|
||
inv.setItem(8,item_bk);
|
||
inv.setItem(9,item_bk);
|
||
inv.setItem(17,item_bk);
|
||
inv.setItem(18,item_bk);
|
||
inv.setItem(26,item_bk);
|
||
inv.setItem(27,item_bk);
|
||
inv.setItem(35,item_bk);
|
||
inv.setItem(36,item_bk);
|
||
inv.setItem(44,item_bk);
|
||
inv.setItem(45,item_bk);
|
||
inv.setItem(46,item_bk);
|
||
inv.setItem(47,item_bk);
|
||
inv.setItem(48,item_bk);
|
||
inv.setItem(49,item_bk);
|
||
inv.setItem(50,item_bk);
|
||
inv.setItem(51,item_bk);
|
||
inv.setItem(52,item_bk);
|
||
inv.setItem(53,item_bk);
|
||
|
||
ItemStack item_button1 = new ItemStack(Material.GOLD);
|
||
ItemStack item_button2 = new ItemStack(Material.ANVIL);
|
||
inv.setItem(22,item_button1);
|
||
inv.setItem(31,item_button2);
|
||
|
||
//然后可以给玩家打开这个Inventory(注意, 我们还没做限制, 这个时候玩家可以自由的在这个GUI里拿东西出来)
|
||
p.openInventory(inv);
|
||
```
|
||
|
||
然后我们监听`InventoryClickEvent`实现功能和限制:
|
||
```java
|
||
@EventHandler
|
||
public void onInventoryClick(InventoryClickEvent e){
|
||
//从这里可以看出来, 标题不是随意设置的, 我们经常用标题作为区分GUI的标志
|
||
if(e.getWhoClicked().getOpenInventory().getTitle().equals("URARA!")){
|
||
e.setCancelled(true); //这样玩家就没办法拿出来物品了
|
||
|
||
//getRawSlot获得玩家点击的格子编号
|
||
//但是玩家点击GUI之外不是格子的地方也会触发InventoryClickEvent, 需要做处理!
|
||
if(e.getRawSlot()<0 || e.getRawSlot()>e.getInventory().getSize() || e.getInventory()==null)
|
||
return;
|
||
|
||
//自从Mojang把HIM删掉以后, 能触发InventoryClickEvent的只有Player了
|
||
//目前来说可以直接把它强转成Player
|
||
Player p = (Player)e.getWhoClicked();
|
||
|
||
if(e.getRawSlot()==22){
|
||
p.sendMessage("你点击了金锭!");
|
||
p.closeInventory();
|
||
} else {
|
||
p.sendMessage("你没有点击金锭!");
|
||
p.closeInventory();
|
||
}
|
||
}
|
||
}
|
||
```
|
||
基于这个思路, 你可以做出一个有功能的箱子GUI了!
|
||
|
||
> 思考: 如果遇到了某些能够修改箱子GUI的标题的插件(比如帮助加前缀)
|
||
> 能不能利用 InventoryHolder 来区分GUI呢?
|
||
|