最近、少しFlashの世界に手を染めていますが、何が難しいのか徐々に分かってきました。
- 体系的に整理されておらず、ドキュメントも良くできているとは言い難く、外部の情報も経験的なノウハウのレベルが多い
- ActionScript 3.0(AS3)になって、オブジェクトの仕様などが体系的に整理されてきたが、ActionScript 2.0(AS2)のオブジェクト体系と互換性はない
- 得られる情報の大多数はActionScript 2.0用で、ActionScript 3.0では動きもしない
- ActionScript 3.0用の情報は、検索時に"AS3"を付けると割とよく見つかるが、絶対数は少ない
どうも、AS2からAS3への端境期に手を出したことに、最大の問題がありそうです。
まあそれはともかく、普通のWindows等の感覚で行くとRENDERイベントとinvalidateの挙動がかなり特殊であることが分かりました。以下に、簡単にまとめておきます。
サンプルソース §
Adobe Flash CS3とActionScript 3.0が前提です。
1フレームと2フレーム目にキーフレームを設定します。
1フレームに適当なシンボルを置き、プロパティでstart1と名付けます。
2フレームに適当なシンボルを2つ置き、プロパティでsymbol1a、symbol2aと名付けます。
そして、以下のようなコードを1フレーム目に入力します。
stop();
function rendereFunc(e)
{
if( currentFrame == 2 )
{
symbol1a.visible = true;
symbol2a.visible = false;
}
}
stage.addEventListener(Event.RENDER,rendereFunc);
start1.addEventListener(MouseEvent.CLICK,function(e)
{
gotoAndStop(2);
stage.invalidate();
});
このコードは、RENDERイベントを使っていますが、ENTER_FRAMEイベントを使うと以下の問題が出ます。
- 最初に2フレーム目でENTER_FRAMEイベントが発生したとき、まだsymbol1a、symbol2aは作成されていないので操作できない
- 2回目に2フレーム目でENTER_FRAMEイベントが発生したとき、symbol1a、symbol2aは作成されて表示済み。ここでvisibleをfalseにしても、一瞬だけ見えてしまう
このような問題を回避するためにRENDERイベントが使えるよ、という情報があったのでそれを使って記述してあります。
ちょっと違う挙動のツボ §
というわけで、このコードにはいくつかのツボがあります。特に、Windows関係等のプログラミングで、Paintイベント(WM_PAINT)やInvalidateメソッド(InvalidateRect)での無効領域指定(再描画要求)に慣れた技術者は要注意のツボです。
以下にそれを要約します。
- RENDERはstageのイベント (ENTER_FRAMEとは発生主体が違う)
- stageのinvalidateメソッドを呼び出した後、シンボル等の作成が終了した後、かつ、それらが描画される前の段階でRENDERイベントが発生する
- invalidateメソッドを呼ばない限り、RENDERイベントは発生しない
- 単純な再描画要求 (手前のウィンドウが消えて見えるようになった等) ではRENDERイベントは発生しない
つまりですね。Windowsプログラミングであればウィンドウ上に何かコントロールのたぐいを追加したとき、それは最初から無効領域なので、特にInvalidateを行わなくとも描画されます。Invalidateは表示内容に変化が生じるまでは呼び出す必要がありません。このような感覚で見ると、RENDERイベントのハンドラだけ用意しておけば、少なくともそれで表示初回にはイベントが発生してくれるような気がします。しかし、AS3では発生しません。発生させるには、invalidateメソッドの呼び出しが必要です。
感想 §
ActionScriptはミステリアスな泥沼君ですが、泥を落とせば光るのも事実。ノウハウを蓄積すれば何とかなりそう。
追記 §
これで上手く行きと思いきや。
実は、コンポーネントのUser Interfaceに属するボタンやスライダーを貼り付けておくと上手く動作しないことが発覚。
- invalidateしてもRENDERイベントが発生しない
- コンポーネント上にマウスカーソルを持っていくとRENDERイベントが発生する。しかも、何回も発生する
うーん、悩ましい。
更に追記 §
どうやら、大きな思い違いをしていたようです。
各フレーム上に配置したシンボル等の初期設定を行うのは、addEventListenerで追加する特定のイベントハンドラではなく、各フレームに対するコードで行うのが基本であるようです。これで、上手く初期化できます。
逆に、invalidateとRENDERイベントの方はPlayerのバージョンによってはバグもあるようです。上記のような現象も含めると、おそらく使うべきではないでしょう。