[AS]外部swfをLoaderで読み込んだ後にunload()で消すときは子swfの中身も綺麗に消す

外部SWFを読み込んで、それを破棄するときはunloadして、removeChildしてnullにするのが良いらしい。
親swfにはこんな感じでかく。

loader.unload()
removeChild(loader);
loader = null();

親swfで子swfをunload()するとに子swfはaddEventListenerでイベントを受けとれる。
子swfでこんな感じ

this.loaderInfo.addEventListener(Event.UNLOAD , unloadHandler);

そこで子swfで設定した参照なり、イベントは破棄してあげないといけない。

例)
ステージにm0_mc、m1_mcというインスタンス名のMCを二個配置。
m0_mcクリックでm子swfの読み込み、m1_mcクリックで子swfを破棄
親swfのドキュメントクラス

package {
	//親swfのドキュメントクラス
		import flash.display.Loader;
		import flash.net.URLRequest;
		import flash.display.LoaderInfo;
		import flash.events.Event;
		import flash.events.MouseEvent;
		import flash.display.MovieClip;
		
		
	public class MainP extends MovieClip {

		var loader:Loader

		public function MainP() {
			m0_mc.addEventListener(MouseEvent.CLICK , movieOn);
			m1_mc.addEventListener(MouseEvent.CLICK , movieOut);
		}
		private function movieOn(e:MouseEvent):void {
			trace("movieOn");
			//子SWFの読み込み
			loader= new Loader();
			loader.contentLoaderInfo.addEventListener(Event.COMPLETE,xComplete);
			var urlReq:URLRequest = new URLRequest("child.swf");
			loader.load(urlReq);
		}
		private function movieOut(e:MouseEvent):void {
			loader.unload();
			removeChild(loader);
			loader = null;
		}
		
		public function xComplete(e:Event):void {
			addChild(loader);
		}
	}
}

子swfのドキュメントクラス

package  {
	//子swfのドキュメントクラス
	import flash.display.MovieClip;
	import flash.events.Event;
	
	public class Child extends MovieClip {
		
		public function Child() {
		trace("child");
		this.addEventListener(Event.ADDED_TO_STAGE, addc);
		this.addEventListener(Event.REMOVED_FROM_STAGE, removec);
		this.loaderInfo.addEventListener(Event.UNLOAD, unloadc);
		}
		function addc (e:Event):void {
			trace("child add");
		}
		function removec (e:Event):void {
			trace("child remove");
		}
		function unloadc (e:Event):void {
			trace("child unload");
		}
	}
}

[AS] 外部SWFファイルの読み込みで、子swf側で動的に配置したmcが表示されない。

すげー悩んだのでメモ。
親SWF(parent.swf)で、ドキュメントクラス Main.asを設定して、外部swfを読む処理はこんな感じ

//ドキュメントクラス
package {
		import flash.display.Loader;
		import flash.net.URLRequest;
		import flash.display.LoaderInfo;
		import flash.events.Event;
		import flash.display.MovieClip;
		
	//親ファイルのドキュメントクラス
	public class Main extends MovieClip {
		var loader:Loader
		private var loaderRoot:Object;

		public function Main() {
			loader= new Loader();
			loader.contentLoaderInfo.addEventListener(Event.COMPLETE,xComplete);
			var urlReq:URLRequest = new URLRequest("test.swf");
			loader.load(urlReq);
		}
		public function xComplete(e:Event):void {
			addChild(loader);
		}
	}
}

子swf(test.swf)はこんな感じで、動的に何かを表示させてみる。
(ステージには他にシェイプとか文字とかおいておく。)

package  {
//ドキュメントクラス
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import  flash.text.TextField;
	import flash.events.MouseEvent;

	public class Main extends Sprite {
		public function Main() {
			var sp:Sprite = new Sprite()
			sp.graphics.beginFill(0xFFCC00);
			sp.graphics.drawRect(0, 0, 100, 100);
			sp.graphics.endFill();
			addChild(sp);
		}
	}
}

一見平気そうにみえるけど、これでparent.swfを開くとtest.swfで定義した四角が表示されてない・・・
test.swf単体で開けば四角は表示されるのに、parent.swfを開くと表示されない。
Flash上で、ステージにおいたものは表示されているので、swfを読み込めてないわけではない。

なんだこりゃー!!!!!と思ったら・・・

読み込み側のswfのドキュメントクラス名と、読み込まれる側のドキュメントクラス名が同じだとまずいようす。
(今回は両方とも、Main.asとなっている)
というわけで、どちらかを変えてあげればOK。
子swf側を変えるのであればこんな感じ。

package  {
//MainからTestMainに変更
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import  flash.text.TextField;
	import flash.events.MouseEvent;

	public class TestMain extends Sprite {
		public function TestMain() {
			var sp:Sprite = new Sprite()
			sp.graphics.beginFill(0xFFCC00);
			sp.graphics.drawRect(0, 0, 100, 100);
			sp.graphics.endFill();
			addChild(sp);
		}
	}
}

もちろんflaファイルの設定も変えなきゃね。

ちなみに、ドキュメントクラス名が一緒の状態で、子swf側でステージ上のインスタンス名を取得しようと以下のようなエラーがでました。

//ステージに_mcというインスタンス名のMCがある状態で。
package  {
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import  flash.text.TextField;
	import flash.events.MouseEvent;

	public class Main extends Sprite {
		public function Main() {
			
			var sp:Sprite = new Sprite()
			sp.graphics.beginFill(0xFFCC00);
			sp.graphics.drawRect(0, 0, 100, 100);
			sp.graphics.endFill();
			addChild(sp);
             //これはエラーになる。
			var mc:MovieClip = MovieClip(this.getChildByName("_mc"));
		}
	}
}

ReferenceError: Error #1056: Main のプロパティ _mc を作成できません。
at flash.display::Sprite/constructChildren()
at flash.display::Sprite()
at flash.display::MovieClip()
at Main()

[AS]progression DoTweenerをinterrupt()するのは大変かも。

SerialListとかのコマンドリスト内にもかけて、とっても便利なDoTweener。
ただし、そのコマンドリストを停止させるときはちょっと注意が必要かも。
具体的には、DoTweenerの含まれているコマンドリストをinterrupt()するとDoTweenerでエラーが吐かれます。
一応、その場で止まってるみたいですが、内部を読めない素人にはエラーという言葉が怖い・・・

というわけで・・・
interrupt()するコマンドにはTweenerを利用するという方法に逃げました。
これだとinterrupt()してもとりあえずエラーは表示されません。
(もちろん内部的に何かエラーが発生してるかはわからんですが・・・)

[AS]progression Sceneの_onLoad中にコマンドをinterrupt()すると良くない?

ちょっとはまったのでメモ

progressionのコマンド機能を使ってオープニングムービー付きサイトを作りました。
で、当然ムービーはスキップできるようにしたいな、と。

コマンドを停止させるにはinterupt()という命令でOK。
ただ停止したいコマンドを指定せねばなので、_onLoad()内のaddCommand内に書いてたコマンドを、別でSerialListを作ってそこにaddCommand、それを_onLoad()のaddCommandにinsertCommand()する感じにしました。
↓だいたいこんなかんじ
(indexSceneに書いたってことで話を進めます)

protected override function _onLoad():void {
	var sList:SerialList = new SerialList();
	sList.addCommand(
		new Trace("insertCommand sList")
	)
	addCommand(
		insertCommand(sList)
	);
}

で、skipボタン用意して押したら、sList.interrupt()を実行して、その後はスキップ後の処理を実行するような感じに。

結果としては、止まった。
でも、通常_onLoad()後に行われる_onInit()は実行されず。
強制ストップだからそういうもんなのかな。
で、ここで問題が。
indexSceneから移動しても、_onGotoイベント、_onUnLoadイベントが発行されないご様子。
_onInit()が実行されてないとだめなのか?と思ったので・・・
スキップしたいコマンドをonInit内に記述して、onInit()のコマンドをinterruptしてみると、

//IndexSceneoの_onInitに書いてます。
protected override function _onInit():void {
	var sList:SerialList = new SerialList();
	sList.addCommand(
		new Trace("insertCommand sList")
	)
	addCommand(
		insertCommand(sList)
	);
}

こんどはシーン移動の際に_onGotoイベントが問題なく発行されました。

_onLoad中でのコマンドのinterrupt()には注意が必要かもね。

[AS]progression Progressionインスタンスの取得方法

下の記事でCastSpriteからprogressionインスタンスを取得するのに、indexSceneを取得して、そのprogressionプロパティから取得しましたが、もっと簡単にできました。

trace("プログレッションid" + getProgressionById("index"))

“index”というidは、index.asのここで定義されてます。

// Progression インスタンスを作成します。
prog = new Progression( "index", stage, IndexScene );

[AS]progression コマンドの順番についてのメモ

ちょっと混乱したので現象をメモしておこう。

まずはソースを、
indexSceneの_onInitに書いてます。

protected override function _onInit():void {
	// 実行したいコマンドを登録する
	var sList0:SerialList = new SerialList();
	sList0.addCommand(
		function() {
			trace("sList0");
		}
	);
	var sList1:SerialList = new SerialList();
	sList1.addCommand(
	function() {
			trace("sList1");
	}
	);
	var sList2:SerialList = new SerialList();
	sList2.addCommand(
	function() {
			trace("sList2");
	}
	);
	var sList3:SerialList = new SerialList();
	sList3.addCommand(
	function() {
			trace("sList3");
	}
	);
	var sList4:SerialList = new SerialList();
	sList4.addCommand(
	function() {
			trace("sList4");
	}
	);
	var sList5:SerialList = new SerialList();
	sList5.addCommand(
	function() {
			trace("sList5");
	}
	);
	
	//
	addCommand(
		function() {
			trace("defolt addCommand Start");
		},
		addCommand(sList0),
		function() {
			insertCommand(sList2);
		},
		addCommand(sList1),
		function() {
			trace("defolt addCommand End");
		},
		insertCommand(sList4)
	);
	//
	insertCommand(sList5);
	insertCommand(sList3);
	
}

で、traceされる結果は、
sList3
sList5
sList4
sList0
sList1
defolt addCommand Start
sList2
defolt addCommand End

こんな順番。

気づいた点は、
_onInit()内に、insertCommand()をいくらでも書けるってこと。
そのinsertCommandはexecute()不要で、順番的には、addCommandよりも先になる。
さらに一番最後にinsertCommandしたものが実質一番最初(addCommandよりも先)に実行される、みたいね。

では、addCommand内でinsertCommandした場合はどうなるか。
これは少し注意が必要ね。
直接、insertCommandした場合は、addCommand内で一番最初に実行される。
でもfunction内でinsertCommandした場合は、既存のaddCommandの順番内に純粋に挿入される感じ。

[AS]progression 出発シーン、到着シーンのsceneIdを取得する方法”departedSceneId”,”destinedSceneId”

便利すぎて鼻血がでます。

prgressionで直接URLを入力されたときや、サイトマップなどから通常の遷移とは違うルートからシーンにたどり着いた場合、内部の処理(主に、通過するシーンの_onLoadイベント)を分岐させたいことがありました。

その際に便利なのが、出発シーン、到着シーンを取得できるプロパテイ。
出発シーン:departedSceneId
到着シーン:destinedSceneId

使い方は、SceneObjectを継承したクラスならこんな感じで取得できます。

trace("出発地:"+this.progression.departedSceneId);
trace("ゴール地:" + this.progression.destinedSceneId);

progressionインスタンスのプロパティってことですかね。
CastSpriteとかで取得したい場合は、まずprogressionインスタンスを取得しないとなので、こんな感じで取れました。(もっと簡単に取れそうだけど)

//まずは何かシーンインスタンスを取得しておいて、
var indexScene:IndexScene  = IndexScene(getSceneById("indexScene"));
//そのインスタンス経由でprogressionを取得する
trace(indexScene.progression.departedSceneId)
trace(indexScene.progression.destinedSceneId)

ちなみに(一番最初に)URLからアクセスした場合は、departedSceneIdはnullになるみたい。
リロードしたときも、departedSceneIdはnullになってました。
これでいろんな状況からのアクセスの条件分岐ができそう。

※追記、
もっと簡単にprogressionインスタンス取得できました。

//progressionインスタンスを取得
var prog:Progression  = getProgressionById("index");
//そのインスタンス経由でprogressionを取得する
trace(prog.departedSceneId)
trace(prog.destinedSceneId)

[AS]progression AddChildAt()でインデックスが境界外と怒られる場合

progressionのAddChidAt、順番無視して突っ込めるので便利ですが、たまに順番どおりにインデックス打たないと境界外エラーがでてました。
なんだこりゃと思った、普通のAddChildで逃げてたわけですが、理由がやっとわかったっぽい。

コンテナをSpriteじゃなくてCastSpriteにしてあげたらエラー無くなった

		public function Hoge( initObject:Object = null ) 
		{
			super( initObject );
			//メニュー用コンテナーを作る
			MenuContainer = new CastSprite();
			//MenuContainer = new Sprite();←Spriteだとだめっぽい
			
			//メニューを作る
			Menu00 = new Menu00();
			Menu01 = new Menu00();
			Menu02 = new Menu00();
			Menu03 = new Menu00();
			Menu04 = new Menu00();
		}
		protected override function _onCastAdded():void 
		{
			addCommand(
				//メニューをコンテナーに格納
				new AddChildAt(companyMenuContainer, Menu00, 10),
				new AddChildAt(companyMenuContainer, Menu01, 11),
				new AddChildAt(companyMenuContainer, Menu02, 2),
				new AddChildAt(companyMenuContainer, Menu03, 23),
				new AddChildAt(companyMenuContainer, Menu04, 44),
			);
		}

[AS]TextFieldの右寄せ設定の際の注意

テキストフィールドで右寄せにする際は、幅とx座標の順番に注意が必要。
イマイチ関係性がわかんないけど。
一番上が意図した結果に違い

var _txt:TextField = new TextField();
_txt.htmlText="あいうえお";
_txt.width=550;
_txt.x = 100;
_txt.autoSize="right";//x座標も幅も設定した後
addChild(_txt);

var _txt0:TextField = new TextField();
_txt0.htmlText="あいうえお";
_txt0.autoSize="right";//x座標も幅も設定前
_txt0.width=550;
_txt0.x =100;
addChild(_txt0);

_txt0.y = 20;

var _txt1:TextField = new TextField();
_txt1.htmlText="あいうえお";
_txt1.x =100;
_txt1.autoSize="right";//幅の設定前
_txt1.width=550;

addChild(_txt1);
_txt1.y = 40;

var _txt2:TextField = new TextField();
_txt2.width=550;
_txt2.autoSize="right";
_txt2.x = 100;
_txt2.htmlText="あいうえお";//最後に文字設定

addChild(_txt2);
_txt2.y = 80;

結果はこんな感じ

txt1

[AS]ボタンを一時的にクリックさせたくないとき

InteractiveObject.mouseEnabled が便利。
MCのデフォルトがtrueなので、イベントリスナーを設定してあるMCでクリックさせたくないときにMovieClip.mouseEnabled = false;でマウス反応を向こうにできる。
buttonModeの効果も切れる。

サンプル
上のボタンを奇数回クリックしてるときは下のボタンが利かない。

//フレームアクション
var m0_mc:MovieClip = new MovieClip();
m0_mc.graphics.beginFill(0xFF0000);
m0_mc.graphics.drawRect(0,0,100,50);
addChild(m0_mc);
m0_mc.buttonMode=true;

var m1_mc:MovieClip = new MovieClip();
m1_mc.graphics.beginFill(0xFFCC00);
m1_mc.graphics.drawRect(0,0,100,50);
m1_mc.y=100;
addChild(m1_mc);
m1_mc.buttonMode=true;

var count:Number=0;

m0_mc.addEventListener(MouseEvent.CLICK, xClick0);
m1_mc.addEventListener(MouseEvent.CLICK, xClick1);

function xClick0(e):void {
	count++;
	if (count%2) {
		trace("false");
		m1_mc.mouseEnabled=false;
	} else {
		trace("true");
		m1_mc.mouseEnabled=true;
	}
}


function xClick1(e):void {
	trace("click");
}