Minecraftサーバーを動かす知識

プレイヤーをスポーン地点に移動させる

投稿:  更新:  By: HimaJyun

ここではプレイヤーやエンティティをスポーン地点に移動させる方法を解説します。

この方法を使うことで/spawnでスポーン地点に移動させるプラグインなどが作れるようになります。

スポンサーリンク

要点

これは実は何ら難しいことではなくて、プレイヤーを含むエンティティにはteleport()メソッドがあります。

読んで字のごとくエンティティを他の場所にテレポートさせるメソッドです。

スポーン地点に移動させるためには、これにWorldクラスのgetSpawnLocation()メソッドを組み合わせるだけです。

ポイントとなるのは、このWorldクラスのオブジェクトを如何にして取得するか?という事でしょう。

最小限のサンプル

まずは最小限のサンプルを見てみましょう。

以下の例はプレイヤーが今いる世界のスポーン地点に移動させる例です。とても簡単。

// Playerを取得しましょう。やり方は何でもいいです。
Player player = (Player)sender;
// Playerが今いる世界を取得
World world = player.getWorld();
// 世界のスポーン地点を取得
Location location = world.getSpawnLocation();
// 移動させる
player.teleport(location, PlayerTeleportEvent.TeleportCause.PLUGIN);
// もちろん1行で書いても構いません
//player.teleport(player.getWorld().getSpawnLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN);

// 余談: teleport()のTeleportCause.PLUGINは指定しなくても構いませんが、他のプラグインに正しく情報を伝えるために指定しておく方が良いでしょう。

基本的な処理はこれだけなのですが、Worldをどう取得するかの違いでいくつかの派生形が考えられます。

Worldの取得

teleport()にはLocationが指定できます。そのため、一言に「スポーン地点へ移動させる」と言ってもいくつかのパターンが考えられます。

  1. 今いる世界のスポーン地点に移動させる
  2. 設定した世界のスポーン地点に移動させる
  3. プレイヤーが指定した世界のスポーン地点に移動させる

作りたいプラグインに合わせて適したものを選ぶと良いでしょう。

今いる世界

一番簡単なパターンです。上で掲載した例がこのパターン。

teleport()メソッドを持っているのはエンティティですが、エンティティは同時にgetLocation()getWorld()も持っています。

そのため"今いる世界のスポーン地点に移動させる"の処理であれば追加の処理が一切必要ありません。

(ちなみに、LocationにはgetWorld()メソッドがあるので、Player.getWorld()Player.getLocation().getWorld()は同じ事です)

設定した世界

プレイヤーが今どこに居ようともあらかじめ設定などで指定した世界に移動させたい場合があります。

この場合はgetConfig()などでconfig.ymlから世界名を読み込んで、それをBukkit.getWorld()に渡すことで世界を取得します。

ただし、getWorld()は指定された世界が存在しない時(=設定が間違っているとき)にnullを返すので、エラーチェックなどは欠かさないようにしましょう。

// 設定から移動先を取得
String config = getConfig().getString("spawnTo");
// 名前から世界を取得
World world = Bukkit.getWorld(config);
// Worldがnull = 設定された世界が存在しない、エラーで終了しましょう。
if (world == null) {
    getLogger().severe("World not found");
    return;
}

// あとは同じです
// 世界のスポーン地点を取得
Location location = world.getSpawnLocation();
// 移動させる
player.teleport(location, PlayerTeleportEvent.TeleportCause.PLUGIN);

プレイヤーが指定した世界

設定した世界に移動させるパターンの応用です。コマンドの引数でプレイヤーが移動先を選べるようにします。

引数の数、権限、コンソールからのコマンド実行、引数が指定されていない場合、世界が存在しない場合など、考えられるエラーのパターンが増えます。

パターンが増えるだけで基本は同じなので、チェック漏れがないようにしっかり実装しましょう。

// プレイヤーにしか使えないので、onCommandなどで使う場合はコンソールかチェックしましょう
if (!(sender instanceof Player)) {
    sender.sendMessage("Player only");
    return;
}
// getWorld()やteleport()に必要なのでPlayerにキャストしましょう。
Player player = (Player)sender;

// 権限チェック
if(!player.hasPermission("permission.spawn")) {
    player.sendMessage("You don't have permission!");
    return;
}

// 引数がある場合は世界を取得、ない場合は今いる世界のスポーン地点を利用
World world;
if(args.length == 0) {
    // 引数がない場合
    world = player.getWorld();
} else {
    // 引数がある場合
    world = Bukkit.getWorld(args[0]);
    // 指定された世界が存在しない場合も考慮しましょう。
    if(world == null) {
        player.sendMessage("World not found");
        return;
    }
}

// 世界のスポーン地点を取得
Location location = world.getSpawnLocation();
// 移動させる
player.teleport(location, PlayerTeleportEvent.TeleportCause.PLUGIN);

引数が指定されていない場合にどうするかはご自由に、今回の例では今いる世界のスポーン地点に移動させています。

課題: 安全に移動させよう

この記事で扱いたい範囲から逸れるので今回は解説しませんが、teleport()で移動させるだけではいくつか問題があります。

  • 移動先が埋まっていた時に窒息してしまう
  • 移動先に穴が開いていた場合に落下死/奈落死してしまう
  • 移動先に溶岩があった場合に溶岩ダイブしてしまう
  • 移動先が海の底だった時に溺死してしまう

これらの事故死を防ぐための「安全なテレポート」の実装を考えてみましょう。

ちなみに、「そこが安全じゃなかったら近くにテレポート」を愚直に実装するとネザーの天井裏に出てしまったり、処理が無限ループになったりしますよ。

工夫を凝らして色々試してみてください。

今回の記事のソースコードはGitHubに掲載しているので、参考にしてみてください。