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);
}
}
}