#はじめに
はじめまして
Gongitsuneです。
趣味でマインクラフトのプラグインを作っていますが、GUIが欲しいです。
(コマンドだけだったらわかりにくい!)
そこで、GUIを作ろうとしてみたのですが難しい!
そのあといろいろ調べてみて現時点で一番いいと思う方法が見つかりました。
改善点はまだまだありますが...
#注意
初投稿なので分かりにくいところなどもたくさんあると思います。
あくまで個人的に良いと思っている方法なので、参考程度でお願いします。
コード中に出てくるCharacterは自作のクラスです。
#コード
とりあえずコードを載せます。
(読みにくかったらすいません)
早くGUIの作り方が見たいという方は GUIの作成 を見てください。
// GUIを構築するクラス
class CustomGui private constructor(
private var title: String, // GUIのタイトル
private var size: Int, // GUIのサイズ(9の倍数で54以下)
private val elements: MutableMap<Int, GuiElement> // 後述のGuiElementを保存
) {
companion object {
fun createGui(title: String, size: Int) =
CustomGui(title, size, mutableMapOf())
}
fun setElement(index: Int, element: GuiElement) = apply {
this.elements[index] = element
}
fun getElement(index: Int) = elements[index]
fun build(handler: Character) =
Bukkit.createInventory(null, size, title).apply {
elements.forEach {
this.setItem(it.key, it.value.build(handler))
}
}
}
これがGUIを構築するクラスです。
このクラスのsetElementメソッドを使って後述のGuiElementを追加していくようになります。
そしてbuildメソッドでインベントリーを実際に作成しています。
data class GuiElement private constructor(
val material: Material,
private var name: (Character) -> String,
private var lore: (Character) -> List<String>,
private var amount: (Character) -> Int,
private var handler: (InventoryClickEvent) -> Unit
) {
companion object {
fun createElement(material: Material) =
GuiElement(material, { "" }, { listOf() }, { 1 }, {})
}
fun name(op: (Character) -> String) = apply { name = op }
fun lore(op: (Character) -> List<String>) = apply { lore = op }
fun amount(op: (Character) -> Int) = apply { amount = op }
fun setHandler(op: (InventoryClickEvent) -> Unit) = apply { handler = op }
fun onClick(e: InventoryClickEvent): GuiElement {
handler(e)
return this
}
fun build(handler: Character) =
ItemStack(material).apply {
this.amount = amount(handler)
this.itemMeta = this.itemMeta?.apply {
this.setDisplayName(name(handler))
this.lore = lore(handler)
}
}
}
これが、GUI内に表示するアイテムのクラスです。
アイテムのビルド時にキャラクターごとにアイテム名や説明、個数を変えることができます。
object PlayerEvent: Listener {
@EventHandler
fun onInventoryClick(e: InventoryClickEvent) {
Guis.guiList[e.view.title.unColored().lowercase()]?.getElement(e.slot)?.onClick(e)
}
}
アイテムがクリックされたときにそのアイテムに紐づけられた処理を行うようにしています。
##GUIの作成
object Guis {
private val menu = createGui("Menu", 9)
.setElement(0, createElement(Material.BOOK)
.name { "Menu" }
.lore { listOf("&7This item is rare!".colored()) }
.setHandler { e ->
e.isCancelled = true
e.whoClicked.sendMessage("Hello world. This world is special world. I think you will have a nice experience.")
})
val guiList = mapOf(
Pair("menu", menu)
)
}
ここでGUIを実際に作成しています。
ただ、制約ができてしまってguiListのキーは対応するCustomGuiのタイトルと一致している必要があります。
(大文字小文字は一致していなくて大丈夫です)
これが一致していないとアイテムがクリックされたときの処理をうまく行うことができません。
正直ここだけ見てもらえれば、GUIの作り方はわかると思います。
##GUIを開く
object GuiCommand: CommandExecutor {
override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean {
if (sender is Player) {
val arg = args.getOrNull(0) ?: return false
when(arg) {
// GuisのguiListからGUIを取得してビルドしたものをプレイヤーに対して開いています
"open" -> Guis.guiList["menu"]?.build(Character())?.let { sender.openInventory(it) }
}
}
return true
}
}
これはコマンドで開けるようにしていますが同じようにすればどこからでも開けます。