サイトトップ

Director Flash 書籍 業務内容 プロフィール

Flash OOP for ActionScript 3.0

第2章 ActionScript 2.0から3.0へのガイド
◎2-4 マウスイベントの扱い

●2-4-1 マウスイベントを受取る
ActionScript 3.0では、イベントの扱い方と考え方が2.0と基本的に変わりました。本節ではマウスイベントを採上げて、そのポイントを解説します。

2-4-1-1 ボタンとしての振舞い ー Sprite.buttonModeプロパティ
ActionScript 2.0では、ボタンのクリックはMovieClip.onReleaseイベントハンドラメソッドで処理しました。たとえば、ボタンとして使うMovieClipシンボルのフレームアクションとして、つぎのスクリプトを記述します。

// MovieClip: ボタンとして使用
// フレームアクション: ActionScript 2.0
onRelease = xTrace;
function xTrace():Void {
  trace(this);
}

ActionScript 3.0は、前述(2-2-3-1「イベントオブジェクトを受取る」)のとおり、InteractiveObject.clickイベントにリスナー関数を登録してイベントを受取ります。

// MovieClip: ボタンとして使用
// フレームアクション: ActionScript 3.0
addEventListener(MouseEvent.CLICK, xTrace);
function xTrace(eventObject:MouseEvent):void {
  trace(this, name);
}

上記のActionScript 2.0と3.0のスクリプトの動作結果を比べてみると、ひとつ違いに気づきます。ActionScript 2.0のイベントハンドラメソッドMovieClip.onReleaseを設定すると、インスタンスにマウスポインタを重ねたとき指差しカーソルに変わります。ところが、ActionScript 3.0でInteractiveObject.clickイベントにリスナーを登録した場合、インスタンスをクリックするとリスナー関数は実行されるものの、インスタンス上でマウスポインタが変化しません(図2-4-001)。

▲図2-4-001■MovieClip.onReleaseとInteractiveObject.clickの違い
Flash_OOP_2-2-001.gif   Flash_OOP_2-2-001.gi2
△ActionScrpt 2.0のMovieClip.onReleaseイベントハンドラメソッドを設定したインスタンスは、マウスポインタが指差しカーソルに変わる(左図)。3.0のInteractiveObject.clickイベントにリスナーを登録しても、マウスポインタは変化しない(右図)。

ActionScript 2.0では、MovieClip.onReleaseイベントハンドラメソッドなどon()イベントハンドラアクションで指定されたイベントは、ボタンが扱うものとされ、ポインタはデフォルトで指差しカーソルに変わりました。しかし、ActionScrpt 3.0のマウスイベントにリスナーを登録しただけでは、インスタンスはボタンとしての振舞いをしません。ボタンとしての動作をさせるためには、Sprite.buttonModeプロパティをtrueに設定する必要があります(スクリプト2-4-001)。

▲スクリプト2-4-001■MovieClipインスタンスをボタンとして動作させる

// MovieClip: ボタンとして使用
// フレームアクション
buttonMode = true;
addEventListener(MouseEvent.CLICK, xTrace);
function xTrace(eventObject:MouseEvent):void {
  trace(this, name);
}

Sprite.buttonModeプロパティをtrueに設定する必要がある。

[ムービープレビュー]で確かめると、MovieClipインスタンスにマウスポインタがロールオーバーしたとき、ポインタが指差しカーソルに変わります(図2-4-002)。

▲図2-4-002■Sprite.buttonModeプロパティをtrueに設定
Flash_OOP_2-4-003.gif
△インスタンスがボタンとして動作し、マウスポインタは指差しカーソルに変わる。

[MEMO]2-4-001 MovieClipインスタンスの[出力]結果
ActionScript 2.0/1.0では、MovieClipインスタンスをtrace()関数で[出力]すると、そのパスが表示されました。ActionScript 3.0は、クラスのインスタンスをtrace()関数に渡すと、文字列表現(toString()メソッドの戻り値)としてクラス名を[object クラス名]のかたちで[出力]します。

クラスが設定されていないMovieClipシンボルのインスタンスをtrace()関数の引数に指定した場合は、シンボル名と整数の組合わさった名前が[出力]されるようです(前掲図2-4-002参照)。シンボル名が日本語(2バイト文字)ですと、その部分は"Timeline"という名前に置替えられます。


[MEMO]2-4-002 MovieClipインスタンスのボタンとしての動作
MovieClipシンボルは、"_up"、"_over"、"_down"という名前のフレームラベルを設定することにより、ボタンのアニメーションを実行させることができます(詳しくは、筆者サイトFumioNonaka.com「ActionScript 3.0に対応したMovieClipのボタンをつくる」<http://www.fumiononaka.com/TechNotes/Flash/FN0706001.html>をご参照ください)。

Sprite.buttonModeプロパティをtrueにすることは、マウスポインタを指差しカーソルに変化させるだけでなく、このボタンアニメーションを有効にするためにも必要とされます。

2-4-1-2 ダブルクリックを受取る ー InteractiveObject.doubleClickEnabledプロパティ
ActionScript 3.0には、マウスイベントとしてダブルクリックが備わりました。イベントは、InteractiveObject.doubleClickです。ところが、このイベントに対して通常どおりリスナー関数を登録しても、ダブルクリックがイベントとして受取られません。

// MovieClip: ダブルクリックを受取るインスタンス
// フレームアクション
addEventListener(MouseEvent.DOUBLE_CLICK, xTrace);   // イベントが受取られない
function xTrace(eventObject:MouseEvent):void {
  trace(this, name, eventObject.type);
}

ダブルクリックをイベントとして受取るには、インスタンスのInteractiveObject.doubleClickEnabledプロパティをtrueに設定しなければなりません(デフォルト値はfalse)。なお、併せてInteractiveObject.clickイベントにもリスナー関数を登録する(スクリプト2-4-002)と、ダブルクリックでマウスボタンを押す操作の最初の1回目はクリック(InteractiveObject.clickイベント)として、2回目がダブルクリック(InteractiveObject.doubleClickイベント)としてイベントを受取ります。

▲スクリプト2-4-002■クリックとダブルクリックを受取る

// MovieClip: ダブルクリックを受取るインスタンス
// フレームアクション
doubleClickEnabled = true;
addEventListener(MouseEvent.CLICK, xTrace);
addEventListener(MouseEvent.DOUBLE_CLICK, xTrace);
function xTrace(eventObject:MouseEvent):void {
  trace(this, name, eventObject.type);
}

△ダブルクリックのマウスボタン操作の1回目はクリック、2回目をダブルクリックとしてイベントが受取られる。

すでに第2節(2-2-3-1「イベントオブジェクトを受取る」)で簡単に触れたとおり、リスナー関数が受取るイベントオブジェクトには、イベントに関わるさまざまな情報がプロパティとして含まれています。その中のEvent.typeプロパティは、発生したイベント名の文字列、つまりイベント定数(上記スクリプト2-4-002ではMouseEvent.CLICKあるいはMouseEvent.DOUBLE_CLICK)の値をもっています(図2-4-003)。したがって、複数のイベントにひとつのリスナー関数を登録した場合でも、Event.typeプロパティの値を調べれば、発生したイベントに応じた処理を記述することができます。

▲図2-4-003■クリックとダブルクリックのイベントに同じリスナー関数を登録
Flash_OOP_2-2-004.gif
Event.typeプロパティの値で、発生したイベントがわかる。

[MEMO]2-4-003[ヘルプ]の「InteractiveObject.doubleClickEnabled」の項の説明
[ActionScript 3.0コンポーネントリファレンスガイド]→[InteractiveObject]の「InteractiveObject.doubleClickEnabled」の項には、「このプロパティを設定すると、イベントは送出されなくなります」と説明されています。しかし、これは邦訳の誤りだと思われます。英語の原文は、"No event is dispatched by setting this property"(このプロパティを設定しても、イベントは配信されません)と述べているだけです。つまり、イベントリスナーの登録が別途必要であることを示しているのです。


●2-4-2 イベントのターゲット
マウスイベントを誰か背受取るかという、イベントのターゲットとなるインスタンスの捉え方が、ActionScript 3.0では2.0/1.0とは変わりました。マウスイベントを受取るインスタンスに、もうひとつ別のインスタンスを加えて、その違いについて確認しましょう。

2-4-2-1 重なり合ったインスタンスに対するマウスイベントの発生
ActionScript 2.0は、イベントハンドラを設定したインスタンスがイベントを受取ります。そして、ボタンに関わる(on()イベントハンドラアクションに指定される)イベントは、最前面のインスタンスに対して排他的に発生します。たとえば、タイムラインに配置したMovieClipインスタンスmy_mcに、つぎのようにテスト用のMovieClip.onReleaseイベントハンドラメソッドを設定したとします。

// タイムライン: メイン
// フレームアクション
// マウスイベントを受取るMovieClipインスタンスmy_mcを配置
my_mc.onRelease = xTrace;
function xTrace():Void {
  trace(this);
}

その前面に別のMovieClipインスタンスを配置したとしても、そのインスタンスにマウスイベントのハンドラが設定されていないかぎり、マウスイベントは受取りません。したがって、その背面にある上記イベントハンドラメソッドを設定したMovieClipインスタンスmy_mcが、マウスポインタに反応します(図2-4-004左図)。

そこで、前面に重ねたMovieClipインスタンスfront_mcに対して、以下のような空のイベントハンドラメソッドを追加します。すると、マウスイベントのハンドラをもつ最前面のインスタンスfront_mcが、マウスイベントを独占して奪ってしまいます。そのため、背後のMovieClipインスタンスmy_mcにはマウスイベントが渡らず、そのイベントハンドラメソッドも呼出されない結果となります(図2-4-004右図)。

// タイムライン: メイン
// フレームアクション
// マウスイベントを遮るMovieClipインスタンスfront_mcを配置
front_mc.onRelease = undefined;
front_mc.useHandCursor = false;

▲図2-4-004■MovieClip.onReleaseハンドラを設定したインスタンスの前面に別のMovieClipを重ねる
Flash_OOP_2-4-007.gif   Flash_OOP_2-4-006.gif
△前面のMovieClipにイベントハンドラがなければ、背後のインスタンスはマウスイベントを受取る(左図)。前面のMovieClipにマウスのイベントハンドラがあると、マウスイベントは奪われる(右図)。

ところが、ActionScript 3.0では、イベントリスナーを登録したかどうかに拘らず、すべてのMovieClipインスタンスがマウスイベントのターゲットになります。たとえば、タイムラインに配置したMovieClipインスタンスmy_mcに、InteractiveObject.clickイベントのリスナー関数を以下のように登録したとします。そして、その前面に別のMovieClipインスタンスを重ねると、そのインスタンスにはイベントリスナーは何も設定していなくても、背後のMovieClipインスタンスmy_mcにはマウスイベントが渡らなくなります(図2-4-004右図)。

// タイムライン: メイン
// フレームアクション
// マウスイベントを受取るMovieClipインスタンスmy_mcを配置
my_mc.buttonMode = true;
my_mc.addEventListener(MouseEvent.CLICK, xTrace);
function xTrace(eventObject:MouseEvent):void {
  trace(eventObject.target, eventObject.target.name);
}

前面のMovieClipインスタンスfront_mcがマウスイベントを受取らないようにするには、InteractiveObject.mouseEnabledプロパティをfalseに設定する必要があります(スクリプト2-4-003)。すると、背面のMovieClipインスタンスmy_mcに、マウスイベントが渡ります。そのため、マウスポインタも、指差しカーソルに変わります(図2-4-005)。なお、イベントオブジェクトのEvent.targetプロパティは、イベントの発生したインスタンスを参照します。

▲スクリプト2-4-003■InteractiveObject.mouseEnabledプロパティをfalseに設定

// タイムライン: メイン
// フレームアクション
// マウスイベントを受取るMovieClipインスタンスmy_mcを配置
my_mc.buttonMode = true;
my_mc.addEventListener(MouseEvent.CLICK, xTrace);
function xTrace(eventObject:MouseEvent):void {
  trace(eventObject.target, eventObject.target.name);
}
front_mc.mouseEnabled = false; // 前面のインスタンス

△インスタンスがマウスイベントを受取らなくなる。

▲図2-4-005■InteractiveObject.mouseEnabledプロパティをfalseにするとマウスイベントを奪わない
Flash_OOP_2-4-008.gif
△背面のMovieClipインスタンスにマウスイベントが渡されるので、ポインタも指差しカーソルに変わる。

[MEMO]2-4-004 マウスイベントを受取るクラス
マウスイベントは、InteractiveObjectクラスが受取ります。InteractiveObjectクラスを継承するのは、MovieClipのほか、SpriteやSimpleButton、TextFieldクラスなどです。したがって、これらのインスタンスが、マウスイベントのターゲットになります。

2-4-2-2 表示リストの階層におけるマウスイベントの発生
インスタンス同士が同じ階層にあるのでなく、親子関係の入れ子になっている場合、イベントはどのように扱われるのでしょうか。ActionScript 2.0/1.0のボタンの(on()ハンドラで指定される)マウスイベントは、親のインスタンスが排他的に受取ります。ですから、子のインスタンスにマウスイベントを扱うハンドラを記述しても、多くの場合意図しない動作になります。

他方で、ActionScript 2.0/1.0には、イベントが発生したインスタンスを問わない(onClipEvent()ハンドラで指定される)マウスイベントもあります。たとえば、MovieClipインスタンスmy_mcのフレームアクションに、以下のようにMovieClip.onMouseMoveイベントハンドラメソッドを記述してみます。

// MovieClip: テスト用
// フレームアクション
function onMouseMove():Void {
   trace(this);
}

マウスイベントの対象となるインスタンスは特定されませんので、マウスポインタがMovieClipインスタンス上になくても、イベントは発生します(図2-4-006)。また、複数のインスタンスに同じイベントハンドラを設定すれば、それらのインスタンスはイベントを同時に受取ります。

▲図2-4-006■インスタンスにMovieClip.onMouseMoveイベントハンドラメソッドを設定
Flash_OOP_2-4-009.gif
△イベントの発生したインスタンスを問わないので、マウスポインタがインスタンス上になくてもイベントを受取る。

ActiolnScript 3.0のマウスイベントは、イベントのターゲットがマウスポインタの座標にある最前面のインスタンスに特定されます。したがって、つぎのようにMovieClipインスタンスのInteractiveObject.mouseMoveイベントにリスナー関数を登録すると、マウスポインタがインスタンス上にあるときしかイベントが発生しません。

// MovieClip: テスト用
// フレームアクション
addEventListener(MouseEvent.MOUSE_MOVE, xTrace);
function xTrace(eventObject:MouseEvent):void {
   trace(eventObject.target, eventObject.target.name);
}

けれども、ActionScript 3.0のマウスイベントでは、そのインスタンスの属する表示リストの上層、つまり親や祖先は、子に発生したイベントが扱える仕組みになっています。そして、表示リストの頂点に存在するStageオブジェクトは、ステージ上のすべてのマウスイベントが受取れます。

▲スクリプト2-4-004■ステージ上のマウスポインタの移動を受取る

// MovieClip: 任意のインスタンス
// フレームアクション
stage.addEventListener(MouseEvent.MOUSE_MOVE, xTrace);
function xTrace(eventObject:MouseEvent):void {
  trace(eventObject.target, eventObject.target.name);
}

△Stageオブジェクトは、ステージ上のすべてのマウスイベントを受取る。

[ムービープレビュー]で確かめると、ステージ全体でマウスポインタの動きに対してイベントが発生します。ただし、イベントの発生しているターゲットを示すイベントオブジェクトのEvent.targetプロパティは、MovieClipインスタンスの領域とその外では値が変化します。インスタンス上ではそのインスタンスの参照を、何もオブジェクトのない領域ではStageインスタンスを返します(図2-4-007)。

▲図2-4-007■StageにMovieClip.onMouseMoveイベントハンドラメソッドを設定
Flash_OOP_2-4-010.gif
△イベントの発生したインスタンスであるEvent.targetプロパティの値は、マウスポインタがMovieClipインスタンス上かその外かによって変化する。


●2-4-3 イベントの発生と処理
ActionScript 3.0では、イベントの発生するターゲットのインスタンスとは別に、イベントリスナーが登録されてイベントを処理するインスタンスが存在します。マウスイベントでは、イベントの発生したターゲットインスタンス自身のほか、そのインスタンスを表示リストのツリーに含む上層、つまり親や祖先のDisplayObjectContainerインスタンスがイベントを扱えます。そこで、イベントの発生するインスタンスとその処理を行うインスタンスについて、ロールオーバーとロールアウトのマウスイベントを採上げて説明しましょう。

[MEMO]2-4-005 イベントの発生するインスタンスと処理するインスタンス
デフォルトでは、イベントはまずターゲットのインスタンスに発生して、それから順に親(上層)に伝えられます。イベントの流れについて詳しくは、[ヘルプ]→[ActionScript 3.0のプログラミング]→[イベントの処理]→[イベントフロー]をお読みください。

なお、イベントによってはターゲットに対して発生するだけで、親に渡されないものもあります。たとえば、DisplayObject.enterFrameイベントがその例です。

2-4-3-1 InteractiveObject.mouseOverとInteractiveObject.rollOverイベント
マウスポインタのロールオーバーを扱うイベントには、InteractiveObject.mouseOverInteractiveObject.rollOverが定義されています。このふたつのイベントの違いは、イベントが発生するターゲットインスタンスの捉え方にあります。サンプルムービーとして、ステージに空のMovieClipインスタンスparent_mcを置き、その中にふたつのMovieClipインスタンスpen0_mcとpen1_mcを入れ子にします。そして、ふたつの子のインスタンスにはスクリプトは書かず、親のparent_mcにつぎのフレームアクションを記述します(スクリプト2-4-005)。

[MEMO]2-4-006 ロールアウトを扱うイベント
マウスポインタのロールアウトを扱うイベントは、ロールオーバーのイベントInteractiveObject.mouseOverInteractiveObject.rollOverに対応して、それぞれInteractiveObject.mouseOutInteractiveObject.rollOutになります。


▲スクリプト2-4-005■InteractiveObject.mouseOverとInteractiveObject.rollOverイベントにリスナー関数を登録

// MovieClip: マウスイベントを処理するparent_mc
// MovieClipインスタンスpen0_mcとpen1_mcを配置
// フレームアクション
addEventListener(MouseEvent.MOUSE_OVER, xTrace);
addEventListener(MouseEvent.ROLL_OVER, xTrace);
function xTrace(eventObject:MouseEvent):void {
  trace(eventObject.type,
    eventObject.target.name,
    eventObject.currentTarget.name);
}

Event.currentTargetプロパティは、リスナー関数の登録を受けてイベントを処理しているインスタンスを返す。

スクリプト2-4-005は、親インスタンスparent_mcのふたつのイベント(定数MouseEvent.MOUSE_OVERMouseEvent.ROLL_OVER)に、同じリスナー関数xTraceを登録しています。関数xTrace()ではイベントオブジェクトから、イベント名を示すEvent.typeとイベントの発生したインスタンスであるEvent.targetの名前(DisplayObject.name)に加えて、Event.currentTargetの名前を[出力]しています。

このEvent.currentTargetプロパティが、リスナー関数の登録を受けて、イベントを処理しているインスタンスになります。ですから、スクリプト2-4-005を[ムービープレビュー]で試し、子のインスタンスの一方、たとえばpen0_mcにマウスポインタをロールオーバーすると、つぎのように[出力]されます。

rollOver parent_mc parent_mc
mouseOver pen0_mc parent_mc

リスナー関数は親のインスタンスparent_mcに登録しました。したがって、どちらの[出力]もEvent.currentTargetプロパティはparent_mcを参照しています。ところが、Event.targetプロパティが、InteractiveObject.rollOverではやはりparent_mcであるのに対して、InteractiveObject.mouseOverは実際にマウスポインタを重ねたpen0_mcになっています。

▲図2-4-008■InteractiveObject.mouseOverとInteractiveObject.rollOverのイベント発生の違い
Flash_OOP_2-4-011.gif
InteractiveObject.mouseOverイベントは、内包する子のインスタンスごとにイベントの発生を区別。InteractiveObject.rollOverでは、リスナーを登録した親インスタンスでまとめてイベントを捉えている。

ふたつの子のインスタンスは一部を重ね合わせておいて、マウスポインタをその重なり場所から、もう一方の子のインスタンスpen1_mc上に移動してみます。すると、InteractiveObject.mouseOverイベントは発生するものの、InteractiveObject.rollOverはイベントが起こりません(図2-4-008)。つまり、InteractiveObject.mouseOverイベントでは、内包する子のインスタンスひとつひとつについてイベントの発生を区別しています。それに対して、InteractiveObject.rollOverでは、中身の子はすべてひとまとめにして、リスナーを登録した親インスタンスでイベントを捉えているということがわかります。

2-4-3-2 子のインスタンスに対するマウスイベントの発生を止める
マウスイベントを、内包する子のインスタンスごとに捉えるInteractiveObject.mouseOverなどで、子のインスタンスに対するマウスイベントを止め、親インスタンスでまとめて受取りたい場合があります。

ひとつの方法は、子のインスタンスについて、前述のInteractiveObject.mouseEnabledプロパティをfalseに設定することです。すると、その子のインスタンスに対するマウスイベントは、親インスタンスがターゲットとして認識されるようになります。ただし、その設定をしていない子インスタンスは、引続き自身をターゲットとしてマウスイベントを受取ります。

子インスタンスについて個別にマウスイベントを発生させず、まとめて親インスタンスのイベントとして受取りたいときは、その親インスタンスのDisplayObjectContainer.mouseChildrenプロパティをfalseにします。たとえば、前記スクリプト2-4-005に以下のステートメントを加えると、子インスタンスpen0_mcとpen1_mcに対するマウスイベントがparent_mcに対して発生し、InteractiveObject.mouseOverInteractiveObject.rollOverイベントの動作が同じになります。

// MovieClip: マウスイベントを処理するparent_mc
// MovieClipインスタンスpen0_mcとpen1_mcを配置
// フレームアクションに追加
mouseChildren = false;

つまり、イベントのターゲットインスタンス(Event.targetの値)はともにparent_mcとなり、一部が重なり合う子インスタンスpen0_mcとpen1_mcとの間でマウスポインタを移動しても新たなイベントは発生しません。

[*筆者用参考] akihiro kamijo「Spriteとマウスイベント」。


●2-4-4 親子によるイベントの連携処理
マウスイベントの発生したターゲットインスタンスだけでなく、親インスタンスがその処理を行えることでどのような利点があるでしょう。それは、親と子が連携して、イベントを扱えることです。マウスイベントについてもう少し解説したうえで、親子で連携してイベントを処理する具体的な例についてご紹介します。

2-4-4-1 InteractiveObject.clickとInteractiveObject.mouseUpイベント
マウスボタンを放す操作については、InteractiveObject.mouseUpイベントが定義されています。このイベントとInteractiveObject.clickとの違いを確認しておきます。前記スクリプト2-4-005と同じムービー構造で、つまり親インスタンスparent_mcの中にふたつの子インスタンスpen0_mcとpen1_mcを内包したうえで、parent_mcにつぎのフレームアクションを記述してみます。

// MovieClip: マウスイベントを処理するparent_mc
// MovieClipインスタンスpen0_mcとpen1_mcを配置
// フレームアクション
addEventListener(MouseEvent.CLICK, xTrace);
addEventListener(MouseEvent.MOUSE_UP, xTrace);
function xTrace(eventObject:MouseEvent):void {
  trace(eventObject.type,
      eventObject.target.name,
      eventObject.currentTarget.name);
}

このスクプトを[ムービープレビュー]で試してみると、InteractiveObject.mouseUpイベントがマウスボタンを放す操作のみを捉えていることがわかります。すなわち、マウスボタンをどこで押したかを問わず、インスタンスparent_mc(内包する子インスタンスを含む)の上でマウスボタンを放すと、InteractiveObject.mouseUpイベントが発生します。

これに対して、InteractiveObject.clickイベントは、インスタンスの上でマウスボタンを押し、かつ同じインスタンス上でボタンを放したときに呼出されます。ただし、子インスタンスは区別します。したがって、上記フレームアクションで、子インスタンスpen0_mcの上でマウスボタンを押したままポインタをpen1_mcの領域に移動して、そこでボタンを放した場合、マウスボタンを押す操作と放す操作の行われた子インスタンスが異なりますので、InteractiveObject.clickイベントは発生しません(図2-4-009)。

▲図2-4-009■InteractiveObject.clickとInteractiveObject.mouseUpイベントの違い
Flash_OOP_2-4-012.gif
InteractiveObject.mouseUpイベントは、マウスボタンを放す操作のみを捉える。InteractiveObject.clickイベントでは、マウスボタンを押す操作と放す操作の行われた子インスタンスが区別される。

通常は、マウスボタンを放す操作のみを捉えて行う処理はあまりありません。InteractiveObject.mouseUpイベントは、マウスボタンを押したとき発生するInteractiveObject.mouseDownイベントと組合わせて用いられます。

2-4-4-2 onReleaseOutsideイベントの実装
ActionScript 2.0/1.0には、インスタンス上でマウスボタンを押しながら、インスタンスの外で放した場合、MovieClip.onReleaseOutsideイベントハンドラメソッドで扱うことができました。ところが、ActionScript 3.0のマウスイベントには、これに相当するものがありません。したがって、インスタンス上でマウスボタンを押し、かつインスタンス外で放したという操作を、スクリプトで処理する必要があります。

まず、スクリプトの構成を考えます。インスタンスの上でマウスボタンを押す操作は、InteractiveObject.mouseDownイベントで捉えられます。問題はその後、マウスボタンを放す操作です。そのイベントを、インスタンス上とインスタンス外の2箇所で待受けなければなりません。そこで、インスタンスとStageが連携して、イベントを処理することになる訳です。

処理の流れは、テニスのダブルスにも似ています。前衛はボタンとなるMovieClipインスタンスで、後衛がStageオブジェクトです。それぞれがイベントリスナーの登録を受けて、マウス操作に対応します。

  1. 前衛のボタン用インスタンスがコートに入り、ユーザーが繰出すサーブのInteractiveObject.mouseDownイベントをレシーブします。
  2. 前衛がレシーブしたのを確認したら後衛のStageもコートに加わり、ふたりでつぎに返されるInteractiveObject.mouseUpイベントを待受けます。
  3. InteractiveObject.mouseUpイベントが前衛の守備領域であるボタンのインスタンス上で起これば、ボタンのクリックとして前衛のインスタンスが返します。前衛が受けなかったInteractiveObject.mouseUpイベントは、ボタンのインスタンス外の操作として後衛のStageがレシーブします。
  4. 1回分の攻防は終わったので、リセットしてまた前衛がつぎのサーブを待ちます。

ポイントは上記の3です。ActionScript 3.0では、インスタンスとStageオブジェクトの親子ダブルスでInteractiveObject.mouseUpイベントが待受けられます。他方で2.0/1.0は、基本的にシングルスです。したがって、マウスを放す操作がインスタンス上で行われたかどうかは、条件判定で処理するしかありません。ダブルスの処理であれば、条件判断をすることなく、守備の分担としてイベントが扱えるのです。

以上の構成をMovieClipインスタンスのフレームアクションとして記述したのが、つぎのスクリプト2-4-006です。

▲スクリプト2-4-006■インスタンスとStageの連携でマウス操作を処理する

// MovieClip: マウスイベントを処理する
// フレームアクション
addEventListener(MouseEvent.MOUSE_DOWN, xPress);
function xPress(eventObject:MouseEvent):void { // サーブを返す
  trace("press");
  // ふたりでmouseUpを待つ
  addEventListener(MouseEvent.MOUSE_UP, xRelease);
  stage.addEventListener(MouseEvent.MOUSE_UP, xReleaseOutside);
}
function xRelease(eventObject:MouseEvent):void { // 前衛がレシーブ
  trace("release");
  // リセット
  removeEventListener(MouseEvent.MOUSE_UP, xRelease);
  stage.removeEventListener(MouseEvent.MOUSE_UP, xReleaseOutside);
}
function xReleaseOutside(eventObject:MouseEvent):void { // 後衛がレシーブ
  trace("releaseOutside");
  // リセット
  removeEventListener(MouseEvent.MOUSE_UP, xRelease);
  stage.removeEventListener(MouseEvent.MOUSE_UP, xReleaseOutside);
}

InteractiveObject.mouseUpイベントをMovieClipとStageの両インスタンスで待受けることにより、マウスボタンを放す操作がインスタンス上か外かを切り分ける。

前述のとおり、InteractiveObject.mouseUpイベントをMovieClipとStageの両インスタンスで待受けることにより、とくに条件判定を設けることなく、マウスボタンを放す操作がインスタンス上か外かの処理が切り分けられています。なお、マウスボタンを放す操作が行われたときは、InteractiveObject.mouseUpイベントに対するリスナーはともに削除する必要があります。

[MEMO]2-4-007 イベントリスナーの二重登録
EventDispatcher.addEventListener()メソッドでイベントリスナーを追加しようとしたとき、指定したイベントにそのリスナー関数がすでに登録されている場合には、関数が重複してリスナーに加えられることはありません。それでも、不要なときは削除しておくことが、不慮のトラブルを防ぎ、確実なスクリプティングにつながります。

上記スクリプト2-4-006では、インスタンスとStageともにイベントリスナーを削除しないと、動作に問題を生じます。

ActionScript 3.0のクラス定義の応用も兼ねて、イベントハンドラメソッドonPress、onRelease、onReleaseOutsideをクラスMyButtonとして実装してみます(スクリプト2-4-007)。MyButtonクラスをMovieClipシンボルの[クラス](もしくは[基本クラス])に設定すると(図2-4-010)、そのインスタンスmy_mcに対してつぎのようなイベントハンドラメソッドの設定が可能になります。

// タイムライン: メイン
// フレームアクション
// MyButtonクラスを設定したインスタンスmy_mcを配置
my_mc.onPress = function (eventObject:MouseEvent):void {
  trace("onPress", this, eventObject);
};
my_mc.onRelease = function (eventObject:MouseEvent):void {
  trace("onRelease", this, eventObject);
};
my_mc.onReleaseOutside = function (eventObject:MouseEvent):void {
  trace("onReleaseOutside", this, eventObject);
};

▲図2-4-010■MovieClipシンボルの[クラス]にMyButtonクラスを設定
Flash_OOP_2-4-013.gif
△複数のシンボルで使いたいときは、[基本クラス]に設定する。

▲スクリプト2-4-007■イベントハンドラメソッドonPress/onRelease/onReleaseOutsideを実装したクラス

// ActionScript 3.0クラス定義ファイル: MyButton.as
// MovieClipシンボルの[クラス]に設定
package {
  import flash.display.MovieClip;
  import flash.events.MouseEvent;
  public class MyButton extends MovieClip {
    public var onPress:Function;
    public var onRelease:Function;
    public var onReleaseOutside:Function;
    public function MyButton() {
      addEventListener(MouseEvent.MOUSE_DOWN, press);
    }
    private function press(eventObject:MouseEvent):void {
      callEvent(onPress, eventObject);
      setMouseUp();
    }
    private function release(eventObject:MouseEvent):void {
      clearMouseUp();
      callEvent(onRelease, eventObject);
    }
    private function releaseOutside(eventObject:MouseEvent):void {
      clearMouseUp();
      callEvent(onReleaseOutside, eventObject);
    }
    private function setMouseUp():void {
      addEventListener(MouseEvent.MOUSE_UP, release);
      stage.addEventListener(MouseEvent.MOUSE_UP, releaseOutside);
    }
    private function clearMouseUp():void {
      removeEventListener(MouseEvent.MOUSE_UP, release);
      stage.removeEventListener(MouseEvent.MOUSE_UP, releaseOutside);
    }
    private function callEvent(eventFunction:Function, eventObject:MouseEvent):void {
      if (eventFunction is Function) {
        eventFunction.apply(this, [eventObject]);
      }
    }
  }
}

△onPress/onRelease/onReleaseOutsideを、Function型のpublicプロパティとして宣言。マウスイベントに応じて、callEvent()メソッドによりコールバック関数を呼出す。

上記スクリプト2-4-007のポイントを、簡単に説明します。第1に、イベントハンドラメソッドとして設定されるonPress、onRelease、およびonReleaseOutsideは、Function型のpublicプロパティとして宣言します。この宣言により、インスタンスにコールバック関数を設定することが可能になります。

第2に、InteractiveObject.mouseUpイベントに対するリスナー関数の設定とその削除は、それぞれメソッドsetMouseUp()とclearMouseUp()として定義しました。また、onPress、onRelease、およびonReleaseOutsideに設定されたコールバック関数は、callEvent()メソッドに引数として渡し、このメソッド内から呼出します。

第3に、callEvent()メソッドからコールバック関数を呼出すとき、関数にFunction.apply()メソッドを適用しています。これはイベントハンドラメソッドに名前のない関数が設定されたとき、インスタンスへの参照を保持するための考慮です([MEMO]2-3-005「名前のない関数とスコープ」参照)。

[COLUMN]2-4-001 ローカルスコープで実行された名前のない関数
名前のない関数を、関数(メソッド)の引数やローカル変数に設定されたローカルスコープで呼出すと、インスタンスへのthis参照をもたずに実行されます。

上記スクリプト2-4-007のcallEvent()メソッドで、引数に渡された関数にFunction.apply()メソッドを適用せずに呼出した場合、イベントハンドラメソッドにつぎのように名前のない関数が設定されていると、ローカルスコープでの実行になります。

// タイムライン: メイン
// フレームアクション
// MyButtonクラスを設定したインスタンスmy_mcを配置
my_mc.onPress = function (eventObject:MouseEvent):void {
  trace("onPress", this, eventObject);
};

▲図2-4-011■クラスMyButtonのcallEvent()メソッドで引数の関数をそのまま呼出した場合
Flash_OOP_2-4-014.gif
△callEvent()メソッドの引数として渡された関数にFunction.apply()メソッドを適用せずに呼出すと、thisがインスタンスへの参照をもたなくなる。

this参照として[出力]された[object global]という表示は、具体的なインスタンスへの参照がないこと示します。グローバルオブジェクト([object global])は、「ActionScriptプログラムが起動すると作成され、すべてのグローバル変数および関数を含みます」([ヘルプ]→[ActionScript 3.0のプログラミング]→[ActionScript言語とシンタックス]→[関数]→[関数のスコープ]参照)。

[Prev/Next]


作成者: 野中文雄
作成日: 2008年2月7日


Copyright © 2001-2008 Fumio Nonaka.  All rights reserved.