代码式GUI创建一个简单的萌芽商店
你可能需要以下条件才可以看得懂此页面 1.你需要理解明白第一篇教程写的代码式创建GUI 2. 拥有良好的Java基础并熟悉使用Java面向对象编程(以下教程会使用面向对象思想) 3. 拥有良好的Bukkit编程基础
注意! 本示例仅做参考,实际使用需要优化。 不要直接使用这段代码,因为这段代码每次都要在主线程进行gui初始化。 生产环境中,应该异步进行耗时的gui初始化行为,同时考虑为每个玩家缓存一个gui,而不是每次都重新生成gui
首先我们编写了我们的插件 config.yml 用于控制我们要卖的东西以及价格~
ShopItems:
# 出售物品1
test-sell1:
# 出售时展示在萌芽界面的物品
show-item:
id: 20
display: '&c一个神奇的玻璃'
lore: '&b你可以用它来装饰屋子'
data: 0
# 价格 100 游戏币
price: 100
# 出售物品1
test-sell2:
# 出售时展示在萌芽界面的物品
show-item:
id: 58
display: '&c一个神奇的工作台'
lore: '&6你可以用它来合成物品~'
data: 0
# 价格 100 游戏币
price: 200
然后编写读取该 config.yml ,使其转换为 JavaBean 。
具体 SellItemManager 出售物品管理类代码可以参考如下 由于篇幅问题 采用了内部类并通过 lombok 简化了 equals 和 hashCode ,在编写代码时可将其分开以便于阅读代码。
public class SellItemManager {
@EqualsAndHashCode
public static class SellItem {
private final ItemStack showItem;
private final double price;
private final String name;
public SellItem(ItemStack showItem, double price, String name) {
this.showItem = showItem;
this.price = price;
this.name = name;
}
public ItemStack getShowItem() {
return showItem;
}
public double getPrice() {
return price;
}
public String getName() {
return name;
}
}
@Getter
private static final SellItemManager instance = new SellItemManager();
private final HashMap sellItemHashMap = new HashMap<>();
public static void init() {
// 从插件实例获取 插件 config.yml 所在的文件夹
File dataFolder = GermLearnPlugin.getInstance().getDataFolder();
// 获取 config.yml 文件
File configYamlFile = new File(dataFolder, "config.yml");
// 简单判不存在
if(!configYamlFile.exists()) {
GermLearnPlugin.getInstance().getLogger().warning("config.yml 不存在无法加载!");
return;
}
// 清理之前缓存 为 reload 插件做准备
getInstance().clear();
// 加载config.yml
YamlConfiguration config = YamlConfiguration.loadConfiguration(configYamlFile);
// 获取 yaml切片 并循环遍历 拿到对应的切片名称 用于获取我们需要的数据
for (String key : config.getConfigurationSection("ShopItems").getKeys(false)) {
// 枚举各个数据
double price = config.getDouble("ShopItems." + key + ".price", 0D);
String name = config.getString("ShopItems." + key + ".name", "Unnamed");
// 类库获取 ItemStack 详情代码可见教程最后 此处因为篇幅太大问题暂不贴出
ItemStack showItem = ItemGenerator.generateItemByYamlSection(config.getConfigurationSection("ShopItems." + key + ".show-item"));
// 创建bean实例
SellItemManager.SellItem sellItem = new SellItem(showItem, price, name);
// 放入缓存中
getInstance().putSellItem(sellItem);
}
//加载完成
GermLearnPlugin.getInstance().getLogger().info("成功加载所有橱窗物品!");
}
public void putSellItem(SellItem item) {
sellItemHashMap.put(item.getName(), item);
}
public void clear() {
sellItemHashMap.clear();
}
/**
* 从缓存中获取出售物品实例
* @param name 出售物品的名字
* @return 出售物品实例
*/
public SellItem getSellItemByName(String name) {
return sellItemHashMap.get(name);
}
/**
* 获取缓存中所有出售的物品的名字
* @return 所有出售的物品的名字
*/
public Set` getAllSellItemName() {
return sellItemHashMap.keySet();
}
}
然后我们在我们的插件主类中 onEnable 方法调用 SellItemManager.init() 方法,做完这一切以后 我们打好了所有的基础,现在我们开始编写真正的萌芽代码式GUI界面类。
具体代码如下 其中关键核心代码注释已写好。
public class SellItemGuiManager {
// 魔数 自己量的间隔距离 宽
private static final int MAGIC_LOC_WIDTH = 100;
public static void openShopGui(Player player) {
// 创建一个 萌芽 screen 用于我们进行渲染
GermGuiScreen screen = GermGuiScreen.getGermGuiScreen("演示商店");
GermGuiTexture backgroundTexture = new GermGuiTexture("背景贴图");
// 我们让背景图从 (0, 0) 点开始渲染 并且 将该图片的渲染面积设置为我们的整个客户端
backgroundTexture.setLocationX("0");
backgroundTexture.setLocationY("0");
backgroundTexture.setLocationY("0");
backgroundTexture.setHeight("h");
backgroundTexture.setWidth("w");
// 设置图片的路径
backgroundTexture.setPath("local`<->`textures/learn/bg.png");
// 将背景图加入萌芽screen 不加入不生效
screen.addGuiPart(backgroundTexture);
// 记录一下当前渲染的下标(第几个) 以便于我们去计算
int index = 0;
// 我们设置商店物品在贴图上方
int itemY = 10;
for (String sellItemName : SellItemManager.getInstance().getAllSellItemName()) {
// 我们控制 一个商店最多有 5个 物品 超过5个则不渲染 后续的进阶会贴出无限延伸的页面如何写以及如何操作分页
// 此操作已经超出了本篇教程的目的 本片教程 只写简单的部分
if(index > 5) break;
// 获取我们的SellItem实例 以便于我们取出数据
SellItemManager.SellItem sellItem = SellItemManager.getInstance().getSellItemByName(sellItemName);
// 创建一个萌芽物品槽位 展示出售的物品
GermGuiSlot slot = new GermGuiSlot("shop_slot_ " + index);
// 将槽位物品设置为我们的展示物品 clone 在这里的作用是方式我们其他地方修改了该物品导致我们缓存中的物品发生变化 若出现问题我们会排查很久
slot.setItemStack(sellItem.getShowItem().clone());
// 设置一个槽位ID 萌芽槽位每一个必须分配一个ID 否则会出错
slot.setIdentity("shop_slot_" + index);
// 设置槽位的位置 用 index 算出当前应该在哪里
slot.setLocationX(100 + (MAGIC_LOC_WIDTH * index));
slot.setLocationY(100);
slot.setLocationZ(itemY);
// 创建一个萌芽文本 展示我们的出售物品名字
GermGuiLabel label = new GermGuiLabel("shop_label_ " + index);
// 设置展示内容
label.setText("&c" + sellItem.getName());
// 设置位置
label.setLocationX(100 + (MAGIC_LOC_WIDTH * index));
label.setLocationY(140);
label.setLocationZ(itemY);
// 创建一个按钮
GermGuiButton buyButton = new GermGuiButton("shop_button_ " + index);
// 设置按钮基础贴图
buyButton.setDefaultPath("local`<->`textures/learn/btn.png");
// 设置按钮鼠标悬浮上去以后的贴图 hover
buyButton.setHoverPath("local`<->`textures/learn/btnh.png");
// 设置位置和渲染大小
buyButton.setLocationX(100 + (MAGIC_LOC_WIDTH * index));
buyButton.setLocationY(190);
buyButton.setLocationZ(itemY);
buyButton.setWidth("120");
buyButton.setHeight("20");
// 注意 此部分很重要!
// 我们向萌芽按钮注册了一个左键点击事件的监听器
// 使用了 Java 函数式编程 lambda 表达式
// 此处的 clickPlayer 是 点击的玩家的实例
// 此处的 btn 是 点击的萌芽按钮的实例
buyButton.registerCallbackHandler((clickPlayer, btn) -> {
// 判断玩家是否有足够的钱
if (!VaultUtils.getEconomy().hasMoney(clickPlayer, sellItem.getPrice())) {
clickPlayer.sendMessage("§c你没有足够的钱, 需要 " + sellItem.getPrice());
return;
}
// 扣除钱
VaultUtils.getEconomy().takeMoney(clickPlayer, sellItem.getPrice());
clickPlayer.sendMessage("§a你已成功购买");
// 将我们出售的物品给予他
PlayerUtils.giveItem(clickPlayer, sellItem.getShowItem().clone());
// 如果我们想实现按钮只可以在本次页面点一次我们就可以在这里设置btn的属性 萌芽会自动更新
// 执行以下代码即可
// btn.setEnable(false);
}, GermGuiButton.EventType.LEFT_CLICK);
// 将以上所有组件加到 萌芽screen 中
screen.addGuiPart(slot);
screen.addGuiPart(label);
screen.addGuiPart(buyButton);
// 渲染自增
index ++;
}
// 我们完成了所有的工作 现在让萌芽将此GUI渲染给玩家
screen.openGui(player);
}
}
完成所有上述操作后 我们向 Bukkit 注册一个指令 我这里注册的是 /germlearn open 用于打开这个商店页面,各位可自行选择打开商店的操作。
示例代码如下 我调用了自己的库 大家可以自行编写Bukkit指令进行操作
@AutoRegister(root = "germlearn", sub = "open")
public class OpenCommand implements ISubCommand {
@Override
public void runCommand(CommandSender commandSender, String[] strings) {
// 判断是否由玩家执行
if(commandSender instanceof Player) {
Player player = (Player) commandSender;
// 给玩家打开商店页面
SellItemGuiManager.openShopGui(player);
}
}
@Override
public String display() {
return "打开商店";
}
@Override
public String usage() {
return "open";
}
}
编写插件以后记得在 plugin.yml 中的 depend 中写入 GermPlugin 依赖于GermPlugin运行 否则你的插件可以早于GermPlugin加载以至于出现错误。
下面我们进入游戏进行测试~
可以看到我们插件已经正常加载读取了数据
我们进入游戏输入 open 指令

可以看到了我们的商店 虽然很丑 但是本教程只是演示代码式创建GUI的过程 后续的美化工作就交给各位啦~ 我们点击了一下购买按钮 发现我们的购买逻辑也是能够成功跑通的~

后续补充 在此贴上上述代码使用的获取物品的代码 有需要的可以拿去用 代码质量一般hh
public static ItemStack generateItemByYamlSection(ConfigurationSection section) {
String type = section.getString("type");
ItemStack stack;
short data = (short) section.getInt("data", 0);
if(type != null && !type.isEmpty()) {
stack = new ItemStack(Material.getMaterial(type));
}else {
int id = section.getInt("id");
stack = new ItemStack(Material.getMaterial(id));
}
stack.setAmount(1);
stack.setDurability(data);
String display = section.getString("display", "unnamed").replace("&", "§");
List` lore = section.getStringList("lore");
if(lore == null) lore = new ArrayList<>();
for(int i = 0; i < lore.size(); i++) {
lore.set(i, lore.get(i).replace("&", "§"));
}
ItemMeta meta = stack.getItemMeta();
if(meta == null) {
meta = Bukkit.getItemFactory().getItemMeta(stack.getType());
}
meta.setDisplayName(display);
meta.setLore(lore);
stack.setItemMeta(meta);
return stack;
}