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

[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
読んでもさっぱり意味がわからない人はどーぞ。。。

[AS]progression4RC departedSceneIdやdestinedSceneIdをif文とかで判定できないってときはここに注意

progressionを使ううえでの便利プロパティ。
departedSceneId
destinedSceneId
以前のエントリでも書いてるように、出発シーンと、到着シーンを取得できます。
ただし↑はprogression3の書き方。
progression4では、progressionインスタンスは無くなり、managerが持ってるプロパティ。

manager.destinedSceneId;
manager.departedSceneId;

これを使って、if文の処理をしたいときちょっとはまったのでメモ。
destinedSceneIdとかdepartedSceneIdをtraceすると、
/indexとか、/index/aboutとか、sceneIdを出力してくれます。
なので、戻り値のデータ型はString?と思いきや、実は違います。

trace(manager.departedSceneId)//出力(例):/index
trace(typeof manager.departedSceneId)//出力:object(stringではない)

なので、if文で判定するときは、↓だと上手く判定されません

//manager.departedSceneId == manager.destinedSceneIdは中身がなんであろうと常にtrueとなる
if(manager.departedSceneId == manager.destinedSceneId){
    //処理
}

traceしたときに見えるSceneId名で判定させる場合は.toString()をつければOK

//
if(manager.departedSceneId.toString() == manager.destinedSceneId.toString()){
    //処理
}

※2010/04/17/16:30追記
コメントにてequals()メソッドというものを教えて頂きました。
こっちの方が簡単ですね。

if(manager.departedSceneId.equals(manager.destinedSceneId)){
	//処理  
}

※4のBetaまではtoStringなしでも判定してくれてたような気がするけど、RCからは無いとダメみたい。

[AS]swcでくるんだクラスをgetDefinitionByName()で読み込むときの注意

SWC書き出しでアセット作るというワークフローを試してみたところ、若干はまったのでメモ。

SWCの書き出しと読み込みはkayacさんの記事を参照ください。
参考:SWC書き出しを有効に使って作業効率アップ:_level0.kayac

で、たとえば、MyMc0ってクラス名でリンケージしたMcを含んだswcファイルを用意したとして、
そのMyMc0を取り出すときの処理で、普通に書くと↓な感じでOK

var myMc0:MyMc0 = new MyMc0();
addChild(myMc0);

ただここで、クラス名を動的に生成したい場合は、getDefinitionByName()使わないとなので、こんな感じ。
これも問題なくOK。

var index:int = 0;
var className:String = "MyMc" + index;
var myClass:Class = getDefinitionByName(className) as Class;
//myMc0はMyMc0として定義
var myMc0:MyMc0 = new myClass();
addChild(myMc0);

で、お次はハマったダメな場合の書き方
↓これはエラーになります。

var index:int = 0;
var className:String = "MyMc" + index;
var myClass:Class = getDefinitionByName(className) as Class;
//myMc0はMovieClipとして定義
var myMc0:MovieClip = new myClass();
addChild(myMc0);

違いは、変数myMc0の型をMyMc0とするか、MovieClipにするか、ということ。
MyMc0なら、エラーにはならないけど、MovieClipにするとエラーになります。

MyMc0の親クラスはMovieClipなんだから、間違いないんじゃね?
と思ってましたが、問題はそこではなく、エラーのコードでは、MyMc0が一度も出てきてない、という事実が問題。
もっと具体的にいうと、「MyMc0に参照が通ってないからMyMc0は使えない」、ということになるんだそうだ。

詳細は以下、

コンパイルする段階で、使われてないクラスは含まないようにして最適化されるからですよん。なので import しただけだと、使ってないとみなされて、コンパイル段階で省かれてしまうんです。
必要なのはクラス名ではなくて、クラスへの参照です。なので、ロジック中にクラス書くと、エラー回避出来るんです。

soundkitchen:Tweet1
soundkitchen:Tweet2

なので、エラーコードを修正例としては、

//MyMc0を明示的に表記することで参照を通して、MyMcを使うよってことをアピール。
MyMc0;
//上の書き方がよくわからなくて気持ち悪いときは、空の変数定義でもOK
var hoge:MyMc0;
var index:int = 0;
var className:String = "MyMc" + index;
var myClass:Class = getDefinitionByName(className) as Class;
//myMc0はMovieClipとして定義
var myMc0:MovieClip = new myClass();
addChild(myMc0);

個人的な感覚としては、 getDefinitionByName()でクラス作って、そのクラスでnewしてるんだから、問題ないんじゃない?と思ってたけど、プログラム的には明示的に参照を通さないとだめらしい。

Twitterのおかげで知識が広がる、嬉しい限り。

ちなみにswcを使いたかったのはFlexBuilderとかFlashDevelop+flexSDKで製作するようにしたかったから。
ムービープレビューが速い速い。