ActionScript3.0から、「ボタンを押した」とか「XMLSocketを受信した」の様なイベントを登録・実行するしくみとしてEventDispatcherというのが使われるようになった。addEventListener(MouseEvent.CLICK, onClick); しておくと、文字通りイベントがDispatch(発行)されるアレの事。

で、今C#用のXMLSocketServerクラスの上で動くFlashとC#を接続するコマンドメッセージ系を実装してて、それのFlash側のラッパーのコマンド受信イベント等をEventDispatcherで実装してaddEventListenerできる様にしたかったので、調べてサンプルを作ってみた。

■EventDispatcherサンプルのソースコード

EventDispatcherTest (AS3 / FlashCS3)

■参考サイト

上から順に読んだらわかりやすいと思う。

akihiro kamijo: EventDispatcher クラス

akihiro kamijo: カスタムイベントのディスパッチ

feb19.jp blog – AS3でEventDispatcherを使う

■EventDispatcherサンプルの動作

今回はtrace()に出力するだけ。毎フレーム、 framecount:フレーム番号 をtrace出力する。

FrameEvent.ON_COUNTFRAME イベントに関数を登録しておくと、10フレーム毎に登録しておいた関数が呼び出される。

イベントを受け取った関数は”receive dispatched Event!: フレーム番号” とtrace出力する。

サンプルを動かすとこうなる

framecount:1

framecount:2

framecount:3

framecount:4

framecount:5

framecount:6

framecount:7

framecount:8

framecount:9

framecount:10

receive dispatched Event!:10

framecount:11

framecount:12

framecount:13

framecount:14

framecount:15

framecount:16

framecount:17

framecount:18

framecount:19

framecount:20

receive dispatched Event!:20

framecount:21

framecount:22

(ずっと続く)



■サンプルの中身

サンプルにはflaとswfの他に、3つの.asファイルがある。

1つ目のEventDispatcherTest.asは最近flaとaction scriptを分けて書くようになったので、まあ2つ合わせてswfを作るメインになる物だ。

残り2つのFrameEventDispatcher.asとFrameEvent.asは、それぞれSpriteとEventを継承している。さらに、SpriteはEventDispatcherを継承している。(SpriteはボタンとかMovieClipとか、画面に表示される物の元になっているクラス)

つまりここにはEventDispatcherとEventそれぞれを継承したサブクラスが1つずつあるという事。

■EventDispatcherのしくみ

イベントを受け取りたいオブジェクトと、EventDispatcherとEventそれぞれを継承した2つのクラス、つまり合計3つのクラスが登場し、イベント登録・発行・受信を行う。

まず、

addEventListener(“イベント名”, 受け取る関数)

でイベントを登録する。

サンプルではEventDispatcherTest.asから登録されている

varfed=newFrameEventDispatcher();

fed.addEventListener(FrameEvent.ON_COUNTFRAME,onCountFrame);

この時に渡しているイベント名は、定数として

publicstaticconstON_COUNTFRAME:String=”on_countframe”;

Eventクラス内であらかじめ宣言してある物。

上で登録したonCountFrame関数もEventDispatcherTest.as内に実装されている。

publicfunctiononCountFrame(e:FrameEvent){

trace(“receivedispatchedEvent!:”+e.count);

}

受け取ったイベントオブジェクトの中から、フレーム番号を取り出して表示する。

次にイベントの発行(dispatch)は、EventDispatcherを継承したクラスから、任意のタイミングで

this.dispatchEvent(new Event(“イベント名”));

する。

サンプル内のFrameEventDispatcherクラスではこの様に、Eventを継承したFrameEventクラスのコンストラクタにON_COUNTFRAMEというイベント名を渡して、さらにフレーム番号も詰めて送っている。

varfe:FrameEvent=newFrameEvent(FrameEvent.ON_COUNTFRAME);

fe.count=frameCount;

dispatchEvent(fe);//イベント発行

FrameEventクラスのコンストラクタでは上位クラス(Eventクラス)のコンストラクタにsuper()でそのまま引数を丸投げしている。どうやらこれが、Eventクラスのコンストラクタ内でtypeプロパティに保存されるらしい。

ようするに、登録も発行も、String型の変数が一致していたら、登録してあった関数を呼び出すだけらしい。実際、akihiro kamijo: カスタムイベントのディスパッチではStringでマッチする様に実装されている。

ちょっとこの中身の動作の理解は自信ない。

■ソース嫁

archive (AS3 / Flash CS3)

FrameEvent.as

このクラスは、1.コンストラクタでsuperに引数を丸投げする事 2.clone()を実装しておく事 3.イベント名判別様の文字列定数を宣言しておくこと

さえやれば後は何をしても良いらしい。今回はフレーム番号をイベント通知するためのint型変数を追加しておいた。

package{

importflash.events.Event;

publicclassFrameEventextendsEvent{

publicvarcount:int;//フレーム番号

publicstaticconstON_COUNTFRAME:String=”on_countframe”;//イベント名判別用の文字列定数

publicfunctionFrameEvent(type:String,bubbles:Boolean=false,cancelable:Boolean=false){

count=0;

super(type,bubbles,cancelable);//コンストラクタ丸投げ

}

publicoverridefunctionclone():Event{

returnnewFrameEvent(type,bubbles,cancelable);

}

}

}

FrameEventDispatcher.as

EventDispatcherを継承しているSpriteをさらに継承している。

任意のタイミングでdispatchEvent()すればいいだけ。addEventListener等はもちろん元のEventDispatcherが持っている。

package{

importflash.events.EventDispatcher;

importflash.events.Event;

importflash.display.Sprite;

publicclassFrameEventDispatcherextendsSprite{

publicvarframeCount:int;

publicfunctionFrameEventDispatcher(){

frameCount=0;

this.addEventListener(Event.ENTER_FRAME,onEnterFrame);//毎フレーム実行

}

publicfunctiononEnterFrame(e:Event){

frameCount++;

trace(“framecount:”+frameCount);//現在のフレーム数

if(frameCount%10==0){//10フレーム毎に実行

varfe:FrameEvent=newFrameEvent(FrameEvent.ON_COUNTFRAME);

fe.count=frameCount;

dispatchEvent(fe);//イベント発行

}

}

}

}

なお、AS3は多重継承できないので、もしEventDispatcherをextendsできなかったらIEventDispatcherをimplementsすれば良いらしい→akihiro kamijo: カスタムイベントのディスパッチ

EventDispatcherTest.as

最後に上記2つを呼び出すメインクラスだが、まあ普通。普通にFrameEventDispatcher型のオブジェクトを作って、addEventListener(“イベント名”, イベントを受け取る関数)すればいい。

Event.ENTER_FRAMEがaddChildしないと受け取れないイベントだという事に気づくまで30分ぐらいかかった。

package{

importflash.events.*;

importflash.display.*;

publicclassEventDispatcherTestextendsSprite{

publicvarfed:FrameEventDispatcher;

publicfunctionEventDispatcherTest(){

fed=newFrameEventDispatcher();

this.addChild(fed);

fed.addEventListener(FrameEvent.ON_COUNTFRAME,onCountFrame);//イベント登録

}

//10フレーム毎に発行されるイベントを受け取る

publicfunctiononCountFrame(e:FrameEvent){

trace(“receivedispatchedEvent!:”+fed.frameCount);

}

}

}