|
マウスの位置がたちどころに分かる MouseInfo |
||||
|
||||
マウスポインタの位置に関しては苦い思いでがいっぱいあります。 ブロック崩しでもシューティングでもいいのですが、マウスポインタの位置に応じてパッドや自機の位置を変えようとします。通常は MouseMotionEvent クラスを使用して、マウスポインタの移動を検出します。 ところが、ところがです。 マウスのポインタがゲームのフレームの外側に出てしまうと、MouseMotionEvent イベントが発生しなくなってしまうのです。つまり、パッドや自機が全然動かなくなってしまうのです。遊んでいるほうはマウスポインタの位置じゃなくてマウスを右や左に動かすということだけ集中しているので、急にパッドや自機が動かなくなってしまいパニック状態に陥ってしまいます。 フレームの外側に出ても唯一イベントがあがってくるのが、マウスをドラッグしたときです。逆にいえば、これ以外の時は一切イベントが発生しませんでした。 マウスドラッグが分かるということは、ドラッグしていないときでもちゃんとマウスポインタの位置を把握できるはずなのに、できない。理不尽です。 でも、そんな杞憂は過去のこと。Tiger ではいつでもどこでもマウスポインタの位置を知ることができるようになったのです。
|
|
||||||
マウスポインタの位置を知るためのキーになるのは java.awt.MouseInfo クラスと java.awt.PointerInfo クラスです。両方とも Tiger で取りいれられたクラスです。 MouseInfo クラスには 2 つの static なメソッドだけが定義されています。1 つがマウスのボタン数を調べる MouseInfo#getNumberOfButtons メソッドと PointerInfo オブジェクトを取得する MouseInfo#getPointerInfo メソッドです。 PointerInfo クラスも 2 つのメソッドが定義されていますが、普通は PointerInfo#getLocation メソッドしか使わないと思います。getLocation メソッドの戻り値は Pointer オブジェクトで位置を知ることができます。 覚えるのはこれだけです。たった 3 つのメソッドを理解すれば、後は何も必要ありません。
このサンプルはマウスのボタンの数を出力した後に、1 秒おきに 10 回マウスポインタの位置を出力します。
マウスのボタンの数は単に MouseInfo#getNumberOfButtons メソッドを呼び出しているだけです。 マウスポインタの位置は MouseInfo#getPointerInfo メソッドで PointerInfo オブジェクトを取得し、PointerInfo#getLocation メソッドで位置を検出しています。
ここで使っているのは 2 ボタンマウスなので、はじめのボタン数は正しいです。 不思議かもしれませんが、MouseInfo クラスの getLocationInfo メソッドは GUI を一切表示しない状態でも使用できるということです。もちろん、AWT や Swing を使用していても、MouseInfo クラス/PointerInfo クラスを使用することは可能です。
|
|
||||||||
独立したクラス MouseInfo を使用すればマウスポインタの位置は取得することができることは分かりました。 でも、せっかくだから AWT や Swing のコンポーネントで使ってみたいですよね。そんなときのために用意されたのが Componennt#getMousePosition メソッドです。なぜ、PointerInfo#getLocation メソッドとメソッド名を合わせないのでしょう。何か裏があるかもしれません ^^;; まぁ、何はともあれやってみましょう。
PointerInfo クラスから取得したものと、Component クラスから取得したものを並べて書いてみました。
実行すると意外な結果が得られました。
1 回目がフレームの内部にマウスポインタがある場合、2 回目の実行がマウスポインタがフレームの外にある場合です。 まず、1 回目のほうですが、マウスポインタの位置が 2 つの方法で違っています。どういうことかというと、座標の原点が違うということです。 PointerInfo#getLocation メソッドで得られる値は画面の絶対的な座標になります。 一方の Component#getMousePosition メソッドで得られる値はそのコンポーネント左上が原点になっているのです。ようするに絶対座標ではなくて、相対座標になっているわけです。 それから考えると 2 回目の実行の結果も分かるような気がします。コンポーネントの相対座標なので、コンポーネント上にマウスポインタがなければ、相対座標がマイナスの値になってしまったりします。それはそれで私はいいと思うのですが、Tiger ではそれよりも null を返すことを選んだようです。 このように取得できる値が異なるので、getLocation と getMousePosition というように名前を変えてきたのだと思います。でも、帰るのだったらもっと分かりやすい名前にしてもらえないかなぁ。 さて、せっかくなので Component#getMousePosition メソッドがどのように処理されているのか見てみましょう。Component#getMousePosition メソッドを次に示します。
はじめの if 文はディスプレイを使用しないヘッドレスモードの場合なので本質とは関係ありません。次のブロックで PointerInfo オブジェクトを取得しています。AccessController クラスが使用されているのはセキュリティポリシーが設定できるようになっているからです。 次の if 文でマウスポインタがコンポーネント上にあるかどうかを調べており、なければ null が帰るようになっています。 最後に MouseInfo#getPointerInfo メソッドを使用して取得した PointerInfo オブジェクトをpointerRelativeToComponent メソッドを使用して相対値に変換します。pointRelativeToComponent メソッドもいっしょに見てみましょう。
getLocationOnScreen メソッドはコンポーネントが絶対座標でどこに位置しているかを調べるためのメソッドです。ですから、引き算をすれば相対値になりますね。
|
|
||||||||
いつでもマウスポインタの位置が取れるとしたら、すぐ思いつくのはあれでしょう。Windows 世代の人にはなじみがないかもしれませんが、UNIX の X11 で育った私にはやっぱり xeye です。 役に立つアプリではありませんが、Java でも同じようなものを作ってみましょう。ただし、xeye のように目のふちが丸いと計算が面倒になるので、四角にします。丸と四角の違いは本質的ではないので大目に見てください。
マウスポインタの位置に目を書くだけなので、たいしたことありません。面倒なのは、マウスがコンポーネントの外がわにあるときです。そのときにはコンポーネントの一番外側に書くようにします。 実際に目を描いているのは、EyeComponent クラスの paintComponent メソッドです。
赤の部分がマウスポインタを調べる部分です。再描画されるごとにマウスポインタの位置を調べて、描画するようにしています。translatePointRelativeToComponent メソッドは先ほどの Component#pointRelativeToComponent とまったく同じなのですが、pointRelativeToComponent メソッドがパッケージプライベートなので、同じことを書き直しただけです。 次の青い字のところが、マウスポインタがコンポーネントの外がわにある場合の処理です。ここで、width と height がコンポーネントの幅と高さになります。また、eyeWidth と eyeHeight が目の黒目の幅と高さです。 本来ならば、目の中心とマウスポインタを結んだ直線がコンポーネントの淵と交わる点を求めてそこに黒目を描くのでしょうか、今回は簡易的な方法ということで。 そして、最後に楕円を描画します。 定期的に再描画するのは JavaEye クラスの start メソッドでタイマーを設定しているからです。タイマーには java.util.Timer と javax.swing.Timer の 2 種類を使えますが、今回は GUI に特化した処理しかしないので javax.swing.Timer を使用しました。
これを実行すると、下のようなウィンドウが表示されるはずです。ぐりぐりやって遊んでみてください。
|
|
||||
こんな簡単なことがなぜいままでできなかったのでしょう。不思議ですね。 逆にいえば、Tiger でデスクトップで Java を使うことをかなり意識してきているということが分かります。せっかくの機能なので、積極的に使っていきたいものです。 ただし、Applet や JavaWebStart で使うには注意が必要です。SandBox 内でマウスの位置を取得すると SecurityException が発生してしまうので、セキュリティポリシーの設定が必要です。
今回使用したサンプルはここからダウンロードできます。
(Jul. 2004) |
|