プログラマのためのFlash遊び方

第4回Flashのイベント処理を理解する

前回までは画面に描画する方法をご紹介しました。単に表示するだけではつまらないので、今回はマウスに反応するFlashを作りながら、Flashのイベント処理を理解していきましょう。

Flashでイベントを扱う方法は、HTMLのDOMイベントとほとんど同じです。DOMイベントをご存知の方にとっては理解しやすい内容かもしれません。

クリックイベントを拾う

さっそくシンプルな例から進めていきましょう。クリックした場所にランダムな色で円を表示するFlashです。

package {
    import flash.display.Sprite;
    import flash.events.MouseEvent;

    public class ClickTest extends Sprite {
        public function ClickTest():void {
            // クリックイベントを監視する
            stage.addEventListener("click", clickHandler);
        }

        private function clickHandler(event:MouseEvent):void {
            // 円を作成
            var s:Sprite = new Sprite();
            s.graphics.beginFill(Math.random() * 0x1000000);
            s.graphics.drawCircle(0, 0, 10);
            s.graphics.endFill();
            addChild(s);

            // 円をクリックされた位置に移動
            s.x = event.stageX;
            s.y = event.stageY;
        }
    }
}

ClickTest.asとして保存して、コンパイルします。

mxmlc ClickTest.as

次のようなFlashができあがります。最初は何も表示されていませんが、クリックした位置に円が表示されます。

背後で飛んでいるイベント

マウスやキーボードからの入力があると、Flash Playerは入力を行った対象の表示オブジェクトに対してイベントを送信します。

例えば、マウスが移動した場合には、マウスのすぐ下の表示オブジェクトに対して、⁠座標(5,10)にマウスが動いた」といったイベントが送信されます。他にも、キーボードが押されたり、マウスホイールが回されたりするたびに、イベントが送られています。

このように、ユーザーから何らかのアクションがあるたびに、背後では無数のイベントが送信されているのです。これらのイベントを検知するには、表示オブジェクトのaddEventListener()メソッドを利用します。

addEventListenerでイベントハンドラを登録

addEventListener()メソッドは次のようにして使います。

画像

この例では、次の意味になります。

  • stageで
  • clickイベントが発生したら
  • clickHandler関数を呼んでね

stageというのは、Flash全体を表す表示オブジェクトです。Flash全体で発生するイベントを監視したい場合には、stageを監視対象とするのが簡単でよいでしょう。

Stageオブジェクトで監視できるイベントの種類については、Flex3リファレンスガイドStageクラスのイベントの項目をご覧ください。ここでは、⁠イベントの種類」としてclickを指定しています。clickイベントは、マウスがクリックされたときに送信されるイベントです。

以上より、ステージ上でマウスがクリックされると、clickHandler()が呼ばれることがわかります。

イベントオブジェクトとイベントハンドラ

では、clickHandler()メソッドを見ていきましょう。

画像

このように、イベントハンドラは1つの引数を受け取り、戻り値がない関数として定義します。関数の中に、イベントが発生したときの処理を書きます。

イベントオブジェクト

イベントハンドラの引数にはイベントオブジェクトが渡されます。

イベントオブジェクトには、イベントの種類やイベントの状態など、イベントに関する基本的な情報が格納されています。イベントの種類によっては、基本的な情報に加え、いくつかの情報が追加で入っている場合があります。

それでは、click イベントが発生した際、イベントオブジェクトにはどのような情報が入っているのでしょうか。Flex3リファレンスガイドのStageクラスをご覧ください。イベントの一覧の中からclickを開いてみましょう。

画像

「イベントオブジェクトの型」flash.events.MouseEventとなっています。このことから、clickイベントが発生したときには、MouseEventオブジェクトが渡されることが分かります。

プロパティの一覧がリファレンスに表形式で詳しく書いてありますね。これを見ると、マウス座標やCtrlキーの状態などが格納されていることが分かります。このように、日本語で充実したドキュメントが手に入るのはActionScript 3.0の大きな利点でしょう。

イベントハンドラの処理

それでは、イベントハンドラのソースコードを追っていきましょう。

private function clickHandler(event:MouseEvent):void {
    // 円を作成
    var s:Sprite = new Sprite();
    s.graphics.beginFill(Math.random() * 0x1000000); (1)
    s.graphics.drawCircle(0, 0, 10);
    s.graphics.endFill();
    addChild(s);

    // 円をクリックされた位置に移動
    s.x = event.stageX;                            --┐(2)
    s.y = event.stageY;                            --┘
}

前半は前回までの内容が理解できていれば大丈夫でしょう。Spriteを作成し、原点を中心とした半径10の円を描画しています。

(1)の箇所では色をランダムに選んでいます。Math.random()は0から1までの値をランダム値で返す関数です。0x1000000とMath.random()を掛け合わせることで、0x000000(黒)から0xffffff(白)までの間のランダムな色となります。

次の(2)では、イベントオブジェクトに格納されているマウスの座標を利用して、円の位置をマウスの座標に設定しています。

clickHandler()では、インスタンスメソッドであるaddChild()メソッドを呼んでいますが、JavaScriptに詳しい方には意外かもしれません。というのも、JavaScriptのイベントハンドラではthisがグローバルオブジェクトを指すからです。それに対して、ActionScript 3.0では、インスタンスメソッドをイベントハンドラにした場合、thisの参照先はインスタンスになります。そのため、イベントハンドラの中から、インスタンス変数やインスタンスメソッドを利用できます。このように、インスタンスに関連付けられたメソッドのことを「バインドメソッド」と呼びます。

無名関数をイベントハンドラにする

今のままだと、円は増えていく一方です。円をクリックすると消えるようにしてみましょう。

clickHandler()メソッドの最後に、次のような処理を追加します。

s.addEventListener("click", function(event:MouseEvent):void {
    removeChild(s);
});

先ほどは、メソッドをイベントハンドラとして登録しましたが、今度は無名関数を登録しています。

無名関数の中からsを参照している点に注目してください。このように、無名関数からは外側のローカル変数を参照することができます。この動作にはクロージャが関わっているのですが、クロージャについて詳しく説明すると、それだけで1回分の分量になってしまうので、ここでは詳細は割愛します。

さて、これでうまく動きそうですね。ところが、実際に動かしてみると、1つ気になる現象に出くわします。

確かにクリックした円は消えるのですが、新たに円が登場します。これはどういうことでしょうか?

実は、円をクリックすると、円のイベントハンドラが呼ばれるだけでなく、ステージ上のイベントハンドラ(clickHandler()メソッド)も呼ばれているのです。円がクリックされたということは、ステージがクリックされたことにもなるのですから、考えてみれば当然の動作です。

伝播するイベント

では、円がクリックされたときに、内部ではどのような動作をしているのでしょうか。イベントオブジェクトの流れを見てみましょう。

画像

円がクリックされると、まずは円オブジェクトにイベントオブジェクトがやってきます(図の一番下⁠⁠。この時点で円に対して登録されたイベントハンドラが呼ばれます。

次に、イベントは親の表示オブジェクトに伝播していきます。円の親であるClickTestオブジェクト、その親であるStageオブジェクトの順に伝わっていきます。Stageオブジェクトにイベントが到達すると、イベントハンドラのclickHandler()メソッドが呼ばれます。このときに新たに円が生成されるわけです。

このようにイベントは複数の表示オブジェクトを順番に伝わっていきます。イベントがターゲットである円に到達したときをターゲット段階といいます。ターゲットから親に向かって伝播していく段階のことを、泡のように広がりながらさかのぼっていくことからバブリング段階といいます。

伝播を止めるには

イベント伝播の仕組みが分かったところで、最初の問題に戻ります。円をクリックしたときには円が消えるようにしましょう。

1つの方法として、イベントの伝播を抑制することができます。伝播を止めるには、イベントオブジェクトのstopPropagation()メソッドを呼びます。

s.addEventListener("click", function(event:MouseEvent):void {
    removeChild(s);

    // 親に伝播しないようにする
    event.stopPropagation();
});

この結果、円オブジェクトより先にイベントが伝播しなくなります。つまり、円をクリックしても、ステージのイベントハンドラが呼ばれず、新たに円が生成されることはなくなります。

イベントオブジェクトのプロパティを利用する

しかし、伝播を止めてしまうと不都合が出る場合もあります。例えば、ClickTestオブジェクトでもイベントを検知したいかもしれません。そのような場合にはどうすればいいでしょうか。

答えは簡単です。clickHandler()メソッドで、イベントのターゲットが円だった場合には何も行わないようにすればよいのです。イベントのターゲット(クリックされた表示オブジェクト)は、イベントオブジェクトのtargetプロパティで参照できます。

private function clickHandler(event:MouseEvent):void {
    // ステージ以外がクリックされたときには何もしない
    if(event.target != stage) {
        return;
    }

他の方法も考えられます。イベントオブジェクトには、イベントがどの段階なのかを表すeventPhaseプロパティが定義されています。

private function clickHandler(event:MouseEvent):void {
    // ターゲット段階以外では何もせず終了する
    if(event.eventPhase != EventPhase.AT_TARGET) {
        return;
    }

円をクリックした場合には、バブリング段階でclickHandler()メソッドが呼ばれるので、何もせずにretrunで終了することにします。EventPhaseクラスを利用しているので、flash.events.EventPhaseをインポートするのを忘れないようにしましょう。

キャプチャ段階について

ここでは触れませんでしたが、ターゲット段階、バブリング段階のほかに、キャプチャ段階というものもあります。キャプチャ段階はあまり利用しないので、今回は分かりやすく説明するために省略しました。

キャプチャ段階はターゲット段階の手前の処理です。Stageオブジェクトから始まり、ターゲットに到達するまで、表示オブジェクトの階層を降りていきます。この段階のことをキャプチャ段階というのです。詳しくは「ActionScript 3.0のプログラミング」イベントフローの項目をご覧ください。

まとめ

今回は、イベントの概念やイベントを監視する方法をご紹介したあとに、イベントの伝播について詳しく説明ました。最後のイベント伝播は、少し難しかったかもしれませんね。ひとまずは、イベントが親の方向に伝播していくことを覚えていただければ幸いです。

次回は、イベントを活用しつつ、インタラクティブなアニメーションを作ってみたいと思います。

おすすめ記事

記事・ニュース一覧