[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");
}

[AS]イベントリスナー登録時にクロージャを使ってパラメータを渡すときの注意

通常マウスイベント等のリスナー関数には引数を持たすことができないが、クロージャ(←よくわかってないけど、関数の中に関数があるような状態のときみたい)を使うと、可能になります。
hakuhinさんの記事がわかりやすいと思います。
クロージャを使って関数にパラメータを保持する

このときに地雷があったのでご報告。

通常、こんな処理があったとします。
上の四角をクリックで、下のボタンにイベントを設定。
下のボタンをクリックすると上のボタンをクリックした回数が表示される。

//フレームアクション
//ボタン1となる四角を作成
var mc1:MovieClip = new MovieClip();
var mcg1:Graphics=mc1.graphics;
mcg1.beginFill(0xFFCC00);
mcg1.drawRect(0,0,100,50);
mcg1.endFill();
addChild(mc1);
mc1.buttonMode=true;

//ボタン2となる四角を作成
var mc2:MovieClip = new MovieClip();
var mcg2:Graphics=mc2.graphics;
mcg2.beginFill(0x88CC00);
mcg2.drawRect(0,0,100,50);
mcg2.endFill();
mc2.y=100;
addChild(mc2);
mc2.buttonMode=true;

//ボタン1クリック回数
var count:uint=0;


//ボタン位置のクリック動作
mc1.addEventListener(MouseEvent.CLICK, addMouseEvent);
function addMouseEvent(e:MouseEvent):void {
	count++;
	mc2.addEventListener(MouseEvent.CLICK,xClick);
	trace(count);
}

function xClick(e:MouseEvent):void {
	trace("count : "+count);
}

これをcountがローカル変数だったとして、クロージャ的に書くと

//フレームアクション
//ボタン1となる四角を作成
var mc1:MovieClip = new MovieClip();
var mcg1:Graphics = mc1.graphics
mcg1.beginFill(0xFFCC00);
mcg1.drawRect(0,0,100,50);
mcg1.endFill();
addChild(mc1);
mc1.buttonMode = true;

//ボタン2となる四角を作成
var mc2:MovieClip = new MovieClip();
var mcg2:Graphics = mc2.graphics
mcg2.beginFill(0x88CC00);
mcg2.drawRect(0,0,100,50);
mcg2.endFill();
mc2.y = 100;
addChild(mc2);
mc2.buttonMode = true;

//ボタン1クリック回数
var count:uint = 0;

//ボタン位置のクリック動作
mc1.addEventListener(MouseEvent.CLICK, addMouseEvent);
function addMouseEvent(e:MouseEvent):void{
	count ++;
	mc2.addEventListener(MouseEvent.CLICK,xClick(count));
	trace(count);
}

function xClick(mc1):Function {
	return function(e:MouseEvent):void {
		trace("count : "+count);
	}
}

実行するとわかるのですが、上の四角をクリックした回数だけ、下のボタンのイベントが重複します。
上の例ではどんだけ上の四角をクリックしても重複しなかったのですが、下の例では重複しまくります。

これ、気づくまでかなり時間かかりました・・・
まぁそもそもイベント登録が重複してしまう設計が悪いのでしょうけど・・・

ともかくクロージャってイベントリスナーに引数を持たせたいときはより厳密に設計しないとまずそうです。

[AS]FlashPlayer10ではTestFieldにアルファ処理可能、あとFlashPlayer9でのフォントの埋め込み方法の詳細

CS4のデフォルト書き出しはFP10です。
FP9まではダイナミックテキストにアルファ処理はフォント埋め込まないと無理でしたが、FP10からはフォント埋め込まなくてできるみたい。

//FP10用
var tf:TextField = new  TextField();
tf.text = "TEST";
addChild(tf);
tf.alpha = 0.1;

FP9での書き出しでは聞きません。
FP9の場合(フォント埋め込み後)

//FP9用
var tfm:TextFormat = new TextFormat();
tfm.font="HGSMinchoE";
var tf:TextField = new TextField();
tf.embedFonts=true;
tf.defaultTextFormat=tfm;
tf.text="あいうえお";
addChild(tf);
tf.alpha = 0.1

フォントの埋め込みはライブラリに埋め込んでないとだめみたいです。
ググるとtipsがたくさんありましたが、意外とはまったので書いときます。
ライブラリの右上のオプションボタンから「新しいフォント…」を選択
好きなフォントを選びます。
上の例では、HGS明朝Eをえらんでます。
リンケージのActionScript用に書き出しにチェック。
このときクラス名はなんでもいいです。(空手バカとかでもOK)
この埋め込んだフォントをASでどう設定するかというところで悩みました。
具体的には↓で設定してるのですが、

tfm.font="HGSMinchoE";

で、”HGSMinchoE”はなんぞや、という感じですが、これがHGS明朝Eのこと。

ボクはここを↓のようにやっていてできませんでした。

//だめな例
tfm.font="HGS明朝E";

ではHGS明朝E→HGSMinchoE、これをどうしらべるかですが。
trick7さんのとこの記事に調べ方がありました。
AS3 フォントリスト取得&確認用コード

これでライブラリ上で埋め込んであるフォントの名前がわかりました。

※2010/06/26追記
フォント名は文字列ではなく、FontクラスのfontNameプロパティを参照した方が安全。

//フォントにリンケージで指定したクラス名がMyFontだった場合
var myFont:Font = new MyFont();
var tfm:TextFormate = new TextFormat();
tfm.font = myFont.fontName;//これでフォント名が指定できる

[AS]TextFormatについて、setTextFormatの順番とか

setTextFormatはTextField.textにテキストを入れてから、実行する。
テキスト入れるまえでは意味がない感じ。

//テキストフォーマットを定義
var tfm:TextFormat = new TextFormat();
//行間の設定
tfm.leading=10;
//テキスト
var blogListTextArray:String="あんぽんたんあんぽんたんあんぽんたんあんぽんたんあんぽんたんあんぽんたんあんぽんたんあんぽんたん";
//テキストフィールドを定義
var tfCreator:TextField = new TextField();
//幅設定
tfCreator.width=50;
//折り返すよ、の設定
tfCreator.wordWrap=true;
//テキストフィールドにStringを入れる
tfCreator.htmlText=blogListTextArray;
//中身が入ってる状態で、フォーマット
tfCreator.setTextFormat(tfm);
//メインのタイムラインに追加
addChild(tfCreator);

順番が重要で、これじゃだめだった。

//先にフォーマット
tfCreator.setTextFormat(tfm);
//フォーマット後に、テキストフィールドにStringを入れる
tfCreator.htmlText=blogListTextArray;

[AS]入れ子状態にあるライブラリのMCにリンケージしたクラスファイルの読み込まれる順番

どういうことかというと。
メインのタイムラインにMC3というムービークリップ
MC3の中にはMC2があり、
MC2の中にはMC1があるという状態。

つまりステージからMC3をクリックしまくって、MC1に達したときの階層がこんな感じになってる状態。
シーン1→MC3→MC2→MC1

この状態で、
メインのタイムライムラインに
trace(“main”);
MC1のタイムラインに
trace(“1”);
MC2のタイムラインに
trace(“2”);
MC3のタイムラインに
trace(“3”);

これでプレビューすると出力結果は、
main
3
2
1

ステージに近い方から実行されていくというのはなんとなく予想通り。

ここで、各MCにクラスファイルをリンケージしてみる。
(全て1フレーム目に書き出しの設定)
MC1にはTest1.as
MC2にはTest2.as
MC3にはTest3.as
それぞれクラス名をトレースするだけの内容が書いてある。

例)Test1.as

package  {	
	import flash.display.MovieClip;
	
	public class Test1 extends MovieClip{
		
		public function Test1() {
			trace("Test1");
		}
	}
}

この状態で実行するこうなりました。
Test1
Test2
Test3
main
3
2
1

入れ子の一番深いリンケージクラスファイルが一番初めに実行、どんどん上に上がっていって、リンケージしたクラスファイルが終わったら、フレームアクションをステージ側から実行していって、一番深いところを目指す。

予想と違ったのでなんか驚いた。

[AS]progression ページタイトルについて。

progressionのページタイトルについて。
ブラウザの上に表示される、アレですね。
SceneObjectのプロパティtitleに設定することでブラウザに反映されるとあります。
だがしかし読み込み直後はこれが表示されない。
なんか別のタイトルが表示されてます。

なんだろう、index.asとかになんか書いてあるのか?といろいろ調べまわり・・・
グーグル先生に聞いてみても質問が悪いのか教えてくれず・・・
Twitterでえらい人の一言で解決しました。
index.htmlのtitle属性の値でした。。。

最近SWFファイルしか作ってないから何だか根本的にダメダメだ。。。