2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Minecraft Kotlinプラグイン開発 中級編 Part.3 GUIのクリック検知

Last updated at Posted at 2024-03-23

初めに

今回は前回作成したチェストGUIにクリック検知を追加していきます。

"クリック検知" というと複雑に聞こえますが
実際はインベントリ内のアイテムがクリックされた時に、
何らかのアクションを起こさせるというものなので
あまり難しく考えずに作ってみましょう!

中級編 Part.2 チェストGUIの作成
中級編 Part.4 GUIのクリック検知

目次目次と今後の展望

今回作る内容のまとめ

今回作る内容をフローチャートにしてみたのが以下の画像です。
ifを使う場所が2回あり、複雑な処理ですが頑張って作りましょう!

カスタムGUIフローチャート.jpg

プレイヤーのインベントリを保存する変数の作成

前回作成したGUIの中のアイテムが取れないように
アイテムのクリックをキャンセルする処理を作成します。

image.png

インベントリがクリックされた時に
そのインベントリが/guiで開かれた物かチェックするために
まずはプレイヤーのインベントリを保存する変数を作成します。

今回はコマンドのファイルとクリック検知のファイルが異なるため
全体で使えるcompanion objectを使用します。
今回使う変数は中級編Part.1で使った変数とは違います。

companion object: メインクラスで定義、どのファイルからでも編集可能。
※プラグインリロードで中身は消えます

メインクラスに定義

メインクラスにて変数guiMapを定義します。
ここで定義する変数はMapという種類で、キーと値の2つセットで保存できます。
Mapではキーで値を呼び出す事ができて、他の変数とは少し変わった使い方ができます。

ここではキーでUUID、値でインベントリを保存します。

TutorialPlugin.kt
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を保存します。

GuiCommand.kt
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に新しく処理を追加していきます。

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という文を追加しました。

一部のイベントではこのisCancelledtrueに変更することで
イベントのキャンセル(その行動が起きていない状態に戻す)事が出来ます。

例えばブロックが設置された時にキャンセルしてブロックを置けなくしたり、
アイテムの右クリックをキャンセルしてアイテムを右クリックできなくしたり、
色んな使い方が出来るので覚えておきましょう。

@EventHandlerはイベントの関数ごとに必須なので付け忘れに注意しましょう

この状態で一度ビルドして実行してみます。
/guiでアイテムが取れなければ成功です。

出来たらクリックした際に何か起こるようにします。

今回はダイヤをクリックされた時にゲームモードがクリエイティブ、
エメラルドだとサバイバルに変更されるようにします。

EventListener.kt
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

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?