ActionScript 3.0で始めるオブジェクト指向スクリプティング

第30回マウスイベントとインスタンスの兄弟・親子の関係

前回に続き、マウスとのインタラクションを扱う。今回のお題は、マウスのイベントだ。マウスイベントはとくに複数のインスタンスが絡むので、気をつけなければならないことがいくつかある。順に、解説していこう。

マウスイベントは誰が受取るのか

まず、マウスイベントは誰が受取るのかを確かめたい。クリックしたインスタンスに決まっている、と思うかもしれない。しかし、インスタンスがタイムラインにいくつも置かれているときは、マウスイベントの発生についてもう少し詳しく知っておかなければならない。

手始めに前回と同じく、インスタンスのマウスクリックで呼出されるInteractiveObject.clickイベント(定数MouseEvent.CLICKに、リスナー関数を定義しよう。ボタンのインスタンス名は、button_mcとする。ただし、前回のスクリプト1と異なり、MovieClipシンボルの中ではなく、インスタンスbutton_mcを置いたフレームアクションとして記述する。

// タイムライン: メイン
// MovieClipインスタンスbutton_mcを配置
// フレームアクション
button_mc.addEventListener(MouseEvent.CLICK, xTrace);
function xTrace(eventObject:MouseEvent):void {
  trace(button_mc.name);
}

また、MovieClipシンボル内のフレームアクションでは、マウスポインタを指差しカーソルに変えるためにSprite.buttonModeプロパティはtrueに設定しておく図1。シンボル名は"Button"とした⁠⁠。

図1 ボタンのMovieClipシンボルのフレームアクションでSprite.buttonModeプロパティをtrueに設定
図1 ボタンのMovieClipシンボルのフレームアクションでSprite.buttonModeプロパティをtrueに設定

もちろん、[ムービープレビュー]でボタンのMovieClipインスタンスをクリックすれば、[出力]パネルにはインスタンス名"button_mc"が表示される図2⁠。

図2 ボタンのMovieClipインスタンスをクリックするとインスタンス名が[出力]される
図2 ボタンのMovieClipインスタンスをクリックするとインスタンス名が[出力]される

つぎに、ボタンのインスタンスと少し重なり合うように、別のMovieClipを前に置いた。重なりがわかりやすいように[カラー効果]の[アルファ]で半透明にしたほかは、スクリプトも設定していなければ、インスタンス名さえつけていない。

図3 ボタンのインスタンスの前に別のMovieClipインスタンスを置く
図3 ボタンのインスタンスの前に別のMovieClipインスタンスを置く

[ムービープレビュー]で確かめると、ふたつのインスタンスが重なり合った部分では、マウスポインタが指差しカーソルに変わらない図4左図⁠。それだけでなく、そこでクリックしても、InteractiveObject.clickイベントのリスナー関数は呼出されない。ボタンのMovieClipインスタンスの重なっていない部分であれば、カーソルは変わり図4右図⁠、リスナー関数も正しく動く。

図4 前に別のインスタンスが重なると後ろのMovieClipインスタンスはボタンとして振舞わない
図4 前に別のインスタンスが重なると後ろのMovieClipインスタンスはボタンとして振舞わない

ActionScript 3.0では、マウスイベントを受取れる(InteractiveObject)インスタンスが同じ階層で重なり合っている場合、つまり兄弟のインスタンス同士では手前のインスタンスが排他的にイベントを受け取る。それは、それらのインスタンスに、マウスイベントのリスナーが設定されているかどうかを問わない[1]⁠。

インスタンスのInteractiveObject.mouseEnabledプロパティfalseに設定すると、マウスイベントを受取らなくなる。このプロパティのデフォルト値はtrueだ(マウスイベントを受け取る⁠⁠。つぎのスクリプト1は、手前の矩形のMovieClipインスタンスにcover_mcという名前をつけたうえで、そのInteractiveObject.mouseEnabledプロパティをfalseにした。これで、後ろのボタンのMovieClipインスタンスが、マウスイベントを受取れるようになる図5⁠。

スクリプト1 手前のインスタンスのInteractiveObject.mouseEnabledプロパティをfalseに設定
// タイムライン: メイン
// MovieClipインスタンスbutton_mcを配置
// フレームアクション
button_mc.addEventListener(MouseEvent.CLICK, xTrace);
cover_mc.mouseEnabled = false;
function xTrace(eventObject:MouseEvent):void {
  trace(button_mc.name);
}
図5 手前のインスタンスが重なった部分でも後ろのインスタンスがマウスイベントを受け取る
図5 手前のインスタンスが重なった部分でも後ろのインスタンスがマウスイベントを受け取る

マウスイベントが発生するインスタンスとそれを処理するインスタンス

前節では、いくつかのインスタンスが同じ階層にある兄弟の関係のとき、マウスイベントを誰が受け取るかについて考えた。しかし、Flashの DisplayObjectインスタンスは、入れ子の階層構造つまり親子の関係になることも多い。つぎは、この場合のマウスイベントについて説明しよう。

イベントリスナーの関数は、引数にイベントオブジェクトを受け取った。このイベントオブジェクトのEvent.targetプロパティを調べると、イベントを受け取ったインスタンスがわかる。たとえば、前述のスクリプトと同じムービーでリスナー関数をつぎのように書替えれば、ボタンをクリックしたとき、インスタンスの文字列表現とインスタンス名が[出力]される図6⁠。

// タイムライン: メイン
// MovieClipインスタンスbutton_mcを配置
// フレームアクション
button_mc.addEventListener(MouseEvent.CLICK, xTrace);
function xTrace(eventObject:MouseEvent):void {
  var myTarget:DisplayObject = eventObject.target as DisplayObject;
  trace(myTarget, myTarget.name);
}
図6 Event.targetプロパティでイベントを受取ったインスタンスとその名前を[出力]
図6 Event.targetプロパティでイベントを受取ったインスタンスとその名前を[出力]

ここで、Event.targetプロパティの値に対して、as演算子を使った。この演算子は、データ型を評価し直す。

式 as データ型

Event.targetプロパティにはさまざまなデータ型の値が入るので、戻り値はObject型で指定されている。しかし、それではDisplayObject型の変数(myTarget)に代入できず、DisplayObject.nameプロパティにもアクセスできない。as演算子により、Event.targetプロパティの戻り値をDisplayObject型のデータに評価し直し、DisplayObject型の変数に代入できるようになるのだ。

さてそれでは、入れ子の親子関係をつくるため、ボタンの MovieClipシンボル内にMovieClipインスタンスをひとつ置く。そして、入れ子のインスタンスにはpen_mcと名前をつけた図7⁠。もちろん、子インスタンスpen_mcには、名前以外のスクリプトなどは一切設定しない。

図7 ボタンのMovieClipシンボル内にMovieClipインスタンスをひとつ置く
図7 ボタンのMovieClipシンボル内にMovieClipインスタンスをひとつ置く

つぎに、メインタイムラインには、以下のフレームアクションを記述する。先ほど試したスクリプトや前掲スクリプト1とはイベントリスナーの登録先が変わり、EventDispatcher.addEventListener()メソッドはDisplayObject.stageプロパティを参照している。

// タイムライン: メイン
// MovieClipインスタンスbutton_mcを配置
// フレームアクション
stage.addEventListener(MouseEvent.CLICK, xTrace);
function xTrace(eventObject:MouseEvent):void {
  var myTarget:DisplayObject = eventObject.target as DisplayObject;
  trace(myTarget, myTarget.name);
}

StageオブジェクトにInteractiveObject.clickイベントのリスナーを登録したので、ステージの何もないところをクリックしてもリスナー関数は呼び出される図8上図⁠。Stageオブジェクトにインスタンス名はないので、DisplayObject.nameプロパティの[出力]はnullだ。さてところが、ボタンのインスタンスbutton_mcやその入れ子のインスタンスpen_mcをクリックすると、Event.targetプロパティの値としてそれらのインスタンスの情報が[出力]パネルに表示される図8中下図⁠。

図8 クリックしたインスタンスがマウスイベントを受取ってリスナー関数が呼び出される
図8 クリックしたインスタンスがマウスイベントを受取ってリスナー関数が呼び出される
ステージをクリック
画像
ボタンのインスタンス(button_mc)をクリック
画像
ボタンの入れ子のインスタンス(pen_mc)をクリック

マウスイベントのリスナーは、Stageオブジェクトにしか登録していない。それでもリスナー関数が呼び出されるのは、マウスイベントそのものはクリックされたインスタンスが受け取っても、イベントの発生は親のインスタンスにも伝えられるからだ。つまり、マウスイベントは子から親に伝わり、子に発生したイベントは親が処理できる[2]⁠。

イベントを処理しているインスタンスは、Event.currentTargetプロパティで調べられる。上記のフレームアクションにこのプロパティ値の[出力]を加えたのが、以下のスクリプト2だ。クリックするインスタンスを変えても、このEvent.currentTargetプロパティはつねにイベントリスナーが呼出されているインスタンスを返す。

// ステージをクリック
[object Stage] null [object Stage]
// ボタンのインスタンス(button_mc)をクリック
[object Button_1] button_mc [object Stage]
ボタンの入れ子のインスタンス(pen_mc)をクリック
[object MovieClip] pen_mc [object Stage] 
スクリプト2 マウスイベントが発生したインスタンスと処理するインスタンスを[出力]する
// タイムライン: メイン
// MovieClipインスタンスbutton_mcを配置
// フレームアクション
stage.addEventListener(MouseEvent.CLICK, xTrace);
function xTrace(eventObject:MouseEvent):void {
  var myTarget:DisplayObject = eventObject.target as DisplayObject;
  var myCurrentTarget:DisplayObject = eventObject.currentTarget as DisplayObject;
  trace(myTarget, myTarget.name, myCurrentTarget);
}

マウスとインタラクションをもたせるインスタンスは、入れ子の親子になっていたり、中に兄弟のインスタンスが並べられていることはむしろ多いだろう。そのような場合のマウスイベントの扱いについて、あともう1回だけ解説に充てたい。

今回解説した次のサンプルファイルがダウンロードできます。

おすすめ記事

記事・ニュース一覧