AS2でリンケージ識別子が衝突する時のまとめ
2009 年 10月 30 日 金曜日 kosuke先日、仕事でハマったところのまとめ。
僕の場合、普段は親SWFから子SWFを読み込んだら不用になったタイミングでアンロードします。その方が無駄にメモリー食わないとでしょうし。
しかし、コンテンツ毎に読み込むような、子SWFがいくつもあったりする場合、毎回ローディングするのが鬱陶しい…とか、サーバー負荷が…とかの理由でアンロードしない仕様で作る場合もあったりする。
この時、複数のSWFに同名のリンケージ識別子があると衝突することがあります。
AS2の場合、この回避には識別子をユニークにするしかない(であってる?)と思います。その挙動を記録しておきます。
上記の検証ファイルは、親SWFのindex.swfから、
子SWFのcontent1.swfとcontent2.swfを読み込んで試したもの。
子SWFとなる二つのファイルには、同じリンケージ識別子Mainで、それぞれのクラスを定義した、色違い・マウスアクション違いの矩形をアタッチしています。
content1.swfとcontent2.swf
public var main:MovieClip; public function onLoad():Void{ //Mainをアタッチ this.main = attachMovie( "Main", "main", this.getNextHighestDepth() ); } |
content1.swfのMain。
public var field:TextField; //マウスプレスで「1」を表示 public function onPress():Void{ this.field.text = "1"; this.field._alpha = 100; Tweener.addTween( this.field, { _alpha:0, transition:"easeNone", time:1 } ); } |
content2.swfのMain。
public var field:TextField; //マウスプレスで「2」を表示 public function onPress():Void{ this.field.text = "2"; this.field._alpha = 100; Tweener.addTween( this.field, { _alpha:0, transition:"easeNone", time:1 } ); } |
親SWFの各ボタンは、load、unload、子がアタッチしたMainインスタンスをremoveするボタンを設置。
//content1.swfをロード public function loadContent1():Void{ if( this.content1.main.getBytesLoaded() ) return; //ロード前ならロード if( !this.content1.getBytesLoaded() ){ this.loader.loadClip( "content1.swf", this.content1 ); } //ロード済みの場合onLoad実行 else{ trace("loaded"); this.content1.onLoad(); } } //content2.swfをロード public function loadContent2():Void{ if( this.content2.main.getBytesLoaded() ) return; //ロード前ならロード if( !this.content2.getBytesLoaded() ){ this.loader.loadClip( "content2.swf", this.content2 ); } //ロード済みの場合 else{ trace("loaded"); this.content2.onLoad(); } } //content1.swfアンロード public function unloadContent1(){ this.content1.unloadMovie(); } //content2.swfアンロード public function unloadContent2(){ this.content2.unloadMovie(); } //content1.swfがアタッチしたmainを削除 public function removeContent1(){ this.content1.main.removeMovieClip(); } //content2.swfがアタッチしたmainを削除 public function removeContent2(){ this.content2.main.removeMovieClip(); } |
これを使って、
Content1LoadクリックしてContent2Loadクリックすると、子SWFのMainが表示されます。表示された矩形をクリックすると左側は「1」右側では「2」が表示されます。
続けて、Content1Removeをクリック。左側のmainがremoveされる。
再度、Content1Loadをクリック。再び左側のmainがアタッチされて表示される。
左側のmainをクリックすると、今度は「1」のはずが「2」と表示されます。
removeでは、読み込んだ子のSWF自体をunloadしていません。この時同じリンケージ識別子があると再アタッチの際、あとで設定されたリンケージ識別子のクラスが適用されるみたい。シンボル自体は正しく表示されるのだけれど。
この症状はunloadをした場合とか、上書きで読み込んだ場合等は発生しないです。
Content1Unloadをクリック。Content1Loadをクリックした場合、正しく「1」が表示されます。
再アタッチしなければ問題なさそうな気もする。
とにかくリンケージ識別子のネーミングには気をつけよう。
というか僕的にはunloadしたい。
読み込んだものを消さない仕様の場合(に限らないが…)、複数人で制作する場合って、ある程度、親子それぞれの仕様とかネーミングルールとか気をつけて決めないと結合した時に面倒なことになりそうですね。
Progression4 LoopListを試してみた
2009 年 10月 23 日 金曜日 kosukeProgression4に追加された新しいコマンドリストの一つにLoopListがあります。
LoopListは名前の通り、登録されたコマンドを繰り返し処理するコマンドリストです。
これを試していてProgression4で変わったことも気づいたので記録します。
LoopListはループの回数をrepeatCountで指定出来ます。
上のサンプルだと、indexSceneでは、
this.addCommand( new LoopList( 2, null, new DoTweener( this.logo, { scaleX:0.5, scaleY:0.5, transition:"easeNone", time:0.25 } ), new DoTweener( this.logo, { scaleX:1, scaleY:1, transition:"easeNone", time:0.25 } ) ) ) |
Scene1では、
this.addCommand( new LoopList( 2, null, new DoTweener( this.logo, { scaleX:2, scaleY:2, transition:"easeNone", time:0.25 } ), new DoTweener( this.logo, { scaleX:1, scaleY:1, transition:"easeNone", time:0.25 } ) ) ) |
で、それぞれ2回繰り返しを指定しシーン遷移時に拡大縮小しています。
これは問題ないと思う。
注意すべきは、コマンドを単体で使う時。
Scene2のLoopListは、ナビゲーションから開始や停止できるようにしています。
this.t0 = new DoTweener( this.logo, { x:520, transition:"easeNone", time:0.5 } ); this.t1 = new DoTweener( this.logo, { x:120, transition:"easeNone", time:1 } ); this.t2 = new DoTweener( this.logo, { x:320, transition:"easeNone", time:0.5 } ); this.comm = new LoopList( 2, null, this.t0, this.t1, this.t2 ); |
ここではシーン遷移時に処理しているのではなくて、LoopListのインスタンスをマウスイベントで実行したり停止したりしています。
注意すべきは、LoopListを実行して停止してもループ回数を数えるcountは0に戻らないという点。
なので、Scene2でexecuteボタンを押すと、初回は2回ループで停止しますが再度executeを実行してからは無限ループになります。止めるにはstopを使う。
countは読み取り専用で書き込めないのと、カウンタを戻すresetメソッドはprotectedで宣言されている為countを戻すのは、あらかじめLoopListを継承したカスタムコマンドを使うとかかなぁと。
stopメソッドで止めた時と、interruptメソッドで中断した時の挙動の違いも確認。
stopで止める時は、処理中のコマンドが終わった時点でループが停止になる。
停止後に再度executeした時も、その次のコマンドから実行されています。
対して、interruptで中断した時は他と同様にコマンド中断処理が実行されます。
再度executeした時も、中断処理後そのままならループの最初のコマンドから実行される。この時もcountは0なっていない。
またProgression3と違って、コマンド中断時の処理方法をinterruptTypeで指定できる。
CommandInterruptTypeクラスに定数が用意されていて、
- ABORT:int = 1
コマンド中断時、その時点の状態で停止するように指定します。 - RESTORE:int = 0
コマンド中断時、処理が実行される以前の状態に戻すように指定します。 - SKIP:int = 2
コマンド中断時、処理が完了された状態と同様になるように指定します。
のようになっています。デフォルトはSKIPみたい。
これまで、エラーをキャッチしてexecuteCompleteとかやっていたのをこれだけで処理できるわけです。
中断した時のループ位置をとって、そこからループ再開させたりとかややこしいことをしたい場合は、イベント発生をキャッチして処理しなきゃいけないですが、大抵の場合、このinterruptTypeのパターンで済むんじゃないですかね。
上のサンプルでは、コンボボックスでLoopListに登録しているDoTweenerのinterruptTypeを変更出来るようにしています。挙動の違いが確認できると思う。
やっぱり便利になってるなぁ。
Progression4 Bata SceneLoaderを試してみた
2009 年 10月 14 日 水曜日 kosukeProgression4 Bata、粛々と試しています。
目玉機能のSceneLoaderを試してみたのだけど、SCENE_POST_UNLOADイベントの挙動で悩んでいる。
API Referenceによれば、SceneLoaderがSWFを読み込み済みで、シーン移動時の目的地がシーンオブジェクト自身、または親階層の時にSceneEvent.SCENE_UNLOADが発生した時、atSceneUnloadが動くはずだと思う。
これを前提に試してみたものは、
indexScene以下に、4つのSceneLoaderを作って別々のSWFを読み込ませている。
SceneLoaderのatScenePreLoadでローディングバーを表示している。
SceneLoaderのatSceneUnloadでunloadを実行しているのだけれど、親に戻る時(Progression4Bataの文字を押した時)はもれなくSCENE_POST_UNLOADイベントが発生して、atSceneUnloadが動いている。ここまではOK。
問題は別のSceneLoaderに移動した時、atSceneUnloadが動いたり動かなかったりする。
atSceneUnloadが動かなければunloadがされないので、もう一度同じSceneLoaderに移動した時は、atScenePreLoadは動かないのでロディングバーは表示されない。これは期待通り。
Flashの出力を見てもscenePostUnloadが出力されないのだが、なんでなんだろう。
ズームのブラー
2009 年 10月 13 日 火曜日 kosukeこちらは作ってみたけど、重くて使えなかったという代物。残念な話が続くなぁ。
ズームのブラー表現する場合って、どうしたって繰り返し拡大したイメージを重ねるしかないのかなぁ。
何かあったら教えて欲しいです。
var blur:BlurZoom = new BlurZoom( ズームをかけたい対象, ズーム, オフセットX(0.5が中心), オフセットY(0.5が中心), クオリティ ) |
で適用します。
重いですがどうぞ。
画像が小さければギリギリ使えるかも。
package jp.nipx.effect{ import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.DisplayObjectContainer; import flash.geom.Matrix; import flash.geom.Rectangle; import flash.display.PixelSnapping; import flash.geom.ColorTransform; public class BlurZoom extends Bitmap{ public function BlurZoom( container:DisplayObjectContainer, zoom:int=0, offsetX:Number=0.5, offsetY:Number=0.5, quality:Number=5 ){ super( new BitmapData( 1,1 ), PixelSnapping.AUTO, true ); this._rect = new Rectangle(); this.container = container; this._zoom = zoom; this._offsetX = offsetX; this._offsetY = offsetY; this._quality = quality; this.render(); } public var container:DisplayObjectContainer; private var _rect:Rectangle; private var _distance:Number = 0; private var _zoom:int; public function get zoom():int{ return this._zoom; } public function set zoom(n:int):void{ this._zoom = n; this.render(); } private var _offsetX:Number; public function get offsetX():Number{ return this._offsetX; } public function set offsetX(n:Number):void{ this._offsetX = n; this.render(); } private var _offsetY:Number; public function get offsetY():Number{ return this._offsetY; } public function set offsetY(n:Number):void{ this._offsetY = n; this.render(); } private var _quality:Number; public function get quality():Number{ return this._quality; } public function set quality(n:Number):void{ this._quality = n; this.render(); } public function render():void{ if( this.container.contains( this ) ) this.container.removeChild( this ); if( this._zoom == 0 ) return; var rect:Rectangle = this.container.getRect( this.container ); var width:Number = rect.right - rect.left; var height:Number = rect.bottom - rect.top; if( this._rect.equals( rect ) ){ this.bitmapData.fillRect( this._rect, 0x00000000 ); } else{ this.bitmapData.dispose(); this.bitmapData = new BitmapData( width, height, true, 0x00000000 ); var a:Number = Math.sqrt( Math.pow( width, 2 ) + Math.pow( height, 2 ) ); var b:Number = a + 1; this._distance = b / a - 1; } var tx:Number = ( 0<rect.left ) ? 0 : -rect.left; var ty:Number = ( 0<rect.top ) ? 0 : -rect.top; this.bitmapData.draw( this.container, new Matrix( 1, 0, 0, 1, tx, ty ) ); for( var i:int=1; i<this._zoom; i++ ){ var s:Number = 1 + ( this._distance * this._quality ) * i; var x:Number = ( width * s - width ) * this.offsetX; var y:Number = ( height * s - height ) * this.offsetY; this.bitmapData.draw( this.container, new Matrix( s, 0, 0, s, tx - x, ty - y ), new ColorTransform( 1, 1, 1, 1/this._zoom ), null, null, false ) } this.x = -tx; this.y = -ty; this.container.addChild( this ); } } } |
Progression 4 Bata のことはじめ
2009 年 10月 12 日 月曜日 kosukeProgression4のBata版が公開されて少し起ちますが、先日、試しにとあるProgression3プロジェクトをProgression4に書き換えてみました。
といっても、純粋にProgression3の構文をProgression4で動くように書き換えただけですので、まだProgression4の新機能はほとんど試せていません。
Progression3で出来ていたことを、Progression4にそのまま移行すること自体は問題なく出来る感じです。
以下、細かい感じですが、今のところ迄で気づいたことを記録しておきます。
■ブラウザウインドウ最小サイズの管理がSWFForceSizeからSWFSizeに変わったみたい。
SWFSizeはSWFForceSizeの機能をASに埋め込んでFlashからHTMLの最小サイズ、最大サイズを設定できるライブラリ。
Progression4 Bataでは、このライブラリが採用されている模様。
Progression3では、HTMLへの記述で最小サイズを指定していました。
progression.embedSWF( { width:800, height:600, adjustHorizontal:true, adjustVertical:true, centering:true, flashvars:{ }, params:{ bgcolor:"#000000", wmode:"window", allowscriptaccess:"samedomain" }, attributes:{ } } ); |
このwidthとheightで最小サイズが設定されていたのですが、Progression4 BataだとWebConfig()を生成した段階なんかで設定されるようだ。
WebConfigの第二引数でSWFSizeを有効化するかどうかとなっている。
で、このまま何もしないとflaファイルのステージサイズが最小サイズになる模様。
またPreloaderを使っている場合、Preloaderの段階では最小サイズの制限が設定されない。
なので、全画面Flash、最小サイズ固定で作る場合、Preloaderの段階で明示的にSWFSize.initializeを実行しておいたほうが良さそう。
■Listenコマンドが無くなったみたい
ListenコマンドはProgression3にあったイベント待ちのコマンド。
あまり使うこともないと思いますが、Listenと同様のことをするならFuncコマンドを使う感じですかね。
protected override function atSceneLoad():void { var dispatcher:SceneObject = this; addCommand( new Trace( "1st" ), new Func( function():void{ Tweener.addCaller( this, { count:1, delay:2, onComplete:function():void{ dispatcher.dispatchEvent( new Event( Event.COMPLETE ) ) } }); }), new Func( new Function(), null, dispatcher, Event.COMPLETE ), //Listenコマンド代用 new Trace( "2nd" ), new Func( function():void{ Tweener.addCaller( this, { count:1, delay:2, onComplete:function():void{ dispatcher.dispatchEvent( new Event( Event.COMPLETE ) ) } }); }), function():void{ this.listen( dispatcher,Event.COMPLETE ) }, //匿名関数でListenコマンド代用 new Trace( "finish" ) ); } |
上記の場合はそもそもListenでやらず、Tweener.addCallerの書かれたFuncでイベント待ち設定すれば良い訳だけれど。
■コマンドの処理状態プロパティがrunningからstateに変わったみたい。
Progression3では、コマンド処理中であるかをどうかを、runningで判定していましたが、これがstateプロパティに変わった模様。Progression3での中断処理のプロパティinterruptingと一緒にstateに統合された感じかなと。
■Progressionのオブジェクトを継承したSWFを読み込んでもガベージコレクションされるみたい。
問題になっていたメモリリークも大分解消されているっぽい。
以前メモリリークで試したサンプルをProgression4に書き換えてみたのがこちら。
サンプル:Progression4 Bataのメモリ使用状況確認
最初の読み込み分が消えいないような気がしますが、ひたすら増えてくってことは無くなった模様。
読み込まれるSWFの方にシーンを持たせるって、目玉の機能はまだ試せていないので、その時のメモリの挙動も次試してみようと思ってます。
ベータ版の状況なんで、今後どう変わってくるかわからないですが正式版のリリース心待ちにしてます。
PixelateFXを詳しく
2009 年 9月 25 日 金曜日 kosukePixelateFXはモザイク表現のサンプルになっている。
モザイクの仕組み的は対象を縮小してBitmapに転写した後、元のサイズに拡大することで、結果的に低画素の画像を拡大していることとなりモザイクが表現されています。
モザイクは色々なライブラリが公開されていて、今更って話なところもあるけどTweensyFXでもPixelateEffectクラスでモザイク効果を表現できるようになっています。
■PixelateEffect
モザイク表現のエフェクト。
■プロパティ
一つだけ…。
-
amount:Number = 50
- モザイク用に画像を縮小する際のスケール値。
- 50なら1/50に縮小。1なら縮小なし(モザイク無し)
- この値が大きい程、モザイクのサイズが大きくなる。
PinkBlobs2FXを詳しく
2009 年 9月 23 日 水曜日 kosukeTweensyFXのサンプル、PinkBlobs2FXを調べてみる。
サンプルにはPinkBlobsFXとPinkBlobs2FXがあるけど、PinkBlobsFXは円のオブジェクトが上下に移動するモーションで、PinkBlobs2FXはPinkBlobsFXにメタボール効果を加えたものになっている。共に円のトゥイーンはAlianRainFXと同様なので割愛することにします。メタボール効果の手法を見てみる。
メタボールは前にエントリーしたことがあるけど、輪郭がギザギザにるのでどうにか滑らかにする必要があった。
PinkBlobs2FXはこの処理の参考になります。
まず、メタボールを作る手法は以前のものと同じ。BitmapDataのthresholdを使ってしきい値以下を置き換えている。
thresholdを扱うエフェクトとしてThresholdEffectが用意されています。
ポイントはしきい値以下を透明に置き換えるところ。
ぼけ足のついた円を上下に動かしていますが、透明に近いところはこの処理で消える。複数の円が動いているのでぼけ足の重なったところはより残るようになる。これを使ってメタボールのくっつく表現をするわけです。
エッジの処理は、これまで同様、前回迄の描画に徐々に消えていく設定のColorEffectを加え、Blurをかけてぼかす。
これにThresholdEffectで分離。
今回の円を描画。
ここまでがBitmapLayerの処理で、更にBitmapLayer自体にGlowFilterを適用して外側に光彩を加えることでエッジのジャギーを無くしている形。かなり溶けて行く感じのメタボールになっていますが綺麗に出来ているなぁと感じました。
TweensyFXのサンプルから学ぶの残り
2009 年 9月 22 日 火曜日 kosukeTweensyFXのサンプルから学ぶ。ここまで名前順に調べエントリーしてきたのだけど、残りのサンプルはEmitterの設定の違いであって目新しいところが少なそうです。
Emitter設定時の色や放出角度の値だったりと手法として参考になる感じじゃないように思えた。なので残りのFXサンプルから調べるものを取捨選択することにしました。
残りのFXサンプルのうち、MagicFX、SmokeFX、ZanyBoltsFXの3つは、FireFXやFireSmokeFXのグラフィックやEmitter設定の違いだけって感じなので省くことに決めた。
残った、
OrbitingMagicFXの回転しているところ。
PinkBlobsFXのメタボールみたいになるところ。
PixelateFXのモザイクを調べてみようと思う。
SnowLeopardのSafariの件
2009 年 9月 17 日 木曜日 kosuke今日の仕事の半分を雪豹のSafariに費やされたので記録しておく。
64bit化が進んだSnowLeopardでは、Safariは通常64bitモードで動いています。
しかし今のところほとんどのプラグインは32bit版。
Flash Playerもまだ32bit版のみ。
FireSmokeFXを詳しく
2009 年 9月 16 日 水曜日 kosukeTweensyFXのサンプルから学ぶ。FireSmokeFXを詳しくみてる。
FireSmokeFXの煙のEmitterと炎のEmitterの二種類を使っている以外、中身はおおよそFireFXと同様の仕組みです。
なので異なる部分を中心に気になったところを記録しました。