前回に続き、マウスとのインタラクションを扱う。今回のお題は、マウスのイベントだ。マウスイベントはとくに複数のインスタンスが絡むので、気をつけなければならないことがいくつかある。順に、解説していこう。
マウスイベントは誰が受取るのか
まず、マウスイベントは誰が受取るのかを確かめたい。クリックしたインスタンスに決まっている、と思うかもしれない。しかし、インスタンスがタイムラインにいくつも置かれているときは、マウスイベントの発生についてもう少し詳しく知っておかなければならない。
手始めに前回と同じく、インスタンスのマウスクリックで呼出されるInteractiveObject.clickイベント(定数MouseEvent.CLICK)に、リスナー関数を定義しよう。ボタンのインスタンス名は、button_mcとする。ただし、前回のスクリプト1と異なり、MovieClipシンボルの中ではなく、インスタンスbutton_mcを置いたフレームアクションとして記述する。
また、MovieClipシンボル内のフレームアクションでは、マウスポインタを指差しカーソルに変えるためにSprite.buttonModeプロパティはtrueに設定しておく(図1。シンボル名は"Button"とした)。
もちろん、[ムービープレビュー]でボタンのMovieClipインスタンスをクリックすれば、[出力]パネルにはインスタンス名"button_mc"が表示される(図2)。
つぎに、ボタンのインスタンスと少し重なり合うように、別のMovieClipを前に置いた。重なりがわかりやすいように[カラー効果]の[アルファ]で半透明にしたほかは、スクリプトも設定していなければ、インスタンス名さえつけていない。
[ムービープレビュー]で確かめると、ふたつのインスタンスが重なり合った部分では、マウスポインタが指差しカーソルに変わらない(図4左図)。それだけでなく、そこでクリックしても、InteractiveObject.clickイベントのリスナー関数は呼出されない。ボタンのMovieClipインスタンスの重なっていない部分であれば、カーソルは変わり(図4右図)、リスナー関数も正しく動く。
ActionScript 3.0では、マウスイベントを受取れる(InteractiveObject)インスタンスが同じ階層で重なり合っている場合、つまり兄弟のインスタンス同士では手前のインスタンスが排他的にイベントを受け取る。それは、それらのインスタンスに、マウスイベントのリスナーが設定されているかどうかを問わない[1]。
インスタンスのInteractiveObject.mouseEnabledプロパティをfalseに設定すると、マウスイベントを受取らなくなる。このプロパティのデフォルト値はtrueだ(マウスイベントを受け取る)。つぎのスクリプト1は、手前の矩形のMovieClipインスタンスにcover_mcという名前をつけたうえで、そのInteractiveObject.mouseEnabledプロパティをfalseにした。これで、後ろのボタンのMovieClipインスタンスが、マウスイベントを受取れるようになる(図5)。
マウスイベントが発生するインスタンスとそれを処理するインスタンス
前節では、いくつかのインスタンスが同じ階層にある兄弟の関係のとき、マウスイベントを誰が受け取るかについて考えた。しかし、Flashの DisplayObjectインスタンスは、入れ子の階層構造つまり親子の関係になることも多い。つぎは、この場合のマウスイベントについて説明しよう。
イベントリスナーの関数は、引数にイベントオブジェクトを受け取った。このイベントオブジェクトのEvent.targetプロパティを調べると、イベントを受け取ったインスタンスがわかる。たとえば、前述のスクリプトと同じムービーでリスナー関数をつぎのように書替えれば、ボタンをクリックしたとき、インスタンスの文字列表現とインスタンス名が[出力]される(図6)。
ここで、Event.targetプロパティの値に対して、as演算子を使った。この演算子は、データ型を評価し直す。
Event.targetプロパティにはさまざまなデータ型の値が入るので、戻り値はObject型で指定されている。しかし、それではDisplayObject型の変数(myTarget)に代入できず、DisplayObject.nameプロパティにもアクセスできない。as演算子により、Event.targetプロパティの戻り値をDisplayObject型のデータに評価し直し、DisplayObject型の変数に代入できるようになるのだ。
さてそれでは、入れ子の親子関係をつくるため、ボタンの MovieClipシンボル内にMovieClipインスタンスをひとつ置く。そして、入れ子のインスタンスにはpen_mcと名前をつけた(図7)。もちろん、子インスタンスpen_mcには、名前以外のスクリプトなどは一切設定しない。
つぎに、メインタイムラインには、以下のフレームアクションを記述する。先ほど試したスクリプトや前掲スクリプト1とはイベントリスナーの登録先が変わり、EventDispatcher.addEventListener()メソッドはDisplayObject.stageプロパティを参照している。
StageオブジェクトにInteractiveObject.clickイベントのリスナーを登録したので、ステージの何もないところをクリックしてもリスナー関数は呼び出される(図8上図)。Stageオブジェクトにインスタンス名はないので、DisplayObject.nameプロパティの[出力]はnullだ。さてところが、ボタンのインスタンスbutton_mcやその入れ子のインスタンスpen_mcをクリックすると、Event.targetプロパティの値としてそれらのインスタンスの情報が[出力]パネルに表示される(図8中下図)。
マウスイベントのリスナーは、Stageオブジェクトにしか登録していない。それでもリスナー関数が呼び出されるのは、マウスイベントそのものはクリックされたインスタンスが受け取っても、イベントの発生は親のインスタンスにも伝えられるからだ。つまり、マウスイベントは子から親に伝わり、子に発生したイベントは親が処理できる[2]。
イベントを処理しているインスタンスは、Event.currentTargetプロパティで調べられる。上記のフレームアクションにこのプロパティ値の[出力]を加えたのが、以下のスクリプト2だ。クリックするインスタンスを変えても、このEvent.currentTargetプロパティはつねにイベントリスナーが呼出されているインスタンスを返す。
マウスとインタラクションをもたせるインスタンスは、入れ子の親子になっていたり、中に兄弟のインスタンスが並べられていることはむしろ多いだろう。そのような場合のマウスイベントの扱いについて、あともう1回だけ解説に充てたい。
今回解説した次のサンプルファイルがダウンロードできます。