ProgressionでFlash Player判定後、無かったらリダイレクトする場合。
2011 年 2月 9 日 水曜日 kosukeこれよりもっと手前でコールバック関数に別のものを指定するなどでもよいですが、なるべく元のソースを変更せずリダイレクトするならprogression_unpack.jsの以下の部分を修正すればいいと思う。
progression_unpack.js
var complete = function( e ) { $p( $( "disabled_javascript" ), { style:{ display:"none" } } ); if ( e.success ) { $render( config ); var target = d.getElementById( config.attributes.id ); if ( target ) { target.style.outline = "none"; target.focus(); } } else { //ここに追記 window.location = 'リダイレクト先のURL'; $css( "#" + config.htmlContentId, { display:"block" } ); } }; |
上の部分の修正をしてprogression.jsとして使えばOK。
このJavaScriptによる実行(JavaScriptによる判定、expressinstall.swf実行結果の正否)なので、JavaScript自体が無効の場合、リダイレクトしない。なのでリダイレクトの場合もhtmlにリンクを設置しておくべきでしょう。
DoExecutor
2010 年 8月 31 日 火曜日 kosukeProgression 4 で追加されたコマンドにDoExecutorがあります。
このコマンドはその名の通り指定したExecutorObjectを実行出来るのですが、便利そうなケースがあったので記録しておきます。
- トップページは、直下に子シーンを持つ。
- 子シーンは背景イメージを変更する。
- トップページでは子シーンのいずれかの背景イメージを表示する。
ぎゅっと簡略した形ですがギャラリーコンテンツなんかでありそうなケース。
子シーンはこんな感じです。シーンに到達したら背景イメージを追加するだけ。
package{ import flash.display.Bitmap; import jp.progression.commands.Prop; import jp.progression.commands.display.*; import jp.progression.scenes.SceneObject; public class ChildScene extends SceneObject{ public function ChildScene(name:String=null, initObject:Object=null){ super(name, initObject); } public var bmp:Bitmap; //背景イメージ protected override function atSceneLoad():void{ //※確認用、シーンパスを表示 var indexScene:IndexScene = parent as IndexScene; addCommand( new Prop( indexScene.field, { text:sceneId.path } ) ); } //背景イメージ追加 protected override function atSceneInit():void{ addCommand( new AddChildAt( container, bmp, 0 ) ); } //背景イメージ削除 protected override function atSceneUnload():void{ addCommand( new RemoveChild( container, bmp ) ); } } } |
トップページはこちら。トップページでは背景イメージをランダムに選んで表示したいのですが、背景イメージの表示は子シーンにも記述があります。なのでこれをDoExecutorコマンドで動かしてしまおうという魂胆。
package{ import flash.display.Bitmap; import flash.text.*; import jp.nipx.debug.btn.Btn; import jp.progression.casts.*; import jp.progression.commands.*; import jp.progression.commands.display.*; import jp.progression.commands.lists.*; import jp.progression.commands.managers.*; import jp.progression.commands.media.*; import jp.progression.commands.net.*; import jp.progression.commands.tweens.*; import jp.progression.data.*; import jp.progression.events.*; import jp.progression.executors.*; import jp.progression.scenes.*; public class IndexScene extends SceneObject{ public function IndexScene(){ var btn:Btn; btn = new Btn( { label:"Index", sceneId:new SceneId( "/index" ), x:5, y:225 } ); container.addChild( btn ); btn = new Btn( { label:"1", sceneId:new SceneId( "/index/1" ), x:btn.x + 5 + btn.width, y:225 } ); container.addChild( btn ); btn = new Btn( { label:"2", sceneId:new SceneId( "/index/2" ), x:btn.x + 5 + btn.width, y:225 } ); container.addChild( btn ); btn = new Btn( { label:"3", sceneId:new SceneId( "/index/3" ), x:btn.x + 5 + btn.width, y:225 } ); container.addChild( btn ); addScene( new ChildScene( "1", { bmp:new Bitmap( new Img1( 250,250 ) ) } ) ) as ChildScene; addScene( new ChildScene( "2", { bmp:new Bitmap( new Img2( 250,250 ) ) } ) ) as ChildScene; addScene( new ChildScene( "3", { bmp:new Bitmap( new Img3( 250,250 ) ) } ) ) as ChildScene; field = new TextField(); field.defaultTextFormat = new TextFormat( "_ゴシック", 10, 0x333333 ); field.x = 5; container.addChild( field ); } public var field:TextField; protected override function atSceneInit():void{ //子シーンをランダムに選ぶ var i:Number = Math.round( numScenes * Math.random() ); i = ( i == numScenes ) ? 0 : i; var scene:SceneObject = getSceneAt( i ); addCommand( new Prop( field, { text:sceneId.path } ), //子シーンexecutorを動かす、シーン到着時のイベントを送信 new DoExecutor( scene.executor, new SceneEvent( SceneEvent.SCENE_INIT ) ) ); } protected override function atSceneGoto():void{ addCommand( new RemoveChildAt( container, 0 ) ); } } } |
こんな形で使うと似たような処理をいくつものSceneObjectに書くようなことを減らせることもありそうですね。
ここでは、もともとSceneObjectに実装されているイベントでExecutorObjectを動かしていますが、独自のイベントでExecutorObjectを動かすことも可能です。
このあたりExecutorObjectってなに?ってところから、解りやすく書かれている記事を見つけました。
Progression 4 の DoExecutorコマンドを使い倒す!
DoExecutorコマンドは、動かしたExecutorObjectの処理でもコマンドシークエンスが有効ってところがとても便利ですね。
CastPreloaderのイベント処理
2010 年 3月 10 日 水曜日 kosukeProgression 4.0.1 Public Bate 1.3 を使っていて気づいたこと。CastPreloaderではProgressionインスタンスが生成されていないので、CastPreloader内でCastSpriteなど、Progressionの表示オブジェクトをAddChildコマンドで加えても、CastSpriteのatCastAddedなどイベント処理の実行がされないようだ。
たとえば以下のように、CastPreloaderのatCastLoadStart処理中にCastSpriteを加え,そのcastAddedイベント中にTraceコマンドで出力を試みても「 sample onCastAdded 」のtraceは出力されない。
override protected function atCastLoadStart():void{ var sample:CastSprite = new CastSprite(); sample.onCastAdded = function():void{ addCommand( new Trace( "sample onCastAdded" ) ) } addCommand( new AddChild( this.foreground, sample ) ); } |
castAddedイベントが正しく処理されるようにするには、PreloaderでもProgressionのインスタンスを生成しておくと期待通りになる。
override protected function atCastLoadStart():void{ Progression.initialize( new WebConfig() ); var manager:Progression = new Progression( "preloader", this.stage ); var sample:CastSprite = new CastSprite(); sample.onCastAdded = function():void{ addCommand( new Trace( "sample onCastAdded" ) ) } addCommand( new AddChild( this.foreground, sample ) ); } |
ここでは、atCastLoadStart内でProgressionクラスの初期化とインスタンスを作っているけど、コンストラクタで作った方が自然かな。
ちなみに、PreloaderにProgressionクラスを加えると10K以上ファイルサイズが増えます。不要ならProgressionインスタンスをPreloaderで使う必要はないでしょう。
Preloaderの性質を考えるとそのファイルサイズは小さいほどよいと思いますので、CastPreloaderがデフォルトでProgressionインスタンスを生成しないのは僕は望ましいと思います。おそらくそのあたりがこの仕様の理由なんじゃないかと思ってみたり、みなかったり。
executeのextra引数
2010 年 2月 23 日 火曜日 kosukeProgressionでコマンドを実行するexecuteメソッドには、extraという引数があります。
シーン遷移でコマンドを実行する場合、extraを使うケースはほとんどないと思いますので、あまり馴染みのない引数ですが、コマンドを単体で利用する場合、extraを使うと便利なこともあります。
コマンドリストを実行する際extraを渡すと、コマンドリストに登録された全てのコマンドは実行時に渡されたextraをリレーします。つまり、登録されたコマンドはすべて実行時にextraを利用できます。
たとえば、一連のアニメーションを登録したコマンドリストを実行し、最後の挙動だけ異なるようにしたい場合など、extraを渡して挙動を変更することが可能です。
例:同じコマンドリストを実行し、最後のコマンドでextraで渡した異なる色値に変更する。
btn0.label = "RED"; btn1.label = "GREEN"; btn2.label = "BLUE"; _comm = new SerialList( null, new Prop( ball, { x:450, y:130 } ), new Prop( ball.transform, { colorTransform:new ColorTransform() } ), [ new DoTweener( ball, { x:200, transition:"easeOutSine", time:3 } ), new DoTweener( ball, { y:350, transition:"easeOutBounce", time:3 } ) ], new Func( function():void{ var trans:ColorTransform = new ColorTransform(); trans.color = this.extra; ball.transform.colorTransform = trans; }) ); _comm.interruptType = CommandInterruptType.RESTORE; btn0.addEventListener( MouseEvent.MOUSE_DOWN, function(e:MouseEvent):void{ if( _comm.state == ExecutorObjectState.EXECUTING ) _comm.interrupt( true ); _comm.execute( 0xFF0000 ); }); btn1.addEventListener( MouseEvent.MOUSE_DOWN, function(e:MouseEvent):void{ if( _comm.state == ExecutorObjectState.EXECUTING ) _comm.interrupt( true ); _comm.execute( 0x00FF00 ); }); btn2.addEventListener( MouseEvent.MOUSE_DOWN, function(e:MouseEvent):void{ if( _comm.state == ExecutorObjectState.EXECUTING ) _comm.interrupt( true ); _comm.execute( 0x0000FF ); }); |
PreloadSWF
2010 年 2月 10 日 水曜日 kosukeProgression 4.0.1 Public Beta 1.3 ですが過去のバージョンに比べると、とても完成度が上がっていますね。Beta版ということで避けていた方もそろそろ手をだしていい時期と思いました。
先日とある案件で使ってみて、便利なところにいろいろ気づきましたので、いくつか記事にしようと思います。
今回はあらたに追加されたPreloadSWFコマンドについて。
このPreloadSWFを使うと任意のタイミングでプリロードを開始できます。
これがどう便利かというと、CastPreloaderクラスを使って子SWFを読み込む場合おおよそ以下の流れとなります。
1.読み込む直前。この時、atCastLoadStart にて非同期処理が可能。
2.読み込み。この時、atProgress でロード毎に処理が可能。(プログレスバーを進めるなど)
3.読み込み完了。この時、atCastLoadComplete にて非同期処理が可能。
通常、これでまったく問題ないのですが、プリローダーで子SWF以外にも外部ファイル(画像だったり、サウンドだったり、他のSWFだったり)を読み込み、プログレスバーを進行させたいこともあると思います。
この場合、Progressionのコマンドで行うなら、プログレスバーの進行度合いを管理しつつ、atCastLoadStart か、atCastLoadComplete 内で読み込む形になります。こうなるとプログレスバーの管理が若干面倒ですよね。PreloadSWFコマンド(とLoaderList)を使えば、これをとてもスマートに行えます。
例:atCastLoadStart で外部ファイルの読み込みとCastPreloaderの読み込みを実行。
override protected function atCastLoadStart():void{ addCommand( new LoaderList( { onProgress:function():void{ trace( this.percent + "%" ); } }, new LoadBitmapData( new URLRequest( "image1.jpg" ) ), new Trace( "image1 完了" ), new PreloadSWF( {} ), new Trace( "Preloader 完了" ), new LoadBitmapData( new URLRequest( "image2.jpg" ) ), new Trace( "image2 完了" ), new LoadSound( new URLRequest( "sound1.mp3" ) ), new Trace( "sound1 完了" ) ), new Wait( 3 ) ) } |
この場合、image1.jpg の読み込み後、CastPreloaderの読み込みが実行され、その後、image2.jpg、sound1.mp3 を読み込み、3秒後に atCastLoadStart が完了します。特に atCastLoadComplete を設定しなければ、この後、読み込んだCastDocumentの処理に移行します。
少し注意したいのは、atCastLoadCompleteは、PreloadSWFの完了ではなく、atCastLoadStart の完了後実行されることでしょうか。
この方法をとれば、プログレスバーは、LoaderListの onProgrss で進行させるだけですむようになります。
読み込むファイルの容量の差が大きく、プログレスバーの進行がスムーズでない時は、各読み込みコマンドのfactorプロパティを設定して相対的な重要度を設定します。
たとえば、
image1.jpg が 10Kbyte
index.swf が 10Kbyte
image2.jpg が 10Kbyte
sound1.mp3 が 30 Kbyte
なら、sound1.mp3を読み込むコマンドのfactorを3にすると、プログレスバーの進行の差が少なくなると思います。
BreakとReturn
2009 年 12月 7 日 月曜日 kosukeProgression 4 にある中断コマンド、BreakとReturnについて、その違いをメモ。
シーン /index/a/1 から シーン /index に移動する時、
シーン /index/a/1 のGotoで Breakの場合とReturnの場合で試します。
protected override function atSceneGoto():void{ this.addCommand( new Break(), // ←これを使う場合と new Return(), // ←これを使う場合の違い new Trace( "a/1 - atSceneGoto" ) ) } |
Breakを使った場合
new Trace( “a/1 – atSceneGoto” ) は実行されないが、その後はシーン遷移に基づき シーン /index まで実行される。
つまりBreakの場合、処理中のコマンドリストはBreak実行された時点で完了となり、その後のコマンドリストは引き続き処理される。
試しに、Gotoを以下にした場合、
protected override function atSceneGoto():void{ this.addCommand( new SerialList( { onComplete:function():void{ trace("onComplete"); } }, new Break(), new Trace( "SerialList" ) ), new Trace( "a/1 - atSceneGoto" ) ) } |
new Trace( “SerialList” )は処理されないけど、
trace(“onComplete”)
new Trace( “a/1 – atSceneGoto” )
は実行される。
Returnを使った場合
new Trace( “a/1 – atSceneGoto” ) は実行されず、Returnが実行された時点でシーン遷移が停止する。
Breakの時と同様以下を試すと
protected override function atSceneGoto():void{ this.addCommand( new SerialList( { onInterrupt:function():void{ trace("onInterrupt"); } }, new Return(), new Trace( "SerialList" ) ), new Trace( "a/1 - atSceneGoto" ) ) } |
new Trace( “SerialList” )
new Trace( “a/1 – atSceneGoto” )
とも処理されず、
trace(“onInterrupt”)は実行される。つまり中断になるわけです。
ちなみに、Progressionクラスの stop() メソッドもReturnと同じっぽい。
試しに以下を実行した場合も結果は同じだった。
protected override function atSceneGoto():void{ this.addCommand( new SerialList( { interruptType:CommandInterruptType.SKIP, onInterrupt:function():void{ trace("onInterrupt"); } }, function():void{ manager.stop(); }, new Trace( "SerialList" ) ), new Trace( "a/1 - atSceneGoto" ) ) } |
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が出力されないのだが、なんでなんだろう。
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の方にシーンを持たせるって、目玉の機能はまだ試せていないので、その時のメモリの挙動も次試してみようと思ってます。
ベータ版の状況なんで、今後どう変わってくるかわからないですが正式版のリリース心待ちにしてます。
Progressionでガベージコレクションされないのをどうにかしようとした記録
2009 年 8月 7 日 金曜日 kosukeProgressionがとにかく便利でよく使っているのですがメモリーまわりにはずーっと悩まされている。
特に困っているのがProgressionのオブジェクトを含む外部swfを読み込むとunloadしてもメモリーに残ったまま消えない。
外部swfでもProgressionのオブジェクトを使いたいのだけれど今のところこの事情で外部swfには思うようにProgressionのクラスを使えていないです。