Category Archive: ActionScript

[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

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

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

[FLASHCS3]ctrl+Enterしてもプレビューできない

そういう状況になったのでメモ。
クラスパス設定(ctrl+Shit+F12)で以下のように設定すると100%プレビューできなくなる。

classpath1

こっちならばOK

classpath2

要は空白の一行が入ってるとプレビューできない。
結構悩んだ。

[AS]progression DoTweenerでフィルター関係(スペシャルプロパテイl)を操作するときPropではスペシャルプロパティは操作できないっぽい。

DoTweenerはTweenerの拡張ですので、同じ手順でスペシャルプロパティが使えます。
今回はフィルター関係のTweenを使いたかったので、

//インポートして
import caurina.transitions.properties.FilterShortcuts;
//コンストラクタとかでイニシャライズ
FilterShortcuts.init();

通常のTweenerでやるときの作業でOK。
ここで一点つまづいたのは・・・

「おお、Progressionでもフィルター使えるんか!
えっとじゃあ、alpha初期化してるとこでフィルタの初期値もまとめて書いちゃうぜ!」

addCommand(
new Prop(this.hoge1_mc, { _Blur_blurX:firstBlurX,alpha:0} ),
new Prop(this.hoge2_mc, { _Blur_blurX:firstBlurX,alpha:0} )
)		

だめでした。
_Blur_blurXはTweenerのプロパテイなのでPropは認識しない様子。
というわけで、DoTweenerのtime:0で初期化したらOKでした。

addCommand(
new DoTweener(this.hoge1_mc, { _Blur_blurX:firstBlurX,alpha:0,time:0} ),
new DoTweener(this.hoge2_mc, { _Blur_blurX:firstBlurX,alpha:0,time:0} )
)		

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

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

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

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

[AS]if文での戻り値のないreturn

functionから抜ける

function init() {
	var a:Number=100;
	trace(a);//出力される:100
	if (a==100) {
		return;
	}
	trace(a);//出力されない
}
init();

[AS]MCにマスクをかけるときの順番。

いつも、どっちがどっちかわからなくなるのでメモ

まずAS3
ステージ上に、
インスタンス名:切り抜かれるMC(青い長方形)
インスタンス名:切り抜きの型となるMC(赤い三角)
二つのインスタンスを重ねた状態で、

切り抜かれるMC.mask = 切り抜きの型となるMC;

これで、青い三角ができあがる、と。

AS2だと

切り抜かれるMC.setMask(切り抜きの型となるMC);

切り抜かれるMCが常に最初と覚えておくことにしよう。

あとインスタンス名とかって全角の日本語でもOKなんですよねぇ。
AdobeMAXで城戸さんのセッションでやっててこういう使い方もありかと思いました。

[AS]SharedObjectについて

ユーザーのPCに変数情報等を保存できる。
Flash版のCokkieみたいなものらしい。
(そもそもCookieをよくわかっていないが)

何ができるかといったら、swfファイル間で同じ変数情報を共有可能。

使い方、owner.flaをつくりのそのフレームアクションに、

// 共有オブジェクトを作成する
var so:SharedObject =SharedObject.getLocal("MyData","/");
//containerBox0という箱を定義して値を設定
so.data.containerBox0="test0";
//ローカルにファイル(MyData.sol)を保存する。
var str = so.flush();

つづいて、user.flaを作ってそのフレームアクションに、

var so:SharedObject=null;
so=SharedObject.getLocal("MyData","/");
var tf:TextField = new TextField();
addChild(tf);
tf.text=String(so.data.containerBox0);

owner.flaをパブリッシュすると、MyData.solが保存される。
保存場所は上記スクリプトの場合は、
C:\Documents and Settings\(ユーザーID)\Application Data\Macromedia\Flash Player\#SharedObjects\NFWUQ76F\(ドメイン名)
もしくは
C:\Documents and Settings\(ユーザーID)\Application Data\Macromedia\Flash Player\(ドメイン名)
といった感じになるようだ。
参照サイト
ちなみにサーバーにあげないでローカルで試してるときは、(ドメイン名)はlocalhostになる。
ファイルができたことを確認して、user.flaをパブリッシュすると、text0が表示される。

ここで少し注意が必要でした。

owner→userの順でパブリッシュすると期待通りの動きですが、
user→owner→userの順でパブリッシュすると、MyData.solが削除されてしまいます。
理由はちゃんと理解してませんが。。。
多分先に作ったSharedObjectインスタンスに情報が入ってないから、なんちゃらかんちゃらな感じ。
なので情報が入ってるか入ってないかを判定して、入ってなかったら、その場でSharedObjectインスタンスを破棄するような処理が必要。

というわけでuser.flaを以下のように変更するとOKでした。

var so:SharedObject=null;
so=SharedObject.getLocal("MyData","/");
if (so.data.containerBox0==null) {
	so.clear();
	so=null;
} else {
	var tf:TextField = new TextField();
	addChild(tf);
	tf.text=String(so.data.containerBox0);
}

ファイル保存の際はエラー処理書いておかないとまずいですね。。。