4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Kotlin+Spigot】2.5 カウントダウンタイマーを作る

Last updated at Posted at 2020-05-03

0. 初めに

build.gradleplugin.yml の設定は終わらせた前提で進めます。
プロジェクトを作成する を参考にメインクラスを作るまで終わらせておいてください。

また、コマンドを作成して進めていきます。
詳しい説明は 2.2 コマンドを新しく作る でしていますので省略します。

1. plugin.yml にコマンドを登録する

commands:
  timer:
    usage: "/<command> [Seconds]"
    description: "カウントダウンします"

このようなコマンドを登録しておきます。
秒数を引数としています。

2. 準備する

メッセージに色をつけたいので、getColored を用意しておきましょう。

Util.kt
object Util {
    fun getColored(text: String): String {
        return ChatColor.translateAlternateColorCodes('&', text)
    }
}

3. コマンドの処理を書く

CommandExecutor を継承した TimerCommand というオブジェクトを作成します。

TimerCommad.kt
object TimerCommand: CommandExecutor {
    override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean {
        
    }
}

引数の処理をしましょう。整数を受け取るので getOrNulltoIntOrNull を使います。

    override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean {
        val seconds = args.getOrNull(0)?.toIntOrNull()
        if(seconds == null){
            sender.sendMessage(getColored("&c秒数を入力してください"))
            return true
        }
        
    }

まずはスケジューラーを使えるようになりましょう。

4. スケジューラーを使う

スケジューラー というものは、**「何秒後に実行」「何秒毎に実行」**といった処理を行う時に使うものです。
この処理はプラグインに紐づけて行われるので、関数の引数として JavaPlugin を必要とします。
基本的には JavaPlugin を継承している Main.kt でしか関数を呼び出せません。
しかし、それではとても不便です。その為に、JavaPlugin を変数として保持しておきましょう。

Main.kt
class Main: JavaPlugin() {
    companion object {
        lateinit var plugin: JavaPlugin
    }

    override fun onEnable() {
        plugin = this
    }
}

lateinit var を使うと 後で初期化される変数 として定義できるので、エラーが出ません。
しかし、これはエラーを無視させているようなものなので多用することはオススメ出来ません。
今回は、プラグインが有効にならなければ plugin にアクセスされることがないのでこれを使っています。

使い方を紹介します。
・1秒後にメッセージを出す場合

Bukkit.getScheduler().runTaskLater(plugin, Runnable { 
    Bukkit.broadcastMessage("1秒経ちました")
}, 20)

runTaskLater は 遅れて処理を行うというものです。
第1引数として JavaPlugin を渡します。
第2引数として 行う処理を書きます。 Runnable {} の中括弧の中に書きます。
第3引数として 遅らせる時間を書きます。 単位は Tick になっています。

Tick
20 1
1 0.05

・15秒毎にメッセージを出す場合

Bukkit.getScheduler().runTaskTimer(plugin, Runnable {
    Bukkit.broadcastMessage("10秒毎に実行しています")
}, 0, 15 * 20)

第1,2,3引数は runTaskLater と同じです。
第4引数として 実行の間隔を書きます。単位は Tick です。

何秒というのを書きたいのであれば、* 20 を書くのがオススメです。
わざわざ電卓を叩いて計算する必要はありません。

・5秒毎にメッセージを出し、3回で終了する場合
今回の内容に似ています。しかし、これが一番難しいのです。
何故なら書き方が大きく変わるからです。処理内でキャンセルできる書き方があります。
勿論、これで統一すればどんな内容にも対応できます。

fun repeat3times(){
    var runCount = 0
    object: BukkitRunnable() {
        override fun run() {
            if(runCount < 3){
                Bukkit.broadcastMessage("${runCount + 1} 回目の実行です")
                runCount ++
            } else {
                cancel()
            }
        }
    }.runTaskTimer(plugin, 0, 5 * 20)
}

Runnable を使っていると、cancel() は使えません。
そして、この書き方は無名クラスというものですが、スケジューラーの定型として覚えてしまって大丈夫です。

実は、runTaskLaterRunnable を使って同じようなことができるんです。
この書き方は直感的ではないと思う人もいるかもしれないので、
説明を聞いて分からなかった人は上の書き方をすることをオススメします。

fun repeat3times(){
    repeat(3)
}

private fun repeat(sumRunCount: Int, remainRunCount: Int = sumRunCount){
    Bukkit.getScheduler().runTaskLater(plugin, Runnable {
        if(0 < remainRunCount){
            val runCount = sumRunCount - remainRunCount
            Bukkit.broadcastMessage("${runCount + 1} 回目の実行です")
            repeat(sumRunCount, remainRunCount - 1)
        }
    }, 5 * 20)
}

remainRunCount: Int = sumRunCount 関数の引数にはデフォルト値を決めておくことができます。
今回であれば、repeat(3) と書いたら repeat(3, 3) となります。
repeat(sumRunCount, remainRunCount - 1)remainCount を1減らしています。
結果的に remainRunCount は0になるので、if(0 < remainRunCount) のおかげで repeat が実行されなくなるのです。

5. タイトルを送る

image.png

こんな感じに表示するためには player.sendTitle というものが使えます。
サーバーの全員に送るので、Bukkit.getOnlinePlayers().forEach { player -> } を使います。

    private fun sendTitle(seconds: Int){
        Bukkit.getOnlinePlayers().forEach { player ->
            player.sendTitle(getColored("&9&l$seconds"), "", 0, 15, 0)
        }
    }

引数として秒数を受け取ります。
では、これらを使って実際にタイマーを作ってみましょう。

6. カウントダウンタイマーを作る

私は、runTaskTimer を使った書き方でやってみます。

    private fun countDownUsingLoop(seconds: Int){
        var remainSeconds = seconds
        object: BukkitRunnable() {
            override fun run() {
                if(0 < remainSeconds){
                    sendTitle(remainSeconds)
                    remainSeconds --
                } else {
                    cancel()
                }
            }
        }.runTaskTimer(plugin, 0, 20)
    }

先ほどのプログラムとほとんど同じなので、説明は省略します。
これを onCommand に実装したら終了です。

TimerCommand.kt
object TimerCommand: CommandExecutor {
    override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean {
        val seconds = args.getOrNull(0)?.toIntOrNull()
        if(seconds == null){
            sender.sendMessage(getColored("&c秒数を入力してください"))
            return true
        }
        countDownUsingLoop(seconds)
        return true
    }

    private fun sendTitle(seconds: Int){
        Bukkit.getOnlinePlayers().forEach { player ->
            player.sendTitle(getColored("&9&l$seconds"), "", 0, 15, 0)
        }
    }

    private fun countDownUsingLoop(seconds: Int){
        var remainSeconds = seconds
        object: BukkitRunnable() {
            override fun run() {
                if(0 < remainSeconds){
                    sendTitle(remainSeconds)
                    remainSeconds --
                } else {
                    cancel()
                }
            }
        }.runTaskTimer(plugin, 0, 20)
    }
}

runTaskLater を使った書き方も出来る人はやってみましょう。
関数名を変えて両方ともテストしてみるといいでしょう。

7. コマンドを登録する

Main.kt
class Main: JavaPlugin() {
    companion object {
        lateinit var plugin: JavaPlugin
    }

    override fun onEnable() {
        plugin = this
        registerCommand("timer", TimerCommand)
    }

    private fun registerCommand(label: String, executor: CommandExecutor){
        val command = getCommand(label)
        if(command != null){
            command.setExecutor(executor)
            logger.info("/$label を登録しました")
        } else {
            logger.severe("/$label を登録できませんでした")
        }
    }
}

では、サーバーでテストしてみましょう。

count_down.gif

お疲れ様でした。

リンク

次の記事に進む

目次に戻る

4
0
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
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?