石橋を叩いて壊すページ

メッセージを送信したり矢を撃ったりする

引き続きボスモンスタープラグインを作りながら思ったことを色々書く。

プレイヤーにメッセージを伝えるには

プラグインからサーバー内のプレイヤーにメッセージを通知する方法は2つある。

特定のプレイヤーにメッセージを通知するには、Playerクラスのplayer.sendMessage(String)を使えばいい。
こうすると、他のプレイヤーにはメッセージは通知されない。

player.sendMessage("§c前のボスが倒されるまで次のボスを呼び出せません。");

もうひとつ、サーバーにログインしているすべてのプレイヤーにメッセージを同報送信するブロードキャストというものがある。
ブロードキャストを使うには、ServerクラスのbroadcastMessage(String)を使う。

getServer().broadcastMessage("§e邪悪な存在がこの地に舞い降りた…");

ちなみにチャットの先頭にある「§c」や「§e」は、文字の表示色を指定するための記号。
全部で16色ある。「§」は、「セクション」で漢字変換すれば出る。


16色すべてを試してみた例。色指定のほかにも、太字(§L)・
打ち消し線(§M)・下線(§N)・斜体(§O)・元に戻す(§R)がある。
アルファベットは大文字と小文字のどちらでもよい。

実はもうひとつ、特定の権限を持ったプレイヤーにメッセージを送信する方法もあるのだが
Bukkitの権限周りは実に面倒くさいとどこかで聞いたことがあるので、あまりお勧めしない。
でも一応調べたので、ここまでの内容をBukkitWikiにも書いておいた。

モンスターに矢を撃たせるには

モンスターに矢を打たせる方法も2つある。

まず1つは、LivingEntityクラスのlaunchProjectileメソッドを使う方法。
launchは「始める」「投げる」といった意味、Projectileはミサイルや石といった「投射物」の意味で、
launchProjectileは何かを投げるメソッドだと理解できる。

具体的には以下のようにする。

getBoss().launchProjectile(Arrow.class);

こうすると、ボスモンスターがArrow(矢)を投射してくれる。
単純に矢を放ちたいのであれば、たったこれだけでいい。


マシンガンショットはlaunchProjectileを1秒間に20回実行することで実現している。

そしてもう一つの矢の発射方法が、WorldクラスのspawnArrowメソッドを使う方法。
複雑な矢の発射をしたい場合はこちらが便利。

Arrow arrow=getBoss().getWorld().spawnArrow(loc.add(v), v, 0.6F, 12);

1つ目の引数は発射地点、2つ目は矢の発射方向、3つ目は発射速度で、4つ目には命中精度を指定する。
発射速度は0.6がBukkitの推奨値なので、これを目安に増減するといい。
命中精度は数値を大きくすると変な方向に矢が飛んでいき、ゼロにすればワンホールショットになる。Bukkitの推奨は12とされている。


シャワーショットは発射速度2.0。最初は0.6にしてあったのだが、
途中でうっかり間違えて2.0にしたら面白いスキルになったのでそのままにした。

2つ目の引数である矢の発射方向は、Vectorオブジェクトで指定する。
Vectorオブジェクトというのは3次元空間における方向を示すもので、具体的にはX・Y・Zの3つの値のセットのこと。
画像にすると以下のようなイメージになる。


Vector型のイメージ。5回描きなおして記事書く時間の3倍かかった。
絵とか図とかかける人はほんと羨ましい。

要するに、new Vector(0,1,0);なら完全に空を向いているし、new Vector(0,0,1)なら完全に南を向いているという話である。

ちなみにVector型のX・Y・Zはそれぞれ最大値は1で、最小値は-1である。
また原点Oから点P(X・Y・Z)までの2点間の距離は常に1になる。

なぜ2点間の距離が常に1になるのかは、そういうものだからである。
たとえばnew Vector(0,0,0);というVectorオブジェクトがあったとして、これはどの方向を指しているだろうか?
3つの値すべてがゼロだと、どの方向を指しているのかわからない。
また上記したとおりVectorは方向を示すもので距離は示さないので、どんなに近い場所も遠い場所も関係なく、
基準となる距離「1」をX・Y・Zの各軸ごとに分解した値を保持しているから、2点間の距離は常に1になる。

といっても、あまり難しく考える必要はない。Vector型にはnormalize()という大変便利なメソッドが用意されている。
これを使えば、new Vector(1,2,3);のようなへんな角度も、2点間の距離が1になるよう自動的に調整してくれる。
これを「正規化する」という。

Vector v = new Vector(1,2,3).normalize();

空間上での2点間の距離の計算方法はGoogle先生に聞けば教えてくれると思うが
原点Oと点P(VectorオブジェクトのX・Y・Z)の2点間の距離を調べるのであれば以下となる。

Math.sqrt(
    Math.pow(v.getX(),2)+
    Math.pow(v.getY(),2)+
    Math.pow(v.getZ(),2))

Vector型の話になってくると、三角関数(サイン・コサイン)の話は避けて通れない。
学校で習ったときは三角関数がなんの役に立つのかと思ったものだが、ゲーム関連の世界ではこれを頻繁に使うので
とっておいた教科書を引っ張り出して勉強しなおしたことがある。

まあ難しい話はさておき、Math.sin(Math.toRadians(N))とやれば、sin(N)の値が得られる。
cos(N)を得たいときはMath.cos(Math.toRadians(N))を使えばいい。
たとえば平面で、原点から45度の方向の方向を示すVector型は、以下のように書けば得られる。

Vector v = new Vector(Math.sin(Math.toRadians(45)), 0, Math.cos(Math.toRadians(45))).normalize();

さて話を戻して、launchProjectileの1つ目の変数である発射地点には、Locationオブジェクトを指定する。
プレイヤーやモンスターからgetLocation()で取り座した座標を使えば、そのキャラクターが矢を発射したようにみえる。

ただしひとつ注意。取り出した座標はキャラクターの足元の座標なので、そのまま使ってしまうとキャラクターの足の裏から矢が発射されてしまう。
座標に高さを1m程度くわえれば、腕の辺りから矢が発射されるようになるが、
今度はキャラクターの体内から矢が発射されてしまうので、発射された瞬間に矢がキャラクター自体に当たってしまう。
これを避けるためには、矢の発射地点の座標から、矢の発射方向に0.5~1ほどの数値を加えておくとよい。
例ではloc.add(v)と、位置座標に矢の発射方向のVectorの値をそのまま足している。

なお、launchProjectileメソッドで発射した矢には、どのキャラクターがその矢を発射したかの情報が含まれるが、
spawnArrowメソッドで発射した矢には、矢の発射者情報が含まれていない。
なぜならLivingEntityのlaunchProjectileは「生物(LivingEntity)に矢を撃たせる(launchProjectile)」メソッドだが、
WorldのspawnArrowは「世界(World)に矢を生み出す(spawnArrow)」メソッドだからだ。
このためlaunchProjectileメソッドで発射した矢に当たって死ぬと、「○○に射抜かれた」といったメッセージが表示されるが、
spawnArrowメソッドで発射した矢に当たると、発射者名の代わりに「Arrowに射抜かれた」と率直な事実だけが表示される。

もしspawnArrowメソッドで発射した矢に発射者情報を含ませたい場合は、spawnArrowの戻り値のArrowオブジェクトに
以下のようにキャラクターの情報を設定すればよい。

arrow.setShooter(getBoss());

また矢を火矢にしたい場合は、同様に戻り値のArrowオブジェクトを以下のようにすれば火がつく。
引数は火が消えるまでの時間で、例では10秒後に自動消火する。
前回の記事に書いたように、時間を指定するときは0.05秒単位の値を入れる必要があるので、
希望の消火時間に20をかければ期待通りの動作になる。

arrow.setFireTicks(10 * 20);

2013/09/29追記:
後半の矢を撃つ話もBukkitWikiに記載した。

この記事を評価

この記事にコメント

  1. ...

【この記事にコメント】
お名前:
コメント:

Menu