Chap04 イベント処理の基礎の最近のブログ記事

(section04-02 イベントとイベントリスナーから抜粋)

 入れ子のムービークリップのロールオーバー/ロールアウトのイベントは、クリックイベントと同じではありません。クリックイベントの場合は親ムービーにイベントリスナーを登録すれば子ムービーのインスタンスのクリックをイベントのtargetとして処理できますが、ロールオーバー/ロールアウトのイベントの場合はtargetとして処理できません。
 これに対処するために、次のサンプルで示すようにaddEventListener()の第3引数(useCaptureプロパティ)をtrueにします。すると親ムービーに設定したリスナー関数でロールオーバー/ロールアウトをした子ムービーのインスタンスをtargetとして処理できるようになり、子ムービーのインスタンスごとにイベントリスナーを登録する手間がなくなります。

[:script:]ロールオーバーした入れ子のインスタンスを拡大する
//イベントリスナーの追加
box_mc.addEventListener(MouseEvent.ROLL_OVER, onRollover, true);
box_mc.addEventListener(MouseEvent.ROLL_OUT, onRollout, true);

//ロールオーバーのリスナー関数
function onRollover(eventObj:MouseEvent):void {
	var target_mc:MovieClip=eventObj.target as MovieClip;
	target_mc.scaleX=target_mc.scaleY=3.0;
}

//ロールアウトのリスナー関数
function onRollout(eventObj:MouseEvent):void {
	var target_mc:MovieClip=eventObj.target as MovieClip;
	target_mc.scaleX=target_mc.scaleY=1.0;
}
fig4-02-09.jpg
イベントリスナーは親ムービーに設定してありますが、ロールオーバーした子ムービーのインスタンスが拡大します
swfを試す

[:note:] ROLL_OVER ROLL_OUT のイベントオブジェクトのbubblesプロパティがfalseだからです。CLICKのbubblesプロパティはtrueです。
詳しくはヘルプのイベントフローを参照してください。

(section04-02 イベントとイベントリスナーから抜粋)

 ムービークリップの中にさらにムービークリップが含まれているという入れ子のムービークリップのインスタンスの場合には、マウスイベントを受け取ったターゲットの処理に注意が必要です。
 たとえば、次のカエルの顔のムービークリップは顔の中の目が入れ子のインスタンスになっています。このムービークリップのインスタンスface_mcにクリックすると回転するイベントリスナーを追加します。

fig04-02-04.jpg
顔のムービークリップの中に目のムービークリップが入れ子で入っています。

[:script:]クリックしたtargetを回転させる
//イベントリスナーの追加
face_mc.addEventListener(MouseEvent.CLICK, onClick);

//クリックのリスナー関数
function onClick(eventObj:MouseEvent):void {
	var target_mc:MovieClip=eventObj.target as MovieClip;
	target_mc.rotation+=30;
}
 カエルの顔すなわちface_mcをクリックすると顔全体が回転します。つまり、targetはface_mcということになります。

fig04-02-05.jpg
顔をクリックすると顔全体が回転します。

 ところが、目をクリックすると目だけが回転します。目のインスタンスにはイベントリスナーを登録していないのに、クリックのtargetはface_mcではなく目のインスタンスになっています。

fig04-02-06.jpg
目をクリックすると目だけが回転します。
swfを試す

 目をクリックしても顔全体を回転させるには、targetプロパティではなくcurrentTargetプロパティを利用します。

[:script:]リスナーを追加したインスタンスを回転させる
//イベントリスナーの追加
face_mc.addEventListener(MouseEvent.CLICK, onClick);

//クリックのリスナー関数
function onClick(eventObj:MouseEvent):void {
	var target_mc:MovieClip=eventObj.currentTarget as MovieClip;
	target_mc.rotation+=30;
}
swfを試す

(section04-01 イベントとイベントリスナーから抜粋)

 イベントが発生するとaddEventListener()で登録しておいたリスナー関数が呼び出されますが、このときイベントオブジェクトが引数として送られてきます。イベントオブジェクトにはイベントの種類によってさまざまなプロパティがあり、発生したイベントの状況を示すデータが含まれています。このプロパティの値を活用することで、スクリプトのむだを省いたり、イベントに応じてできることをひろげることができます。

クリックされたターゲット
 次のサンプルのball1_mcからball4_mcの4個のインスタンスはクリックのたびに10度回転します。各インスタンスのaddEventListener()ではそれぞれのインスタンスを10度回転させるリスナー関数を追加していますが、リスナー関数のstepRotation1()からstepRotation4()の4個のリスナー関数定義をみると回転の対象となるインスタンスが違うだけで同じ機能の関数です。この方法ではターゲットと同じ個数のリスナー関数が必要になります。

[:script:]ターゲットと同じ個数のリスナー関数が必要
//4個のmcに同じ機能のイベントリスナーを追加する
ball1_mc.addEventListener(MouseEvent.CLICK, stepRotation1);
ball2_mc.addEventListener(MouseEvent.CLICK, stepRotation2);
ball3_mc.addEventListener(MouseEvent.CLICK, stepRotation3);
ball4_mc.addEventListener(MouseEvent.CLICK, stepRotation4);

//ball1_mcを回転させる
function stepRotation1(eventObj:MouseEvent):void {
	ball1_mc.rotation+=10;
}
//ball2_mcを回転させる
function stepRotation2(eventObj:MouseEvent):void {
	ball2_mc.rotation+=10;
}
//ball3_mcを回転させる
function stepRotation3(eventObj:MouseEvent):void {
	ball3_mc.rotation+=10;
}
//ball4_mcを回転させる
function stepRotation4(eventObj:MouseEvent):void {
	ball4_mc.rotation+=10;
}
fig04-01-06.jpg
インスタンスをクリックすると、そのインスタンスが10度回転します。。→swfを試す

 このスクリプトは、次のようにリスナー関数の引数のイベントオブジェクトのtargetプロパティを利用することで、どのインスタンスも同じstepRotation()をリスナー関数として使えるようになりスクリプトも短くなります。

[:script:]イベントオブジェクトのtargetプロパティを利用する
//4個のmcに同じイベントリスナーを追加する
ball1_mc.addEventListener(MouseEvent.CLICK, stepRotation);
ball2_mc.addEventListener(MouseEvent.CLICK, stepRotation);
ball3_mc.addEventListener(MouseEvent.CLICK, stepRotation);
ball4_mc.addEventListener(MouseEvent.CLICK, stepRotation);

//ターゲットを回転させる
function stepRotation(eventObj:MouseEvent):void {
	eventObj.target.rotation+=10;
}

 このスクリプトのポイントは9行目のeventObj.targetです。イベントオブジェクトのtargetプロパティには、イベントが発生したターゲットの参照、すなわちクリックされたインスタンスの参照が入っています。したがって、イベントによって呼ばれるリスナー関数が同じであっても、どのインスタンスがクリックされたのかがわかります。eventObj.targetをいったん変数に代入して使う場合には、次のようにas演算子を使って変数のデータ型にキャスト(型変換)しなければコンパイルエラーになります。

[:script:]targetの値を変数に代入して利用する
//4個のmcに同じイベントリスナーを追加する
ball1_mc.addEventListener(MouseEvent.CLICK, stepRotation);
ball2_mc.addEventListener(MouseEvent.CLICK, stepRotation);
ball3_mc.addEventListener(MouseEvent.CLICK, stepRotation);
ball4_mc.addEventListener(MouseEvent.CLICK, stepRotation);

//ターゲットを回転させる
function stepRotation(eventObj:MouseEvent):void {
	//targetの値をMovieClipクラスにキャストする
	var ball_mc:MovieClip=eventObj.target as MovieClip;
	ball_mc.rotation+=10;
}
(section04-01 イベントとイベントリスナーから抜粋)

 今度は同じイベントに同じリスナー関数を重ねて追加した場合を見てみましょう。次のサンプルではa_mcのフレーム再生イベントに対してstepRotationA()を4回重ねてリスナー関数として追加しています。この場合にはフレーム再生でstepRotationA()が4回実行されることになり、同じ機能のstepRotationB()を1回しか追加していない場合にb_mcに比べて4倍の速度で回転するでしょうか。
 その結果はa_mcもb_mcと同じくstepRotationA()を1回しか追加していない場合と同じ速度で回転します。すなわち、同じイベントに同じリスナー関数を重ねて追加しても、リスナー関数は重複して登録されないわけです。

[:script:]同じイベントに同じリスナー関数を重ねて追加した場合
//同じイベントに同じリスナー関数を重ねて追加する
a_mc.addEventListener(Event.ENTER_FRAME, stepRotationA);
a_mc.addEventListener(Event.ENTER_FRAME, stepRotationA);
a_mc.addEventListener(Event.ENTER_FRAME, stepRotationA);
a_mc.addEventListener(Event.ENTER_FRAME, stepRotationA);
//b_mcには1度しか追加しない
b_mc.addEventListener(Event.ENTER_FRAME, stepRotationB);

//回転させる
function stepRotationA(eventObj:Event):void {
	a_mc.rotation+=2;
}

function stepRotationB(eventObj:Event):void {
	b_mc.rotation+=2;
}
fig04-01-05.jpg
a_mcもb_mcと同じ速度で回転します。→swfを試す
(section04-01 イベントとイベントリスナーから抜粋)

 同じターゲットの同じイベントに対して複数のリスナーを追加するとどうなるでしょうか。次のスクリプトでは5行目でインスタンスf_mcのEvent.ENTER_FRAMEイベントにstepRotation()をリスナー関数として追加し、次の6行目でも同じくf_mcのEvent.ENTER_FRAMEイベントに今度はstepZoom()をリスナー関数として追加しています。この結果がどうなるかを予想すると、後から実行した6行目のaddEventListener()の設定が5行目のaddEventListener()の設定を上書きしていまい、Event.ENTER_FRAMEイベントではstepZoom()だけが実行されるようになるのではないかと思われます。
 しかし、実際にはEvent.ENTER_FRAMEイベントではstepRotation()とstepZoom()の両方のリスナー関数が実行され、両者の動作が組み合わさったアニメーションになります。すなわち、stepRotation()によって回転し、stepZoom()によって伸縮とアルファ変更を繰り返すアニメーションになります。

[:script:]同じイベントに別のリスナー関数を重ねて追加する
//stepZoomで比率を変化させるための角度
var degree:int;

//同じイベントに別のリスナー関数を重ねて追加する
f_mc.addEventListener(Event.ENTER_FRAME, stepRotation);
f_mc.addEventListener(Event.ENTER_FRAME, stepZoom);

//回転させる
function stepRotation(eventObj:Event):void {
	f_mc.rotation+=2;
}

//伸縮とアルファ変更
function stepZoom(eventObj:Event):void {
	degree=(degree+2)%360;
	//回転角度で比率を変える
	var ratio:Number=Math.abs(Math.sin(degree*Math.PI/180));
	//伸縮
	f_mc.scaleX=f_mc.scaleY=1+9*ratio;
	//アルファ変更
	f_mc.alpha=Math.max(0.02,1-ratio);
}
fig04-01-04b.jpg
回転と伸縮の複数のリスナー関数の動作が組み合わさったアニメーションになります。→swfを試す

 stepZoom()では17行目で比率ratioの値を作っています。sinの値は繰り返しでdegreeに2度ずつ加算され-1〜1のサイン波になり、Math.abs()によって絶対値0〜1で変化する値になります。
 19行目のscaleX、scaleYはインスタンスの横方向、縦方向のスケールを示すプロパティで、1のときが等倍です。この設定値を1+9*ratioで計算しているので、スケールは1倍〜10倍で変化します。21行目のalphaはインスタンスのアルファ値を示すプロパティで、0が透明、1が不透明の0〜1の範囲の値です。この設定値を1-ratioの計算で設定値を変化させ、完全な透明にならないようにMath.max()を使って最低値を0.02に制限しています。
(section04-01 イベントとイベントリスナーから抜粋)

 さらに発展させて、止まっているbox_mcをクリックすると回転が始まり、回転しているbox_mcをクリックすると回転が止まるようにしてみましょう。

 今度は止まっている状態から回転を開始する必要があるので、box_mcの回転を開始するstartRotation()を用意し、startRotation()をクリックイベントのリスナー関数として追加するところから始めます。
 startRotation()では2つのことを行います。1つはbox_mcが回転するようにフレーム再生イベントにstepRotation()を追加します。もう1つはbox_mcをクリックしたら回転を止める機能です。このとき、クリックイベントには回転を開始するstartRotation()がリスナー関数として追加されている状態なので、startRotation()を取り除くのを忘れないようにします。
 回転中にクリックしたときに実行されるstopRotation()では、startRotation()の場合とは逆に回転を止めることに加えて、次にクリックされたならば回転を開始するように設定し直します。このように、addEventListener()とremoveEventListener()を使ってイベントリスナーを追加したり取り除いたりすることで、イベントへの対応を切り替えるのがイベント処理の基本テクニックです。

[:script:]box_mcをクリックして回転と停止を切り替える
//クリックで回転を開始するようにする
box_mc.addEventListener(MouseEvent.CLICK, startRotation);

//回転を始めるリスナー関数
function startRotation(eventObj:MouseEvent):void {
	//フレーム再生で回転するようにする
	box_mc.addEventListener(Event.ENTER_FRAME, stepRotation);
	//クリックで回転を停止するようにする
	box_mc.removeEventListener(MouseEvent.CLICK, startRotation);
	box_mc.addEventListener(MouseEvent.CLICK, stopRotation);
}

//回転を止めるリスナー関数
function stopRotation(eventObj:MouseEvent):void {
	//回転を止める
	box_mc.removeEventListener(Event.ENTER_FRAME, stepRotation);
	//クリックで回転を開始するようにする
	box_mc.addEventListener(MouseEvent.CLICK, startRotation);
	box_mc.removeEventListener(MouseEvent.CLICK, stopRotation);
}

//フレーム再生で実行されるリスナー関数
function stepRotation(eventObj:Event):void {
	//box_mcを回転させる
	box_mc.rotation+=2;
}
fig4-1-3.jpgswfを試す
(section04-01 イベントとイベントリスナーから抜粋)

 イベントターゲットに追加したイベントリスナーを取り除くにはremoveEventListener()を使います。イベントターゲットに対して取り除きたいイベントタイプとリスナー関数を引数で指定します。

 書式:イベントリスナーを取り除く
イベントターゲット.removeEventListener(イベントタイプ, リスナー関数);

 たとえば、box_mcに追加してある「Event.ENTER_FRAMEイベントでstepRotation()を実行する」というイベントリスナーを取り除くスクリプトは次のようになります。これでbox_mcの回転が止まります。

[:script:]box_mcからイベントリスナーを取り除く
box_mc.removeEventListener(Event.ENTER_FRAME, stepRotation);

クリックで回転を止める
 ぐるぐる回転しているbox_mcをクリックすると回転が止まるようにするにはどうすればよいでしょうか。box_mcを回転させる方法は先のサンプルで説明したようにEvent.ENTER_FRAMEのイベントリスナーにリスナー関数としてstepRotation()を追加し、stepRotation()でbox_mcを少しずつ回転させます。この回転をクリックで止めるには次のスクリプトの3行目のようにクリックのMouseEvent.CLICKイベントにstopRotation()をリスナー関数として追加し、stopRotation()の中でEvent.ENTER_FRAMEのイベントリスナーを取り除きます。

[:script:]回転しているbox_mcをクリックして止める
//イベントリスナーを登録する
box_mc.addEventListener(Event.ENTER_FRAME, stepRotation);
box_mc.addEventListener(MouseEvent.CLICK, stopRotation);

//フレーム再生で実行されるリスナー関数
function stepRotation(eventObj:Event):void {
	//box_mcを回転させる
	box_mc.rotation +=2;
}

//クリックで回転を止めるリスナー関数
function stopRotation(eventObj:MouseEvent):void {
	//イベントリスナーを取り除く
	box_mc.removeEventListener(Event.ENTER_FRAME, stepRotation);
}
fig4-1-3.jpgswfを試す

(section04-01 イベントとイベントリスナーから抜粋)

 インスタンスがぐるぐる回転するといったアニメーションを作る場合にもイベント処理を利用します。インスタンスをぐるぐる回転させるには、インスタンスの回転角度を加算するリスナー関数を繰り返し発生するイベントで繰り返し呼び出せばよいわけです。
 フレームレートに合わせて繰り返し発生するフレーム再生イベントは、アニメーションで利用する繰り返しイベントとして最適なイベントです。次のスクリプトではbox_mcのフレーム再生イベントにstepRotation()をリスナー関数として追加しています。stepRotation()が実行されるたびにbox_mcが2度ずつ回転することから、box_mcはぐるぐる回り続けます。

[:script:]box_mcがぐるぐる回り続ける
box_mc.addEventListener(Event.ENTER_FRAME, stepRotation);

//フレーム再生で実行されるリスナー関数
function stepRotation(eventObj:Event):void {
	//box_mcを回転させる
	box_mc.rotation += 2;
}
 なお、Event.ENTER_FRAMEはフレームの移動に合わせて発生するイベントですが、フレームが1フレームしかない場合やstop()でフレーム移動を停止している場合にもフレームレートに合わせて継続的にイベントが発生します。
(section04-01 イベントとイベントリスナーから抜粋)

 「インスタンスbox_mcをクリックしたらtest()を実行する」といったインタラクティブな処理を考えたとき、この中には「1.どこで、2.何が起きたら、3.どうする」の3つの要素が含まれています。

 1.どこで 
 2.何が起きたら
 3.どうする 

 これをイベント処理の用語で言い換えると次のようになります。

 1.イベントターゲット ・・・・ イベントが発生するオブジェクト
 2.イベントタイプ ・・・・ 発生するイベントの種類
 3.リスナー関数 ・・・・ イベント発生で実行する関数

 さきほどの「インスタンスbox_mcをクリックしたらtest()を実行する」をこれに当てはめると次のようになります。

 1.イベントターゲット ・・・・ box_mc
 2.イベントタイプ ・・・・ クリック
 3.リスナー関数 ・・・・ test()

 このイベント処理をスクリプトではaddEventListener()というメソッドを使って設定します。書式は次のようになります。イベントが発生するオブジェクトに対して、イベントタイプとリスナー関数をペアにしてイベントリスナーとして追加します。

書式:イベントリスナーを追加する
イベントターゲット.addEventListener(イベントタイプ, リスナー関数);
[:note:] addEventListener()には引数がまだありますが、ここでは省略しています。

 実際のスクリプトは次のようになります。クリックというイベントタイプはMouseEvent.CLICKで指定します。リスナー関数はtestのように()を付けずに指定します。
 実行するtest()も定義しておきます。リスナー関数にはイベントタイプと同じデータ型のイベントオブジェクトが引数として送られてくるので、コンパイルエラーにならないようにtest(eventObj:MouseEvent)のようにイベントオブジェクトを受け取る引数が必要です。

[:script:]box_mcをクリックしたらtest()を実行する
box_mc.addEventListener(MouseEvent.CLICK, test);

//クリックで実行されるリスナー関数
function test(eventObj:MouseEvent):void {
	trace("test OK");
}
 そこで、test()でbox_mcの現在の回転角度つまりbox_mc.rotationに45度を加算すれば、box_mcをクリックするたびにbox_mcが45度回転するようになります。

[:script:]box_mcをクリックすると45度回転する
box_mc.addEventListener(MouseEvent.CLICK, test);

//クリックで実行されるリスナー関数
function test(eventObj:MouseEvent):void {
	//box_mcを45度回転させる
	box_mc.rotation += 45;
}
fig04-01-02.jpgswfを試す
まったく新しいAS3の世界!
694a.jpg
Adobe Flash CS4
詳細!ActionScript3.0入門ノート[完全改訂版](CD-ROM付)

楽しいActionScript。
新たなる1歩へと踏み出しましょう。
■内容は?→ 目次を見る
■評判は?→ 書評を読む
この本を書いたわけ

このアーカイブについて

このページには、過去に書かれたブログ記事のうちChap04 イベント処理の基礎カテゴリに属しているものが含まれています。

前のカテゴリはChap03 配列とベクターと結合配列です。

次のカテゴリはChap05 基本的なアニメーションテクニックです。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

あわせて読みたいブログパーツ