Category Archive: ActionScript

[AS][?]FlashIDEでドキュメントクラスをSpriteに設定した場合の怪

一般的にMovieClipとSpriteの違いは、

・MovieClip:タイムラインを持てる。
・Sprite:タイムラインを持てない。

リファレンスを見てもMovieClipはSpriteのサブクラスであり、タイムライン操作についてのメソッドが追加されたものとなっている。

////////////////////////////////////////////////////////////////////////
・そもそも「タイムラインを持っている」とは具体的に何なのか?
フレームを2フレーム以上持っている状態を指すらしい。
参考:ActionScript3.0入門ノート|Spriteの作成と表示

つまり
・MovieClip:フレームを2フレーム以上持てる。
・Sprite:フレームを1フレームだけ持てる。
こういうことになるのでしょう。

////////////////////////////////////////////////////////////////////////
・ここで疑問なのです。
FlashIDEではドキュメントクラスをSpriteを継承したクラスを指定することができますよね。
この場合、メインタイムラインはSprite扱いということになると思うのですが、実際はそうではないようです。
以下の画像のような状態の場合、

これをパブリッシュすると、青い四角が点滅する状態となります。
ドキュメントクラスをSpirteにしているのに、メインタイムラインは2フレーム目以降も繰り返し再生されているのです。
ちなみに、この際フレームにstop();等のスクリプトを書くと、errorとなります。
これは、フレームにスクリプトが書けるのはMovieClipだけだからという理由です。
具体的にはフレームにスクリプトを書くという行為は、内部的にはaddFrameScript()というメソッドで実装されているようで、SpirteにはこのaddFrameScript()メソッドが定義されていないから、ということのようです。

つまり、ドキュメントクラスをSpriteで定義した場合(のメインタイムライン)は、完全なSpriteでもなく、またMovieClipでもないというような中途半端な存在に思えるのです。

////////////////////////////////////////////////////////////////////////
ちなみにMovieClipとしてライブラリに登録したシンボルの基本クラスをSpriteとし、
そのシンボルのタイムラインを10フレームにした場合について、
(下画像参照)

このシンボルをステージに置いた場合のシンボルの挙動はどうなると思いますか?
先ほどのように2フレーム目以降が繰り返し再生されるのでしょうか?

答えは、黒いシェイプは点滅しません。
1フレーム目のみが描画された状態でstop();状態となり、2フレーム目以降は無視されているようです。
これが本来のSpriteの挙動であると思います。

////////////////////////////////////////////////////////////////////////
メインタイムラインだけが特別な扱いになっているのでしょうか?
こういうもんだと思えば実害は無いのですが、なんか気持ち悪いですよね。
この理由をご存知の方いましたら是非ご教授くださいませ。

テスト環境:winXP FlashCS5

[JSFL]シンボル内の静止テキストのサイズ、色、フォントを変更するJSFL改

東京てらこでの発表ネタとして以前作ったものを少し改造しました。
基本用途は同じですので上のリンク先を御確認ください。

◆改修点
・パラメータの入力をswfパネルからできるようにしました。
・これによりフォント名については、直接入力から、PCにインスコされているフォントから選択する形式になりました。
(入力間違いが減って便利だと思う)
・以前のものは内包するshapeの色まで変わってしまいまいたが、静止テキストの色だけを変更するようにしました。
・mxp形式でインスコできるようにしました。(CS3/CS4/CS5)
・SWFパネルを常駐させるか、コマンド実行時のみ表示させるかの2通り用意しました。
・SWFパネル常駐タイプは、フォント名リストが表示できるSWFパネルがあわせてインスコされます。

◆使い方◆
▼SWFパネル常駐タイプ
1.ウィンドウ→他のパネル→ReInitializeStaticTextを選択
2.ライブラリで変更したいMCを選択し、SWFパネルのパラメータを調整しOKをクリック
※ウィンドウ→他のパネル→FontNameでフォント名一覧が表示されるSWFパネル表示
▼コマンド実行タイプ
1.コマンド→Re-InitializeStaticTextを選択するとSWFパネルが起動
2.ライブラリで変更したいMCを選択し、SWFパネルのパラメータを調整しOKをクリック
( SWFパネルは自動で閉じる)

以下よりDL可能です。
◆CS5
コマンド実行タイプ
SWFパネル常駐タイプ
◆CS4
コマンド実行タイプ
SWFパネル常駐タイプ
◆CS3
コマンド実行タイプ
SWFパネル常駐タイプ

[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を返す
AddKeyDownUtil.getInstance().isShiftDown;//戻り値:Boolean
//コントロールキーが押されてたら、true,押されてなかったらflaseを返す
AddKeyDownUtil.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;//文字列で指定しない。

[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)
//[2011.10.19追記]
//Indexでキャストだとうまくいかなくなったみたい
//CastDocumentでキャストする
trace(CastDocument(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();