2010.03.10
弊社(というか、ワタクシ)は日ごろの業務のほとんどがProgression 4 を使っての作業になっているのですが、
その中で最近のキーワードが、DoExecutor コマンドでチョー幸せコーディングです。
四の五の言わず、早速解説していきます。
Progression 4 の、WebConfig、および、BasicAppConfigを指定した場合として読んでください。
なお、この記事を読んだ後でも、そんなに幸せになれなかったとしても、当社では一切の責任を負いかねますのであしからず。
そもそも、ExecutorObjectってなによ?
Progression 3 でも、内部的には存在していた(と記憶してますが、曖昧です・・・)Executor系のインスタンスですが、バージョンが4になってから、表舞台に出てきました(アクセス制御子がpublicになった)。
で、ExecutorObjectが何者かというと、Cast系、Scene系のインスタンス内にプロパティとして存在していて、シーン遷移に同期した処理を上手いことやりくりしてくれているお方です。
シーン遷移と同期をとるために、Progressionではいろんなところで「addCommand()」というメソッドを使います。そのaddCommandメソッドが実行されると、実際のところ、ExecutorObjectインスタンス内にプロパティとして存在するSerialListコマンドに対してaddCommandが実行されています。
[Progression 4 CommandExecutorソースの該当部分]
public function addCommand( … commands:Array ):void {
//…
// 登録する
_current.addCommand.apply( null, commands );
}
_currentというのが、SerialListインスタンスです。
というこで、処理自体はSerialListが、諸々の管理をCommandExecutorインスタンスがやっている事になります。
さらに、ExecutorObjectインスタンスには target:IEventDispatcher というプロパティがあり、ここには、自身が管理しなければならない対象が保存されています。
targetが参照しているのは、ExecutorObjectインスタンス自身が保持されている、Cast系、Scene系のインスタンスです。
つまり、Cast系インスタンスと、ExecutorObjectインスタンスには、相互に参照しあっている状態です。
お互いがつながっている状態で、それぞれ処理を委譲しあって、上手いこと処理を同期させている、くらいの感じで理解しておけば大丈夫だと思います。
で、Do Executorコマンドでどう幸せになるのか?
シーン遷移と同期したアニメーションを実装していくと、色々なパターンに遭遇します。
- 子シーンに行く時は、メニュータブが隠れる。
- 子シーンから、自身シーンに戻ってきた時は、メニュータブがニョキッと現れる。
- 自身シーンの親シーンに行く時は、表示ツリーから削除するためのアニメーション後、実際に削除。
こういった場合に、Do Executorコマンドが真価を発揮します。
実際の使用方法を見る前に、ちょっとDoExecutorコマンドのコンストラクタを見てみましょう。
public function DoExecutor(executor:ExecutorObject,event:Event,initObject:Object = null ) {
// 引数を設定する
_executor = executor;
_event = event;
// スーパークラスを初期化する
super( _executeFunction, _interruptFunction, initObject );
}
第1引数にExecutorObjectインスタンス、第2引数にEventインスタンスがあります。
これは、Do Executorコマンドがexecuteされると、ExecutorObjectインスタンスのexecute()メソッドが実行されるのですが、その際に、Eventインスタンスが必要だからです。
[DoExecutorコマンド内 executeメソッド内]
// イベントを送出する
_target.dispatchEvent( _event );
// 状態を変更する
_dispatching = false;
// 処理を実行する
if ( _executeFunction != null ) {
_executeFunction();
}
else {
_executeComplete();
}
ここまでソースを追えばなんとなく解りますかね。
ExecutorObjectが管理しなきゃいけないターゲット(Cast系、Scene系)に対してイベントを発行して、ターゲット内でリスナが実行されたのち、ExecutorObjectの実行関数が実行される、という流れです。
ExecutorObjectの実行関数は、サブクラスで定義されます。
今回の場合のサブクラスはCommandExecutorインスタンスなので、その実行関数を見てみると、
private function _executeFunction():void {
if ( _current ) {
//…
// コマンドを実行する
_current.execute( super.extra );
}
となっています。 _currentは、SerialListです。
つまり、流れを追うと、
- DoExecutor実行
- targetにイベントが発行
- target内のリスナが実行される。
- リスナ内でaddCommandをすると、実質、executor.addCommandが実行される。(実質、_current.addCommand)
- ExecutorObjectインスタンスのexecuteが実行される(実質、_current.execute)
となっている、と。
で、実装してみる
ここまで仕組みがわかれば、後は簡単です。
Castインスタンスで、特定のイベントに対してリスナを登録して、あとは、そのイベントをつかってDoExecutorを実行すればいいだけです。
まず、Cast系インスタンスにリスナを登録します。イベント名は適宜わかりやすい感じで。
僕は、atCastAddedのタイミングで登録してます。
//CastHogeインスタンス(CastSprite継承) override protected function atCastAdded():void { addEventListener("show", _onShow); addEventListener("hide", _onHide); addEventListener("defaultIn", _onDefaultIn); addEventListener("defaultOut", _onDefaultOut); } //リスナ実装。内部では、atCastAdded同様、addCommandが使えます! private function _onShow(e:Event):void{ addCommand( new DoTweener(this, {alpha:1, time:0.5}) ); }
で、実行する時は、シーンオブジェクトにて、
override protected function atSceneLoad():void{ new AddChild(container, _castHoge).execute(); //自身のシーンへ到着する場合は、defaultIn if(manager.destinedSceneId.equals(sceneId)) { addCommand( new DoExecutor(_castHoge, new Event("defaultIn")) ); ) //子シーンへ行く時は、隠れるアニメーション if(sceneId.contains(manager.destinedSceneId)){ addCommand( new DoExecutor(_castHoge, new Event("hide")) ); } }
的にやれば、動・・・・・きません!!
こ、ここまで読ませておいてなんなんだ!
となった方には、動くように僕が改変したDoExecutor2コマンドクラスを配布致します。
DoExecutor2.as
動かない理由は、Cast系のexecutorプロパティは、表示ツリーに追加されるまではnullだからです。
つまり、コマンド登録時にはnullなので、動かない、と。
なので、そこを改変して、実行時にexecutorの参照を取りに行くように変えてあります。
(といっても、既存のソースをコピーしてるだけなんですけどね・・・)
さて、長々と書きましたが、これでどなたかのProgressionライフが幸せになる事を願いつつ、
僕は再び仕事へ戻ります・・・。
負けない!
https://dis.ne.jp/blog/flash/890.html/trackback