[AS]ActionScript3.0のコーディングスタイルの規約(FlexSDKにおける規約)

Adobe推奨が以下のリンク
Flex SDKコード記述に関する規則とベストプラクティス

あくまでFlexSDK用っぽいので、FlashIDEがこれに準拠するかといったら微妙っぽい。
たとえば、{で改行をどこにいれるかという点では、
FlashIDEで、function生成のショートカットキーESC+f+nで生成されるコードは、


function  () {
    //処理
}

規約では、{を整列させると書かれている


function  ()
{
    //処理
}

とはいれ、それ以外の部分は大体真似していいんじゃないかと。

◆気になった部分だけmemo
・new Array() → []
・new Object() → {}
・if(flag == true) → if(flag) (flag:Boolean,Object)
・as演算子よりもキャストを使う
・単純な条件はifよりも三項演算子を使う (ifより遅くなるんじゃないの?)
・if文が(全ての項目のステートメントが)一項目のときはブロック{}を書かない
・for、while、do文では必ずブロック{}を書く
・protected override → override protected
・static public → pubilc static
・定数は全てstaticとする
・変数を初期化するときはコンストラクタではなく宣言時にする。

?なところもあるけど一通り目を通しておいてもよさげ。

[AS][オレオレライブラリ]シフトキー(ctrlキー)を押してる、押してないを判定してくれるクラス

製作の上であったら便利かな、と思ったのでクラス化。

◆動機
たとえば、あるボタンを押したら外部読み込み開始するとしたら、それが失敗したときなどを考慮してにエラー処理を実装しますよね。
この実装のチェックとしては、わざとエラーが起こる状態を再現してテストしなくちゃいけない。
たとえば、読み込むべきURLが外部xmlに記述してあって、それが存在しないURLだった場合のエラー処理だったら、わざとxmlに記述されたURLを間違えたものに書き換えてチェックとか。
というようなことを以前はやってました。
が、いちいちファイル書き換えてチェックするのが面倒。

そこで、
もしshiftキーを押しながらクリックしたら、存在しないURLを読みにいくようなロジックを最初から組んどけばテスト簡単じゃね?
お客さんにエラー時の動作処理をみてもらうときも、お客さんの負担減るんじゃね?

ってことで、shiftキーとctrlキーが押されてるかどうかを判定するクラスを作りました。
Singletonで作ってあるので、最初に初期化しちゃえば、どこからでも参照可能。
消すときも、Singletonなのでクラス名で検索書ければどこで使ってるかが一目瞭然(のはず)。

使い方は以下のような感じ


//準備
//ステージに追加、これだけでOK
addChild(AddKeyDownUtil.getInstance());

//使い方、以下のプロパティを参照して、true/falseを取得できる
//シフトキーが押されてたら、true,押されてなかったらflaseを返す
SingletonManager.getInstance().isShiftDown;//戻り値:Boolean
//コントロールキーが押されてたら、true,押されてなかったらflaseを返す
SingletonManager.getInstance().isCtrlDown;//戻り値:Boolean

//使用例)
//_btnがクリックされたときにシフトキーが押されているかいないかで処理を分岐
_btn.addEventListener(MouseEvent.CLICK, clickHandler);
private function clickHandler(e:MouseEvent):void {
    if(AddKeyDownUtil.getInstance().isShiftDown){
        //シフトキーが押されていた場合に実行したい処理
    }else{
        //シフトキーが押されていない場合に実行したい処理
    }
}

クラスファイルは以下の通り


package bz393.util {
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.ui.Keyboard;

	/**
	 * ...
	 * @author 393
	 */
	public class AddKeyDownUtil extends Sprite{

		public static var instance:AddKeyDownUtil;
		public var isShiftDown:Boolean = false;
		public var isCtrlDown:Boolean = false;

		public function AddKeyDownUtil(enforcer:SingletonEnforcer) { };

		public static function getInstance():AddKeyDownUtil {
			if (!AddKeyDownUtil.instance) {
				AddKeyDownUtil.instance = new AddKeyDownUtil(new SingletonEnforcer());
				AddKeyDownUtil.instance.addEventListener(Event.ADDED_TO_STAGE,init);
			}
			return AddKeyDownUtil.instance;
		}

		static private function init(e:Event):void {
			AddKeyDownUtil.instance.removeEventListener(Event.ADDED_TO_STAGE, init);

			AddKeyDownUtil.getInstance().stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHander);
			AddKeyDownUtil.getInstance().stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHander);
		}

		static private function keyDownHander(e:KeyboardEvent):void {
			switch (e.keyCode) {
				case Keyboard.SHIFT:
				AddKeyDownUtil.instance.isShiftDown = true;
				break;
				case Keyboard.CONTROL:
				AddKeyDownUtil.instance.isCtrlDown = true;
				break;
			}
		}

		static private function keyUpHander(e:KeyboardEvent):void {
			switch (e.keyCode) {
				case Keyboard.SHIFT:
				AddKeyDownUtil.instance.isShiftDown = false;
				break;
				case Keyboard.CONTROL:
				AddKeyDownUtil.instance.isCtrlDown = false;
				break;
			}
		}
	}

}
class SingletonEnforcer {
};

[AS]StageQuality.MEDIUM以下の状態で日本語フォントを埋め込んだTextFieldの3Dプロパティを有効にすると拡大される。(ただしムービープレビューのときのみ)

あまり意味のない情報だけど数時間ハマったのメモしとく。

まず結論から書くと、これはムービープレビューのときのみ発生(FlashCS4で確認)
ムービープレビューのときに立ち上がるFlashPlayerはブラウザに埋め込まれてるプレイヤーとは異なるもの。
まれにおかしな挙動をすることがあるのだけど、今回もその類のバグだったわけ。
なので、実際にブラウザ上で確認すれば題記のような現象にはならない。

一応再現手順を書いておく。
1.flaファイルに日本語フォントを埋め込む。(小塚ゴシックProBとする)
2.ASにて、動的にTextFieldを配置(フォントサイズを16とかに)
3.TextFieldに埋め込んだフォントを設定
4.stage.qualityをMedium以下に設定
5.TextFieldの3Dプロパティを変更(z=0とか)
6.ctrl+enterでムービープレビュー
7.指定したフォントサイズよりもでかく表示される

※stage.quailtyをHIGH以上にすれば上記現象はおきません。

まぁ実害はないけど遭遇したら一瞬焦るので一応覚えておいたほうがいいかも。

[AS]FlashCS4、CS5でフォント埋め込みの際、フォント名の指定には要注意(フォントスタイル違いで日本語/ローマ字が違うケース有)

すげー、はまったのでメモ。

↓の問題でした。
[AS]フォント名を指定するときの注意(日本語?英語?)
(今、このを記事書いてるときに上のエントリーに気付いた。アホすぎる。。。)

上の記事では、FlashIDEとFlexSDKでフォント名が異なる?という内容だけど、
FlashIDEでもバージョンによってはフォント名が異なることが発覚。
※windows/mac、Flashの日本語版/英語版で結果が異なるかも
※今回検証したのはwindow、日本語版

例1)小塚ゴシックPro レギュラーの場合
CS3:小塚ゴシック Pro R (日本語で指定)
CS4:Kozuka Gothic Pro R (ローマ字で指定)
CS5:Kozuka Gothic Pro R (ローマ字で指定)

CS3は日本語で指定だったけど、CS4以降はローマ字で指定。

・ちなみにボールドの場合はまた違った感じに。。。
例2)小塚ゴシックPro ボールドの場合
CS3:小塚ゴシック Pro B (日本語で指定)
CS4::Kozuka Gothic Pro B (ローマ字で指定)
CS5:小塚ゴシック Pro B (日本語で指定)

もちろん、日本語で書くべきところをローマ字で書いたりしたら、フォントは指定されない。
同じフォントなのにスタイル違いで、書き方変わるなんてそんなのわからんよ。。。

というわけでフォント名は直接文字列で書くのではなく、FontクラスのfontNameプロパティを使った方が間違いがおきなそう。


//ライブラリにフォントを追加
//ASに書き出しにチェック
//クラス名を指定 (仮にMyFontとする)
var myFont:Font = new MyFont();
myFont.fontName;//フォント名

var tfm:TextFormat = new TextFormat();
tfm.font = myFont.fontName;//文字列で指定しない。

[FlashIDE]ダイナミックテキストへのフォント指定について、CS4とCS5では挙動が変わったみたい

ダイナミックテキストへのフォント指定での挙動について。
CS5から挙動が変わってることに気付いたのでメモ。

◆手順
1.FlashIDEにて、ステージにダイナミックテキストを置く。
2.ダイナミックテキストに何か文字(ここでは”あいうえお”とする)を入力。
3.ダイナミックテキストに任意のフォント(ここではA-OTF 新ゴ Pro Bとする)を指定。
(ここでフォント:A-OTF 新ゴ Pro Bは埋め込まない)
4.パブリッシュ
5.フォント(A-OTF 新ゴ Pro B)が入っていないPCで表示を確認。

◆結果
・CS4でパブリッシュした場合
→文字が、デバイスフォントで表示される。
・CS5でパブリッシュした場合
→文字が、指定フォント(A-OTF 新ゴ Pro B )で表示される。

つまり、CS4ではフォントの埋め込みは行われないが、CS5ではフォントが埋め込まれている。
またここでは、入力した文字(あいうえお)のみが埋め込まれているので、動的にtextを変更しようとしても使える文字は”あいうえお”だけとなる。

というわけで、これをCS4でも同様の挙動をさせるには、

1.ダイナミックテキストを選択
2.プロパティパネルの「文字の埋め込み」をクリック
3.「自動読み込み」ボタンをクリック。
→ダイナミックテキストの文字列(ここでは”あいうえお”)が埋め込み対象文字となる。
4.「OK」をクリック
これでOK。

つまりCS5からは、上記の埋め込み作業を自動でやってくれるようになったようだ。
嬉しいような、こんがらがるような。。。

[AS]mouseEnabled=falseで下の階層にあるボタンを有効にするときの注意。

感覚的に勘違いをしていたのでメモしておく。

言葉で書いてもうまく伝えられないので、まずは以下フレームアクションのスクリプト参照。


import flash.display.Sprite;

//1階層目
//コンテナ的sp(塗り無し)
var container01:Sprite = new Sprite();

//コンテナの背景的なもの(赤塗り)
var bg01:Sprite = new Sprite();
bg01.graphics.beginFill(0xFF0000);
bg01.graphics.drawRect(0,0,300,300);
bg01.graphics.endFill();

//ボタン的なもの
var btn01:Sprite = new Sprite();
btn01.graphics.beginFill(0x660000);
btn01.graphics.drawRect(40,40,40,40);
btn01.graphics.endFill();
btn01.buttonMode = true;

//コンテナ的なものをメインのタイムラインに配置
this.addChild(container01);
//背景的なものをコンテナに配置
container01.addChild(bg01);
//ボタン的なものをコンテナに配置
container01.addChild(btn01);

//////////////////////////////////////

//2階層目
//コンテナ的sp(塗り無し)
var container02:Sprite = new Sprite();
//1階層目が見えるように半透明にして配置をずらす
container02.alpha = .5;
container02.x = container02.y = 20;

//コンテナの背景的なもの(青塗り)
var bg02:Sprite = new Sprite();
bg02.graphics.beginFill(0x0000FF);
bg02.graphics.drawRect(0,0,300,300);
bg02.graphics.endFill();

//ボタン的なもの
var btn02:Sprite = new Sprite();
btn02.graphics.beginFill(0x000066);
btn02.graphics.drawRect(40,40,40,40);
btn02.graphics.endFill();
btn02.buttonMode = true;

//コンテナ的なものをメインのタイムラインに配置
this.addChild(container02);
//背景的なものをコンテナに配置
container02.addChild(bg02);
//ボタン的なものをコンテナに配置
container02.addChild(btn02);

//1階層目のボタンを押せるようにするには、
//2階層目の背景的なものをmouseEnabled = false
bg02.mouseEnabled = false;
//さらに、コンテナ的なspもmouseEnabled = false;
//にしないとだめ
container02.mouseEnabled = false;

はまったポイントは2階層目のコンテナ自身に対してもmouseEnable=falseを設定してあげないといけないということ。
2階層目のコンテナ自身は塗りを持ってないから、(塗りの実体である)背景だけをmouseEnable=falseにすればいいんじゃね、と思い込んでおりましたが、それだと1階層目のボタンに触れません。
コンテナ自身もmouseEnabled=falseにしてやらないとだめだったのです。
感覚的に、コンテナをmouseEnabled=falseにしちゃったら、コンテナが内包しているボタンもmouseEnabled=false扱いとなってしまうかと思ったら、そんなことは無いというオチ。
mouseChildrenってプロパティもあるし、それぞれの機能をちゃんと理解しとかないとマズイです。。

[AS]リスナー関数の弱参照について

避けて通ってきたところを理解できた(気がする)のでメモ。

リスナー関数を弱参照にする場合は、addEvenListener()の第5引数をtrueとする。


//例
//第5引数のデフォルト値はfalse
this.addEventListener(Event.ENTER_FRAME, this.enterFrameHandler,false,0,true);

trueを設定することでリスナー関数は弱参照となりガベージコレクション(GC)の対象となる。
※弱参照とGCについては偉い人のサイトがわかりやすいです。
イベントリスナ (AS3) とガーベジコレクション:akihiro kamijo
Tweenのアニメーションが途中で止まる:FumioNonaka.com

はまったのはここで弱参照となるリスナー関数の状態。
どういうことかというと・・・
以下の処理ではリスナー関数が弱参照でGCが発動した際にenterframe処理は停止。
これは認識どおり。


//フレームアクション
//GCが発動するとhogeの出力が止まる。
this.addEventListener(Event.ENTER_FRAME, function(e:Event):void{trace("hoge");},false,0,true);
//※もちろん、第五引数をfalseにした場合はenterframe処理はGCが発動しても動き続ける。

でも以下のように書いた場合は、第五引数をtrueにしてあってもenterframe処理はGCが発動しても動き続ける。


//フレームアクション
//GCが発動してもhogeは出力され続ける。
this.addEventListener(Event.ENTER_FRAME, this.enterFrameHandler,false,0,true);
function enterFrameHandler(e:Event):void{
trace("hoge");
}

なんで???参照きれて破棄されるんじゃないのと思ったら・・・
リスナー関数:enterFrameHandlerの自身の参照がthisに残っているから破棄されないということらしい。

なのでこう書けば破棄される。


//フレームアクション
//GCが発動でhogeの出力は止まる。
function init(){
this.addEventListener(Event.ENTER_FRAME, this.enterFrameHandler,false,0,true);
//関数init内にenterFrameHandlerを定義することでローカル関数となる。
function enterFrameHandler(e:Event):void{
trace("hoge");
}
}
init();

ちなみにこれではだめ。


//フレームアクション
//GCが発動でもhogeの出力が止まらない。
function init(){
this.addEventListener(Event.ENTER_FRAME, this.enterFrameHandler,false,0,true);
}
//関数initの外にenterFrameHandlerを定義することで関数自身の参照が保持される。
function enterFrameHandler(e:Event):void{
trace("hoge");
}
init();

ようするに。。。
第五引数をtrueにしても、弱参照になるのは、addEventLisner()とリスナー関数の関係性、と。
リスナー関数自身の参照が残ってる場合はtrueにしてもそれが残って破棄されない、と。
なんとなく、わかったような。。。
リスナー関数自身の参照も残らないようにしないといけないということらしい。

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

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

[AS]BetweenAS3のグループ化は、配列を使っても管理可能。  

BetweenAS3でのグループ化について。
ITween.serial()、ITween.parallel()メソッドを使う場合は


//ステージ上に_mc1と_mc2のMovieClipが配置してあるとしてフレームアクション
import org.libspark.betweenas3.BetweenAS3;
import org.libspark.betweenas3.tweens.ITween;

var iTween1:ITween =  BetweenAS3.to( _mc1 , {x:100},1);
var iTween2:ITween =  BetweenAS3.to( _mc2 , {x:100},1);
var iTween:ITween =  BetweenAS3.parallel(iTween1, iTween2);
iTween.play();

ITweenインスタンスをfor文とかで一括で作りたいときは配列で管理。
その際は、parallelTweens()メソッド、serialTweens()メソッドを使う
これは便利!!!


//ステージ上に_mc0から_mc9の10個MovieClipが配置してあるとしてフレームアクション
import org.libspark.betweenas3.BetweenAS3;
import org.libspark.betweenas3.tweens.ITween;

var iTweens:Array = [];
for(var i:int = 0; i < 10; i++){
var name:String = "_mc" + i;
iTweens.push(BetweenAS3.to( getChildByName(name) , {x:100},1));
}
var iTween:ITween =  BetweenAS3.parallelTweens(iTweens);
iTween.play();

[AS]as3でのスクリプトの実行の順番をmemo

swfを開いたときに実行される順番がこんがらがることが多いので調べた。

まずはドキュメントクラスのコンストラクタとメインのタイムラインのフレームアクションの比較

  • 1.ドキュメントクラスのコンストラクタ。
  • 2.メインタイムラインのフレームアクション

これは想像通り。

注意が必要なのはリンケージしたMCをnewするとき、(ドキュメントクラスの)コンストラクタでnewするか、(メインのタイムラインの)フレームアクションでnewするかというとき。
※前提として、メインのタイムラインにはMc(Mc0_tl)が配置してあり、Mc0_tlには、さらにMc(Mc1_tl)が入れ子で配置してることとする。

まず、ドキュメントクラスのコンストラクタでnewする場合

  • 1.DocumentClass コンストラクタ開始
  • 2.DocumentClass ムービークリップを動的に生成(new MyMc0_as())
  • 3.▲MyMc0_as コンストラクタ開始
  • 4.▲MyMc0_as コンストラクタ終了
  • 5.DocumentClass ムービークリップを動的に生成終了
  • 6.DocumentClass コンストラクタ終了
  • 7.★MyMc0_as フレームアクション開始
  • 8.★MyMc0_as フレームアクション終了
  • 9.mainTimeLine フレームアクション開始
  • 10.mainTimeLine フレームアクション終了
  • 11.MainTimeLineに配置した Mc0_tl フレームアクション開始
  • 12.MainTimeLineに配置した Mc0_tl フレームアクション終了
  • 13.Mc0_tlに配置した Mc1_tl フレームアクション開始
  • 14.Mc0_tlに配置した Mc1_tl フレームアクション終了

動的にnewしたMyMc0_asのフレームアクションが開始されるのは、ドキュメントクラスのコンストラクタ終了直後。
(メインタイムラインのフレームアクションの前)

次にメインタイムラインのフレームアクションでnewする場合(1フレーム目に記載)

  • 1.mainTimeLine フレームアクション開始
  • 2.mainTimeLine ムービークリップを動的に生成(new MyMc0_as)
  • 3.▲MyMc0_as コンストラクタ開始
  • 4.▲MyMc0_as コンストラクタ終了
  • 5.mainTimeLine ムービークリップを動的に生成終了(new MyMc0_as)
  • 6.mainTimeLine フレームアクション終了
  • 7.MainTimeLineに配置した Mc0_tl フレームアクション開始
  • 8.MainTimeLineに配置した Mc0_tl フレームアクション終了
  • 9.Mc0_tlに配置した Mc1_tl フレームアクション開始
  • 10.Mc0_tlに配置した Mc1_tl フレームアクション終了
  • 11.★MyMc0_as フレームアクション開始
  • 12.★MyMc0_as フレームアクション終了

動的にnewしたMyMc0_asのフレームアクションが開始されるのは、ステージに配置してあるMc0_tlのフレームアクションが全て完了してから。

ここで気をつけるのは、MyMc0_asのフレームアクションにたとえば、


//MyMc0_asのフレームアクション開始
var myProp:Number;
myProp = Math.random();
MyMc0_asのフレームアクション終了

と書いて、
メインのタイムラインのフレームアクションで、以下のようにすると


var mc:MovieClip = new MyMc0_as();
trace(mc.myProp);//出力:NaN(Math.random()の代入は実行されていない)

期待通りの結果が得られない。

乱数が代入された結果を得るには、メインタイムラインの2フレーム目以降で、mc.myPropを参照すればOK。

一応検証したファイルをアップします。
1004231414_asflowtest.zip
読んでもさっぱり意味がわからない人はどーぞ。。。