初めに
今回は前回作成したチェストGUIにクリック検知を追加していきます。
"クリック検知" というと複雑に聞こえますが
実際はインベントリ内のアイテムがクリックされた時に、
何らかのアクションを起こさせるというものなので
あまり難しく考えずに作ってみましょう!
前 → 中級編 Part.2 チェストGUIの作成
次 → 中級編 Part.4 GUIのクリック検知
目次 → 目次と今後の展望
今回作る内容のまとめ
今回作る内容をフローチャートにしてみたのが以下の画像です。
ifを使う場所が2回あり、複雑な処理ですが頑張って作りましょう!
プレイヤーのインベントリを保存する変数の作成
前回作成したGUIの中のアイテムが取れないように
アイテムのクリックをキャンセルする処理を作成します。
インベントリがクリックされた時に
そのインベントリが/gui
で開かれた物かチェックするために
まずはプレイヤーのインベントリを保存する変数を作成します。
今回はコマンドのファイルとクリック検知のファイルが異なるため
全体で使えるcompanion object
を使用します。
今回使う変数は中級編Part.1で使った変数とは違います。
companion object
: メインクラスで定義、どのファイルからでも編集可能。
※プラグインリロードで中身は消えます
メインクラスに定義
メインクラスにて変数guiMap
を定義します。
ここで定義する変数はMap
という種類で、キーと値の2つセットで保存できます。
Map
ではキーで値を呼び出す事ができて、他の変数とは少し変わった使い方ができます。
ここではキーでUUID、値でインベントリを保存します。
package com.github.tooooowa.tutorialplugin
import org.bukkit.inventory.Inventory
import org.bukkit.plugin.java.JavaPlugin
import java.util.*
class TutorialPlugin : JavaPlugin() {
// ここから下を新たに追加
companion object{
val guiMap: MutableMap<UUID, Inventory> = mutableMapOf()
}
// ここまで
override fun onEnable() {
// Plugin startup logic
server.pluginManager.registerEvents(EventListener, this)
getCommand("debug")?.setExecutor(DebugCommand)
getCommand("sum")?.setExecutor(SumCommand)
getCommand("gui")?.setExecutor(GuiCommand)
}
override fun onDisable() {
}
}
完成形は上の文です。
追加したのは以下の文のみです。
companion object{
val guiMap: MutableMap<UUID, Inventory> = mutableMapOf()
}
普段のval 変数名
のあとに: MutableMap<UUID, Inventory>
と書いてあります。
これは変数の型を指定しています。
今プラグインを書いている言語Kotlinでは基本的には型の指定がいりませんが、
Mapを使う時はキーと値の型に関して記述が必須です。
= mutableMapOf()
の部分では空っぽのMapを変数に入れています。
Mapの宣言ではよく型の指定、空っぽの代入のセットで使うので覚えておきましょう。
コマンドの処理に追加
GUIを開いた時に、今作成した変数にGUIを保存します。
package com.github.tooooowa.tutorialplugin // この行は環境によって異なります
import org.bukkit.Bukkit
import org.bukkit.Material
import org.bukkit.command.Command
import org.bukkit.command.CommandExecutor
import org.bukkit.command.CommandSender
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
object GuiCommand: CommandExecutor {
override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean {
if (sender !is Player){
return false
}
val inventory = Bukkit.createInventory(sender, 9 * 3, "§bメニュー")
val diamond = ItemStack(Material.DIAMOND, 1)
val diamondMeta = diamond.itemMeta
diamondMeta?.setDisplayName("§b青い宝石")
diamond.itemMeta = diamondMeta
inventory.setItem(12, diamond)
val emerald = ItemStack(Material.EMERALD, 1)
val emeraldMeta = emerald.itemMeta
emeraldMeta?.setDisplayName("§a緑色の宝石")
emerald.itemMeta = emeraldMeta
inventory.setItem(14, emerald)
TutorialPlugin.guiMap[sender.uniqueId] = inventory // この行を新たに追加
sender.openInventory(inventory)
return true
}
}
上の文が完成形です。新しく追加したのは下から5行目の以下の文のみです。
TutorialPlugin.guiMap[sender.uniqueId] = inventory
companion object
の変数を他のファイルから呼び出すには、
クラスの名前.変数名
で呼び出し可能です。
Map
に値を代入する時は変数名[キー] = 値
の形で代入します。
今回はキーとしてsender.uniqueId
(実行者のUUID)を使用しています。
値には上で使用した変数inventory
を代入しています。
クリック時のイベントの追加
インベントリをクリックされた時に呼び出される関数を作成します。
入門編Part.3で使用したEventListener.kt
に新しく処理を追加していきます。
package com.github.tooooowa.tutorialplugin
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.event.player.PlayerJoinEvent
object EventListener: Listener {
@EventHandler
fun onJoin (event: PlayerJoinEvent) {
event.joinMessage = "§e${event.player.name}がサーバーに参加しました。"
}
// ここから下に追加
@EventHandler
fun onInventoryClick (event: InventoryClickEvent) {
if (TutorialPlugin.guiMap[event.whoClicked.uniqueId] == event.inventory) {
event.isCancelled = true
}
}
// ここまで
}
上のではクリックのキャンセルまで新たに実装しました。
前回作ったonJoin
関数の下にonInventoryClick
関数を追加しています。
また、関数の中にインベントリがguiMapに保存されてるものかチェックする処理と
event.isCancelled = true
という文を追加しました。
一部のイベントではこのisCancelled
をtrue
に変更することで
イベントのキャンセル(その行動が起きていない状態に戻す)事が出来ます。
例えばブロックが設置された時にキャンセルしてブロックを置けなくしたり、
アイテムの右クリックをキャンセルしてアイテムを右クリックできなくしたり、
色んな使い方が出来るので覚えておきましょう。
@EventHandler
はイベントの関数ごとに必須なので付け忘れに注意しましょう
この状態で一度ビルドして実行してみます。
/gui
でアイテムが取れなければ成功です。
出来たらクリックした際に何か起こるようにします。
今回はダイヤをクリックされた時にゲームモードがクリエイティブ、
エメラルドだとサバイバルに変更されるようにします。
package com.github.tooooowa.tutorialplugin
import org.bukkit.GameMode
import org.bukkit.Material
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.inventory.InventoryClickEvent
import org.bukkit.event.player.PlayerJoinEvent
object EventListener: Listener {
@EventHandler
fun onJoin (event: PlayerJoinEvent) {
event.joinMessage = "§e${event.player.name}がサーバーに参加しました。"
}
@EventHandler
fun onInventoryClick (event: InventoryClickEvent) {
if (TutorialPlugin.guiMap[event.whoClicked.uniqueId] == event.inventory) {
event.isCancelled = true
// ここから下に追加
if (event.currentItem == null) return
// ダイヤモンドのときの処理
if (event.currentItem!!.type == Material.DIAMOND) {
event.whoClicked.gameMode = GameMode.CREATIVE
}
// エメラルドのときの処理
if (event.currentItem!!.type == Material.EMERALD) {
event.whoClicked.gameMode = GameMode.SURVIVAL
}
// ここまで
}
}
}
上の文が完成形です。
event.currentItem
というのがインベントリ内のクリックされたアイテムです。
まずは追加した1行目のif (event.currentItem == null) return
で
クリックされたアイテムがnull
(空)じゃないかを確かめて
null
(空)だったらreturn
(処理終了)させています。
その後、event.currentItem.type
(クリックされたアイテムの種類)によって
場合分けして処理を実行しています。
このインベントリクリックのイベントでは
event.whoClicked
がクリックしたプレイヤーになっています。
ほかとは少し違っていて特殊なので気をつけましょう。
デバッグ
これをサーバーに入れて、コマンドを実行してみます。
クリックしてゲームモードが変われば成功です。
(音がないので少し分かりにくいかもしれないです)
終わりに
今回はチェストを使用したGUIでクリックを検知する方法を学習しました。
また、イベントのキャンセルに関しても導入してみました。
自分でGUIまで作れるようになるとかなり自由度が高いプラグインを作れると思います!
初級編、中級編と少しずつTutorialPluginのコードが増えてきて読みづらくなってきました。
あともう少しで初級編、中級編で使ってきたプラグインは完成させて、
そこから上級編では毎回プラグインを新しく作ろうと思います。
毎週一本ずつ記事を投稿していて少し雑になってしまっていますが
なるべく時間がある時に一度投稿した記事を編集したりしたいと思っていますので
これからもよろしくお願いします!
とりあえず今回は終了です。お疲れ様でした!
前 → 中級編 Part.2 チェストGUIの作成
次 → 中級編 Part.4 GUIのクリック検知
目次 → 目次と今後の展望
参考
https://www.youtube.com/watch?v=hPzg4tXcu5I
https://www.youtube.com/watch?v=MenxQ76Weho