![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
||||||||||||||||
![]() |
Flash OOP for ActionScript 3.0 第2章 ActionScript 2.0から3.0へのガイド
|
// MovieClip: ボタンとして使用 |
[ムービープレビュー]で確かめると、MovieClipインスタンスにマウスポインタがロールオーバーしたとき、ポインタが指差しカーソルに変わります(図2-4-002)。
▲図2-4-002■Sprite.buttonModeプロパティをtrueに設定
△インスタンスがボタンとして動作し、マウスポインタは指差しカーソルに変わる。
[MEMO]2-4-001 MovieClipインスタンスの[出力]結果 クラスが設定されていないMovieClipシンボルのインスタンスをtrace()関数の引数に指定した場合は、シンボル名と整数の組合わさった名前が[出力]されるようです(前掲図2-4-002参照)。シンボル名が日本語(2バイト文字)ですと、その部分は"Timeline"という名前に置替えられます。 |
[MEMO]2-4-002 MovieClipインスタンスのボタンとしての動作 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: ダブルクリックを受取るインスタンス |
すでに第2節(2-2-3-1「イベントオブジェクトを受取る」)で簡単に触れたとおり、リスナー関数が受取るイベントオブジェクトには、イベントに関わるさまざまな情報がプロパティとして含まれています。その中のEvent.typeプロパティは、発生したイベント名の文字列、つまりイベント定数(上記スクリプト2-4-002ではMouseEvent.CLICKあるいはMouseEvent.DOUBLE_CLICK)の値をもっています(図2-4-003)。したがって、複数のイベントにひとつのリスナー関数を登録した場合でも、Event.typeプロパティの値を調べれば、発生したイベントに応じた処理を記述することができます。
▲図2-4-003■クリックとダブルクリックのイベントに同じリスナー関数を登録
△Event.typeプロパティの値で、発生したイベントがわかる。
[MEMO]2-4-003[ヘルプ]の「InteractiveObject.doubleClickEnabled」の項の説明 |
●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を重ねる
△前面の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に設定
// タイムライン: メイン |
▲図2-4-005■InteractiveObject.mouseEnabledプロパティをfalseにするとマウスイベントを奪わない
△背面のMovieClipインスタンスにマウスイベントが渡されるので、ポインタも指差しカーソルに変わる。
[MEMO]2-4-004 マウスイベントを受取るクラス |
○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イベントハンドラメソッドを設定
△イベントの発生したインスタンスを問わないので、マウスポインタがインスタンス上になくてもイベントを受取る。
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: 任意のインスタンス |
[ムービープレビュー]で確かめると、ステージ全体でマウスポインタの動きに対してイベントが発生します。ただし、イベントの発生しているターゲットを示すイベントオブジェクトのEvent.targetプロパティは、MovieClipインスタンスの領域とその外では値が変化します。インスタンス上ではそのインスタンスの参照を、何もオブジェクトのない領域ではStageインスタンスを返します(図2-4-007)。
▲図2-4-007■StageにMovieClip.onMouseMoveイベントハンドラメソッドを設定
△イベントの発生したインスタンスであるEvent.targetプロパティの値は、マウスポインタがMovieClipインスタンス上かその外かによって変化する。
●2-4-3 イベントの発生と処理
ActionScript 3.0では、イベントの発生するターゲットのインスタンスとは別に、イベントリスナーが登録されてイベントを処理するインスタンスが存在します。マウスイベントでは、イベントの発生したターゲットインスタンス自身のほか、そのインスタンスを表示リストのツリーに含む上層、つまり親や祖先のDisplayObjectContainerインスタンスがイベントを扱えます。そこで、イベントの発生するインスタンスとその処理を行うインスタンスについて、ロールオーバーとロールアウトのマウスイベントを採上げて説明しましょう。
[MEMO]2-4-005 イベントの発生するインスタンスと処理するインスタンス なお、イベントによってはターゲットに対して発生するだけで、親に渡されないものもあります。たとえば、DisplayObject.enterFrameイベントがその例です。 |
○2-4-3-1 InteractiveObject.mouseOverとInteractiveObject.rollOverイベント
マウスポインタのロールオーバーを扱うイベントには、InteractiveObject.mouseOverとInteractiveObject.rollOverが定義されています。このふたつのイベントの違いは、イベントが発生するターゲットインスタンスの捉え方にあります。サンプルムービーとして、ステージに空のMovieClipインスタンスparent_mcを置き、その中にふたつのMovieClipインスタンスpen0_mcとpen1_mcを入れ子にします。そして、ふたつの子のインスタンスにはスクリプトは書かず、親のparent_mcにつぎのフレームアクションを記述します(スクリプト2-4-005)。
[MEMO]2-4-006 ロールアウトを扱うイベント |
// MovieClip: マウスイベントを処理するparent_mc |
スクリプト2-4-005は、親インスタンスparent_mcのふたつのイベント(定数MouseEvent.MOUSE_OVERとMouseEvent.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のイベント発生の違い
△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.mouseOverとInteractiveObject.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イベントの違い
△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オブジェクトです。それぞれがイベントリスナーの登録を受けて、マウス操作に対応します。
ポイントは上記の3です。ActionScript 3.0では、インスタンスとStageオブジェクトの親子ダブルスでInteractiveObject.mouseUpイベントが待受けられます。他方で2.0/1.0は、基本的にシングルスです。したがって、マウスを放す操作がインスタンス上で行われたかどうかは、条件判定で処理するしかありません。ダブルスの処理であれば、条件判断をすることなく、守備の分担としてイベントが扱えるのです。
以上の構成をMovieClipインスタンスのフレームアクションとして記述したのが、つぎのスクリプト2-4-006です。
▲スクリプト2-4-006■インスタンスとStageの連携でマウス操作を処理する
// MovieClip: マウスイベントを処理する |
前述のとおり、InteractiveObject.mouseUpイベントをMovieClipとStageの両インスタンスで待受けることにより、とくに条件判定を設けることなく、マウスボタンを放す操作がインスタンス上か外かの処理が切り分けられています。なお、マウスボタンを放す操作が行われたときは、InteractiveObject.mouseUpイベントに対するリスナーはともに削除する必要があります。
[MEMO]2-4-007 イベントリスナーの二重登録 上記スクリプト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クラスを設定
△複数のシンボルで使いたいときは、[基本クラス]に設定する。
// ActionScript 3.0クラス定義ファイル: MyButton.as |
上記スクリプト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 ローカルスコープで実行された名前のない関数 上記スクリプト2-4-007のcallEvent()メソッドで、引数に渡された関数にFunction.apply()メソッドを適用せずに呼出した場合、イベントハンドラメソッドにつぎのように名前のない関数が設定されていると、ローカルスコープでの実行になります。 // タイムライン: メイン ▲図2-4-011■クラスMyButtonのcallEvent()メソッドで引数の関数をそのまま呼出した場合 this参照として[出力]された[object global]という表示は、具体的なインスタンスへの参照がないこと示します。グローバルオブジェクト([object global])は、「ActionScriptプログラムが起動すると作成され、すべてのグローバル変数および関数を含みます」([ヘルプ]→[ActionScript 3.0のプログラミング]→[ActionScript言語とシンタックス]→[関数]→[関数のスコープ]参照)。 |
作成者: 野中文雄
作成日: 2008年2月7日