[AS]ProgressionのPreloaderで”「Error #2044: ハンドルされていない ioError : text=Error #2036: 読み込みが未完了です。 URL: index.swf?”となってしまう場合の対処方法

ProgressionでPreloader.asからindex.swfを読み込むときにこんなエラーダイアログに悩まされてました。

どういうときにこのエラーになるかというと、Preloader.swfがindex.swfの読みこみが終わらないうちに、ブラウザを閉じてしまうとこうなります。
index.swfが重い場合、「ローディング長いからもう観なくていいや」ってブラウザ落とすと上記ダイアログで固まるんですね。
最高にうざい状態です。

で、エラーとしては、IOErrorってわかってるので、そこのエラー処理を書いてあげればいいんですけど、読み込みの処理はPreloader.asの親クラスに書いてあるので、どのオブジェクトに対して、IOErrorのハンドリングをすればいいのかわからなかったのですが、ついに重い腰をあげて親クラスの中身を覗いてみたら、5分で解決したというオチです。

結論としては、こんな感じ。

//Preloader.asの atReady()の中に一行追加
override protected function atReady():void{
//↓これを追加
this.addEventListener(IOErrorEvent.IO_ERROR, function():void { trace("エラー") } );
}

Preloader.asのatReady()の中で、thisに対してIOErrorのハンドリングを書けばOKです。

なぜ、thisに書くかといえば、Preloader.asの親クラス、CastPreloader.asの中にこの一行があるので。

_loader.contentLoaderInfo.addEventListener( IOErrorEvent.IO_ERROR, super.dispatchEvent );
//_loaderはindex.swfを読むためのもの

リスナー関数でsuper.dispatchEventを飛ばしてるから、Preloader.asのthisでイベントを取れるようです。

ちなみに、CastPreloader.as内の_loaderは、Preloader.asからはcontentLoaderInfoで参照が取れるので、Preloader.as内で、

//thisの代わりにcontentLoaderInfoでイベントをハンドリング
contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, function():void { trace("エラー") } );

という感じで書いてみると、一応traceは出力されるんですけども、traceの前に最初のエラーが表示されてしまい、まったく意味がないことになりました。
なので、contentLoaderInfoではなく、素直にthisでIOErrorEventをハンドリングすればOK。

[AS]progression4 Index.swfからPreloaderのforeground、backgroundを参照するにはCastDocumentインスタンス経由で可能

便利すぎて自分の実装力がどんどん低下してるprogression4のメモ。

Preloaderにて表示オブジェクトを追加するには、backgroundかforegroundを使うことになります。
で、index.swfにてPreloaderのbackgroundおよびforegroundにアクセスするには、index.swfのドキュメントクラスであるCastDocumentがbackgroundおよびforegroundのプロパティを持っているので簡単に参照できます。

そこで、ちょっとはまったのが、このドキュメントクラスの参照の仕方。
結論だけ書くと、こんな感じ。

trace(manager.current.container.root);//出力:[object:index]
//Indexのプロパティを取得するにはIndexでキャストする。
trace(Index(manager.current.container.root).background.numChildren)
//[2011.10.19追記]
//Indexでキャストだとうまくいかなくなったみたい
//CastDocumentでキャストする
trace(CastDocument(manager.current.container.root).background.numChildren);

要はSceneObjectが持つcontainerプロパティのrootが、index.swfのドキュメントクラスとなっているみたい。

[AS]progression4RC departedSceneIdやdestinedSceneIdをif文とかで判定できないってときはここに注意

progressionを使ううえでの便利プロパティ。
departedSceneId
destinedSceneId
以前のエントリでも書いてるように、出発シーンと、到着シーンを取得できます。
ただし↑はprogression3の書き方。
progression4では、progressionインスタンスは無くなり、managerが持ってるプロパティ。

manager.destinedSceneId;
manager.departedSceneId;

これを使って、if文の処理をしたいときちょっとはまったのでメモ。
destinedSceneIdとかdepartedSceneIdをtraceすると、
/indexとか、/index/aboutとか、sceneIdを出力してくれます。
なので、戻り値のデータ型はString?と思いきや、実は違います。

trace(manager.departedSceneId)//出力(例):/index
trace(typeof manager.departedSceneId)//出力:object(stringではない)

なので、if文で判定するときは、↓だと上手く判定されません

//manager.departedSceneId == manager.destinedSceneIdは中身がなんであろうと常にtrueとなる
if(manager.departedSceneId == manager.destinedSceneId){
    //処理
}

traceしたときに見えるSceneId名で判定させる場合は.toString()をつければOK

//
if(manager.departedSceneId.toString() == manager.destinedSceneId.toString()){
    //処理
}

※2010/04/17/16:30追記
コメントにてequals()メソッドというものを教えて頂きました。
こっちの方が簡単ですね。

if(manager.departedSceneId.equals(manager.destinedSceneId)){
	//処理  
}

※4のBetaまではtoStringなしでも判定してくれてたような気がするけど、RCからは無いとダメみたい。

[AS]progression4 PreloadSWF()を実行するタイミングで「Error: 対象の CastPreloader は準備が完了していないか、すでにコンテンツが読み込まれています。」エラーに注意

タイトルどおりの現象。

※PreloadSWF()については↓の記事がとてもわかりやすいです。
jp.ikekou.blog.Main:Progression 4.0.1 Public Beta 1.3 追加機能① PreloadSWFクラス

で、これを観ながら上記ブログとは少し変えながら試してみたのですが、タイトルのエラー発生。
変更した箇所はatCastLoadStart()の部分で、以下のソース

override protected function atCastLoadStart():void {
	var loaderList:LoaderList = new LoaderList({
		onProgress:function():void{
			trace(this.percent)
		}
	});
	loaderList.addCommand(
        //new PreloadSWF()が遅い
		new LoadSound(new URLRequest("sound/sample.mp3"))
		,new PreloadSWF()
	);
	loaderList.execute();
}

結論から言うと、new PreloadSWF()の書く順番が重要。
↓このように書けばOK

override protected function atCastLoadStart():void {
	var loaderList:LoaderList = new LoaderList({
		onProgress:function():void{
			trace(this.percent)
		}
	});
	loaderList.addCommand(
        //PreloadSWF()が早い
		new PreloadSWF()
		,new LoadSound(new URLRequest("sound/sample.mp3"))
	);
	loaderList.execute();
}

ちなみに参考にしたブログでは、loaderListの処理をatCastLoadStart()のaddCommand()内で行い、そこに追加している。
その際には、new PreloadSWF()の順番は影響はしないようす。

override protected function atCastLoadStart():void {
	addCommand(
		function(){
			var loaderList:LoaderList = new LoaderList({
				onProgress:function():void{
					trace(this.percent)
				}
			});
			loaderList.addCommand(
				//new PreloadSWF()の位置は先でもエラーはでない
				,new PreloadSWF()
				new LoadSound(new URLRequest("sound/sample.mp3"))
			);
			this.parent.addCommand(loaderList);
		}
	);
}

command周りの処理の順番がイマイチちゃんと理解していないから、泥臭く検証しないとわからないのよね。。。
ガッツリ理解しとかないとな。。。

[AS]progression4 LoadCommand系のイベント(開始、終了、ローディング、エラー、中断)のまとめ

progressionのコマンドを使ったときのイベント処理追加(開始、終了、ローディング、エラー、中断)のやり方
何種類か方法があるようなのでまとめ。

1.addEventLinstenerを使った一般的な書き方

public function Test2() {
	//コマンドの定義
	var loadSound:LoadSound = new LoadSound(new URLRequest("./sound.mp3"));
	//イベント処理の追加
	loadSound.addEventListener(ExecuteEvent.EXECUTE_START, startHandler);
	loadSound.addEventListener(ExecuteEvent.EXECUTE_COMPLETE, completeHandler);
	loadSound.addEventListener(ExecuteErrorEvent.EXECUTE_ERROR, errorHandler);
	loadSound.addEventListener(ProgressEvent.PROGRESS, progressHandler);
	//コマンドの実行
	loadSound.execute();
}
private function progressHandler(e:ProgressEvent):void {
	trace("e : " + e);
}
private function errorHandler(e:ExecuteErrorEvent):void {
	trace("e : " + e);
}
private function completeHandler(e:ExecuteEvent):void {
	trace("e : " + e);
}
private function startHandler(e:ExecuteEvent):void {
	trace("e : " + e);
}

2.addEventLisnerを使わない書き方
※エラー発生時のイベントについては、onErrorとcatchErrorのどちらでも取れるけど、エラーの内容を参照するには、catchErrorを使っておいた方がいいみたい。
(引数:errorでエラー内容が取れる)

public function Test2() {
	//コマンドの定義
	var loadSound:LoadSound = new LoadSound(new URLRequest("./sound.mp3"));
	//イベント処理の追加
	loadSound.onStart = function(){
		trace("onStart this : " + this);
	};
	loadSound.onComplete = function(){
		trace("onComplete this : " + this);
	};
	loadSound.onError = function(){
		trace("onError this : " + this);
	};
	loadSound.catchError = function(target:Object, error:Error){
		trace("onError this : " + target ,error);
                target.interrupt();
	};
	loadSound.onProgress = function(){
		trace("onProgress this : " + this);
	};	
	//コマンドの実行
	loadSound.execute();
}

3.addCommand内とかでコマンドのインスタンスを作れないときのやり方
※エラー発生時のイベントについては、onErrorとcatchErrorのどちらでも取れるけど、エラーの内容を参照するには、catchErrorを使っておいた方がいいみたい。
(引数:errorでエラー内容が取れる)

public function Test2() {
	//コマンドの定義
	var slist:SerialList = new SerialList();
	slist.addCommand(
		new LoadSound(new URLRequest("./sound.mp3"), null,
			{
				//読み込み開始時
				onStart:function():void{trace("start : " + this)}
				//読み込み完了時
				,onComplete:function():void{trace("onComplete : " + this)}
				//読み込み中
				,onProgress:function():void{ trace("onProgress : "+this); }
				//読み込みエラー
				,onError:function():void{trace("error : "+ this)}
				,catchError:function(target:Object, error:Error):void{trace("error : "+ target,error)}
				//読み込み中断時
				,onInterrupt:function():void{trace("interrupt : "+ this)}
			}
		)
	);
	
	//コマンドの実行
	slist.execute();
}

[AS]progression4 LoadCommand系でのエラー処理とかローディング処理とか中断処理とか

メモ
initObjectでイベントを管理可能

slist.addCommand(
	new LoadSound(new URLRequest("./sound.mp3"), null,
		{
			//読み込み開始時
			onStart:function():void{trace("start")}
			//読み込み完了時
			,onComplete:function():void{trace("onComplete")}
			//読み込み中
			,onProgress:function():void{trace("progress" + this.bytesLoaded);}
			//読み込みエラー
			,onError:function():void{trace("error")}
			//読み込み中断時
			,onInterrupt:function():void{trace("interrupt")}
		}
	)
);
slist.execute();

参考:_level0.KAYAC:Progression4の新コマンドあれこれ

[AS]progression4 Load系コマンド(LoadURLとかLoadSoundとか)で読み込んだデータをインスタンス変数に参照させて取り扱うときの注意

題名のまんまをprogression forumに質問したらすぐに回答を頂けたのでその補足メモ。

まずLoad系のコマンドで読み込んだデータを扱う際の定石は

//※要点を抜粋したコード
var _sound:Sound;
addCommand(
    new LoadSound(new URLRequest("./sound.mp3"))
    ,function(){
        _sound = this.latestData;
        trace(_sound)//出力:[Object Sound]
    }
)

と思ってましたが、progression4からは、getResourceByIdというものを使った方が便利とのこと。
↑のコードを書き換えると

//※要点を抜粋したコード
var _sound:Sound;
addCommand(
    new LoadSound(new URLRequest("./sound.mp3"))
    ,function(){
        _sound = getResourceById( "./sound.mp3" ).data;
        trace(_sound)//出力:[Object Sound]
    }
)

getResourceById()の便利なところは、一度読み込めば、読み込んだデータがどこからでも参照できるというところみたい。
getResourceById( “./sound.mp3” )と書くことでstaticは変数みたいな扱いができるということかな。

↓ざっくり書くとこんな感じ。

//※要点を抜粋したコード
var _sound:Sound;
addCommand(
    new LoadSound(new URLRequest("./sound.mp3"))
    ,function(){
        _sound = getResourceById( "./sound.mp3" ).data;
        trace(_sound)//出力:[Object Sound];
        new Test();
    }
)
//
class Test {
    function Test(){
        trace(getResourceById( "./sound.mp3" ).data)//出力:[object Sound]
    }
}

あとgetResourceByGroupというものもあって同様な扱いができるそうです。
上記の検証で参考になった記事
参考:_level0.KAYAC:Progression4の新機能Resourceを試してみる

でタイトルの本題については、forumに書いたけど↓ではSerialList内のtraceはnullになり

addCommand(
    new LoadSound(new URLRequest("./sound.mp3"))
    ,function(){
        _sound = getResourceById( "./sound.mp3" ).data;
        trace(_sound)//出力:[Object Sound];
        new Test();
    }
    ,new SerialList(null
        ,new Trace(_sound)//出力:null
    )
)

SerialListをfinctionでくくると参照がとれました。

addCommand(
    new LoadSound(new URLRequest("./sound.mp3"))
    ,function(){
        _sound = getResourceById( "./sound.mp3" ).data;
        trace(_sound)//出力:[Object Sound];
        new Test();
    }
    ,function(){
        new SerialList(null
            ,new Trace(_sound)//出力:[Object:Sound]
        ).execute()
    }
)

最後にこれらの集大成的なコード(ドキュメントクラス)
(functionで括らないSerialList内でのTraceでnullになるのは、イマイチ理解できてないけど)

package  {
	import flash.display.Sprite;
	import flash.media.Sound;
	import flash.net.URLRequest;
	import jp.progression.commands.media.DoSound;
	import jp.progression.commands.display.*;
	import jp.progression.commands.lists.*;
	import jp.progression.commands.net.*;
	import jp.progression.commands.tweens.*;
	import jp.progression.commands.*;
	import jp.progression.data.getResourceById;
	/**
	 * @author 393
	 */
	public class Test extends Sprite{
		private var _sound:Sound;
		private var number:int = 0;
		
		public function Test() {
			var slist:SerialList = new SerialList();
			slist.addCommand(
				new LoadSound(new URLRequest("./sound.mp3"))
				,function(){
					_sound = getResourceById( "./sound.mp3" ).data;
					trace("_sound : " + _sound);
					new DoSound(_sound).execute();
					number = 100;
				}
				,new SerialList(null
					,new Trace("SerialList number : " + number)//出力:0
					,new Trace("SerialList sound : " +_sound)//出力:null
				)
				,new Func(next)
			);
			slist.execute();
		}
		private function next():void{
			trace("number : " + number);//出力:100
			trace("nextの_sound : " + _sound);//出力: [object Sound]
			new Test2()
		}
	}
}
import jp.progression.data.getResourceById;

class Test2{
	public function Test2() {
		trace("Test2のgetResourceById( \"./sound.mp3\" ).data : " + getResourceById( "./sound.mp3" ).data);
	}
}

[AS]progression4 managerインスタンスの参照でちょっとはまった

progression4からは、Progressionクラスを参照できるプロパティがmanagerになりました。
(3はprogressionだった)
SceneObjectとかCastSpriteとかが持ってるプロパティなので簡単に参照できます。

んが、ちょっとはまったのでmemo。

SceneObject(を継承したカスタムクラス)の場合、そのSceneObjectがaddSceneされてない状態では、managerはnullとなる。
つまりSceneObjectのコンストラクタに
trace(manager)
としてもnullが返ってくるだけ。
addSceneされたあとなら、Progressionインスタンスを返してくれる。

CastSprite(を継承したカスタムクラス)の場合は、自身の親が普通のspriteとかMovieClipの状態では参照できない。
親はCastSpriteとかCastMovieClipとかprogression独自のDisplayObjectContainerでないとだめ。

[2011.10.19追記]
とうぜんstageに追加した場合もmanagerはnullとなる

//nullの例
var sp:Sprite = new Sprite();
container.addChild(sp) //containerはprogressionの持ってるプロパティ。(progression的なstage)
var csp:CastSprite = new CastSprite();
sp.addChild(csp);
trace(csp.manager);//出力:null
//nullじゃない例
var sp:CastSprite = new CastSprite();
container.addChild(sp) //containerはprogressionの持ってるプロパティ。(progression的なstage)
var csp:CastSprite = new CastSprite();
sp.addChild(csp);
trace(csp.manager);//出力:[Progression id="index"]

というようなことが上手くまとめられてる記事もありました。
ありがたいことです。

参考:Fountain:Flash初心者がProgression 4 publicBeta1.1を始めたメモ

[AS]progression4 preloaderの表示オブジェクトコンテナー foreground/background

preloader.fla(プリローダー)に表示オブジェクトを追加する際は、foregroundもしくはbackgroundにaddChild()する。
CastPreloader.foreground
CastPreloader.background

foregroundは、メインコンテンツ(index.swf)よりも上の階層。
backgroundは、メインコンテンツ(index.swf)よりも下の階層

つまりコンテンツの背景として表示したいものはbackgroundに、
常に表示させたいものは、foregroundに、addChild()