‘Flash’ カテゴリーのアーカイブ

executeのextra引数

2010 年 2月 23 日 火曜日 kosuke

Progressionでコマンドを実行するexecuteメソッドには、extraという引数があります。
シーン遷移でコマンドを実行する場合、extraを使うケースはほとんどないと思いますので、あまり馴染みのない引数ですが、コマンドを単体で利用する場合、extraを使うと便利なこともあります。

コマンドリストを実行する際extraを渡すと、コマンドリストに登録された全てのコマンドは実行時に渡されたextraをリレーします。つまり、登録されたコマンドはすべて実行時にextraを利用できます。

たとえば、一連のアニメーションを登録したコマンドリストを実行し、最後の挙動だけ異なるようにしたい場合など、extraを渡して挙動を変更することが可能です。

例:同じコマンドリストを実行し、最後のコマンドでextraで渡した異なる色値に変更する。

This movie requires Flash Player 10.0.0

btn0.label	= "RED";
btn1.label	= "GREEN";
btn2.label	= "BLUE";
 
 
_comm	= new SerialList( null, 
	new Prop( ball, { x:450, y:130 } ),
	new Prop( ball.transform, { colorTransform:new ColorTransform() } ),
	[
		new DoTweener( ball, { x:200, transition:"easeOutSine", time:3 } ),
		new DoTweener( ball, { y:350, transition:"easeOutBounce", time:3 } )
	],
	new Func( function():void{
		var trans:ColorTransform = new ColorTransform();
		trans.color	= this.extra;
		ball.transform.colorTransform = trans;
	})
);
_comm.interruptType = CommandInterruptType.RESTORE;
 
 
 
btn0.addEventListener( MouseEvent.MOUSE_DOWN, function(e:MouseEvent):void{
	if( _comm.state == ExecutorObjectState.EXECUTING ) _comm.interrupt( true );
	_comm.execute( 0xFF0000 );
});
 
btn1.addEventListener( MouseEvent.MOUSE_DOWN, function(e:MouseEvent):void{
	if( _comm.state == ExecutorObjectState.EXECUTING ) _comm.interrupt( true );
	_comm.execute( 0x00FF00 );
});
 
btn2.addEventListener( MouseEvent.MOUSE_DOWN, function(e:MouseEvent):void{
	if( _comm.state == ExecutorObjectState.EXECUTING ) _comm.interrupt( true );
	_comm.execute( 0x0000FF );
});

Twitterの投稿欄に入力するリクエスト

2010 年 2月 18 日 木曜日 kosuke

Twitterを全然利用しないので(特にFlasherには前から流行っているのは知ってるけどニガテなんす。この手のもの)知らなかったのだが、TwitterにstatusパラメータをつけたURLでリクエストすると自分の投稿欄にstatusの値が入力されるってことを最近知りました。

こんな感じ。

http://twitter.com/home/?status=aiueo

Twitterのアカウントを持っていてログインしているなら、投稿欄に「aiueo」と入力されるはず。

何も考えずにFlashからこれを使ったら、

var req:URLRequest		= new URLRequest( "http://twitter.com/home/?status=aiueo" );
navigateToURL( req );

これで同様に投稿欄に入力されているので、ヨシッと思ったのですが、

//2バイト文字を含む
var req:URLRequest		= new URLRequest( "http://twitter.com/home/?status=あいうえお" );
navigateToURL( req );

とか、

//メタ文字を含む
var req:URLRequest		= new URLRequest( "http://twitter.com/home/?status=http://nipx.jp/#/nipx" );
navigateToURL( req );

だとうまくいきません。

これはURLエンコードされないのが原因。端折らずちゃんとURLVariablesで値を渡せば解消されます。(問題の文字列をエンコードするって手もありますが。)

var req:URLRequest	= new URLRequest( "http://twitter.com/home/" );
var vers:URLVariables	= new URLVariables();
vers.status	= "あいうえお/#/かきくけこ";
req.data	= vers;
navigateToURL( req );

さらにTwitterに送る文字コードはUTF-8である必要があります。
なので、FlashでSystem.useCodePage = true;を使っている場合、送信前にフラグをおろす必要があります。

//文字コードはUTF-8で送る必要あり
System.useCodePage = true;	//useCodePage = trueにしている場合
 
var req:URLRequest		= new URLRequest( "http://twitter.com/home/" );
var vers:URLVariables	= new URLVariables();
vers.status	= "あいうえお/#/かきくけこ";
req.data	= vers;
System.useCodePage = false;
navigateToURL( req );
System.useCodePage = true;

PreloadSWF

2010 年 2月 10 日 水曜日 kosuke

Progression 4.0.1 Public Beta 1.3 ですが過去のバージョンに比べると、とても完成度が上がっていますね。Beta版ということで避けていた方もそろそろ手をだしていい時期と思いました。
先日とある案件で使ってみて、便利なところにいろいろ気づきましたので、いくつか記事にしようと思います。

今回はあらたに追加されたPreloadSWFコマンドについて。
このPreloadSWFを使うと任意のタイミングでプリロードを開始できます。
これがどう便利かというと、CastPreloaderクラスを使って子SWFを読み込む場合おおよそ以下の流れとなります。

1.読み込む直前。この時、atCastLoadStart にて非同期処理が可能。
2.読み込み。この時、atProgress でロード毎に処理が可能。(プログレスバーを進めるなど)
3.読み込み完了。この時、atCastLoadComplete にて非同期処理が可能。

通常、これでまったく問題ないのですが、プリローダーで子SWF以外にも外部ファイル(画像だったり、サウンドだったり、他のSWFだったり)を読み込み、プログレスバーを進行させたいこともあると思います。

この場合、Progressionのコマンドで行うなら、プログレスバーの進行度合いを管理しつつ、atCastLoadStart か、atCastLoadComplete 内で読み込む形になります。こうなるとプログレスバーの管理が若干面倒ですよね。PreloadSWFコマンド(とLoaderList)を使えば、これをとてもスマートに行えます。

例:atCastLoadStart で外部ファイルの読み込みとCastPreloaderの読み込みを実行。

override protected function atCastLoadStart():void{
	addCommand(
		new LoaderList( {
				onProgress:function():void{
					trace( this.percent + "%" );
				}
			},
			new LoadBitmapData( new URLRequest( "image1.jpg" ) ),
			new Trace( "image1 完了" ),
			new PreloadSWF( {} ),
			new Trace( "Preloader 完了" ),
			new LoadBitmapData( new URLRequest( "image2.jpg" ) ),
			new Trace( "image2 完了" ),
			new LoadSound( new URLRequest( "sound1.mp3" ) ),
			new Trace( "sound1 完了" )
		),
		new Wait( 3 )
	)
}

この場合、image1.jpg の読み込み後、CastPreloaderの読み込みが実行され、その後、image2.jpg、sound1.mp3 を読み込み、3秒後に atCastLoadStart が完了します。特に atCastLoadComplete を設定しなければ、この後、読み込んだCastDocumentの処理に移行します。
少し注意したいのは、atCastLoadCompleteは、PreloadSWFの完了ではなく、atCastLoadStart の完了後実行されることでしょうか。

この方法をとれば、プログレスバーは、LoaderListの onProgrss で進行させるだけですむようになります。
読み込むファイルの容量の差が大きく、プログレスバーの進行がスムーズでない時は、各読み込みコマンドのfactorプロパティを設定して相対的な重要度を設定します。

たとえば、
image1.jpg が 10Kbyte
index.swf が 10Kbyte
image2.jpg が 10Kbyte
sound1.mp3 が 30 Kbyte

なら、sound1.mp3を読み込むコマンドのfactorを3にすると、プログレスバーの進行の差が少なくなると思います。

イベントフローの確認

2010 年 1月 15 日 金曜日 kosuke

普段気にすることはありませんが、addEventListenerのuseCaptureについて。
AS3ではイベントが3段階で処理されている。原則的にイベントはステージから、イベントが発生したオブジェクトに向かい、オブジェクトに辿り着いたら今度はステージに戻ってくる。
入れ子になった表示オブジェクトは、この経路にしたがってイベントをリレーしているわけだ。
普段意識せず、useCapture = false のままに設定しているなら、イベントが発生したオブジェクトの段階か、ステージに戻ってくる段階の通知をキャッチしている。

このことを示した図がリファレンスに書かれている。
イベントフロー

この図はとてもよく説明されているのだけど、実際に動いているもので確認したくて試しました。
Eventオブジェクトには、イベントのリレーを止める、stopPropagationとstopImmediatePropagationってメソッドもあるので、この二つの挙動についても試している。

イベントフローを意識することがあるのは、入れ子になった表示オブジェクトの場合が想定されるので、大きい方から順番にA、B、Cと円を定義して、入れ子にする。

var A:Sprite	= new Sprite();
A.name = "A";
var B:Sprite	= new Sprite();
B.name = "B";
var C:Sprite	= new Sprite();
C.name = "C";
 
A.graphics.lineStyle(5, 0x000000 );
A.graphics.beginFill( 0x000000, 0 );
A.graphics.drawCircle( 0,0, 100 );
 
B.graphics.lineStyle(2.5, 0x404040 );
B.graphics.beginFill( 0x404040, 0 );
B.graphics.drawCircle( 0,0, 75 );
 
C.graphics.lineStyle(1, 0x808080 );
C.graphics.beginFill( 0x808080, 0 );
C.graphics.drawCircle( 0,0, 50 );
 
//BはAの子、CはBの子
A.addChild( B );
B.addChild( C );
addChild( A );

全ての円にキャプチャ段階とターゲット・バブリング段階それぞれにリスナーを定義します。
stopPropagation、stopImmediatePropagationを試すべく、優先順位をつけてリスナーを二つ登録しました。
優先順位の高い方のリスナーは、ラジオボタンに応じてstopPropagation、stopImmediatePropagationが実行されるようにします。
リスナーはテキストエリアに処理された内容を表示します。

//優先度の高いリスナー
A.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownCaptureing1, true );
B.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownCaptureing1, true );
C.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownCaptureing1, true );
 
A.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownTarget1, false );
B.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownTarget1, false );
C.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownTarget1, false );
 
//優先度の低いリスナー
A.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownCaptureing2, true, -1 );
B.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownCaptureing2, true, -1 );
C.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownCaptureing2, true, -1 );
 
A.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownTarget2, false, -1 );
B.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownTarget2, false, -1 );
C.addEventListener( MouseEvent.MOUSE_DOWN, _onMouseDownTarget2, false, -1 );
 
 
private function _onMouseDownCaptureing1(e:MouseEvent):void{
	if( group1.selectedData == 1 ) e.stopPropagation();
	else if( group1.selectedData == 2 ) e.stopImmediatePropagation();
	var str:String = "#1 " + e.currentTarget["name"] + "がキャプチャ段階のリスナーで、" + e.target["name"] + "をターゲットに" + e.type + "イベントを" + _returnPhase(e) + "で処理。";
	_field.appendText( str + "\n" );
}
 
private function _onMouseDownTarget1(e:MouseEvent):void{
	if( group2.selectedData == 1 ) e.stopPropagation();
	else if( group2.selectedData == 2 ) e.stopImmediatePropagation();
	var str:String = "#1 " + e.currentTarget["name"] + "がターゲット・バブリング段階のリスナーで、" + e.target["name"] + "をターゲットに" + e.type + "イベントを" + _returnPhase(e)  + "で処理。";
	_field.appendText( str + "\n" );
}
 
private function _onMouseDownCaptureing2(e:MouseEvent):void{
	var str:String = "#2 " + e.currentTarget["name"] + "がキャプチャ段階のリスナーで、" + e.target["name"] + "をターゲットに" + e.type + "イベントを" + _returnPhase(e)  + "で処理。";
	_field.appendText( str + "\n" );
}
 
private function _onMouseDownTarget2(e:MouseEvent):void{
	var str:String = "#2 " + e.currentTarget["name"] + "がターゲット・バブリング段階のリスナーで、" + e.target["name"] + "をターゲットに" + e.type + "イベントを" + _returnPhase(e)  + "で処理。";
	_field.appendText( str + "\n" );
}
 
private function _returnPhase(e:MouseEvent):String{
	return ( EventPhase.AT_TARGET == e.eventPhase ) ? "ターゲット段階" : ( EventPhase.BUBBLING_PHASE == e.eventPhase ) ? "バブリング段階" : "キャプチャ段階";
}

こちらで試せます。場合によっては意図に反して2回処理されていたみたいな間違いはありそうだね。
こんなものを意識して何か作ることが無いことを祈るばかり。

This movie requires Flash Player 10.0.0

コメントを記述するなら

2010 年 1月 5 日 火曜日 kosuke

Flashで開発する際、コメントってどうしてます?
僕の場合、自分しかさわらない一度っきりのプロジェクトにいちいち緻密にコメントつける必要性は微妙と思いつつも、見直してみると大抵コメントをつけているようです。
最近はどうせコメントをつけるならとASDocに対応した形式で記述するようにしました。
このASDocの書式には結構多様な指定が出来るようになっています。
刺身のつまみたいなもんなんで、全部覚えようなんて気にはまったくならないのだけども、いざ書き出そうって時にエラーってことが頻繁なんで、自分的な要点を記録しておきます。

先にヘルプのインデックスへリンク。
参考:ASDoc の使用

コメントの記述は以下の書式。/** と */ の間にコメントを書く。
コメント行頭の * は無くても問題なく書き出されるような気がしますが、ASDocで解析するのに使うと参考にあるので記述する。この書式をクラスやメソッド、プロパティなどを記述している直前に書く。この位置にコメントを書くことは多いと思うので、どうせならこれにしておくって感じ。

/** 
* Main comment text.
* 
* @tag Tag text.
*/

参考:ASDoc コメントの作成

@の部分がASDocのタグになっていて、ASDocを書き出す時に整形されます。
参考:ASDoc タグ

いろいろありますが、メソッドやコンストラクタの引数用に@param。戻り値用に@return。くらいしか使っていない。自分用にはこの位でいんじゃないかと思います。僕的にコケたところで、getter/setterの場合、ASDocのコメントは1カ所に書きます。

/**
 * 直線を定義します。
 */	
public class Line{
 
	/**
	 * 直線を定義します。
	 * 
	 * @param a 直線を(ax + by + c = 0)で表す場合のaです。
	 * @param b 直線を(ax + by + c = 0)で表す場合のbです。
	 * @param c 直線を(ax + by + c = 0)で表す場合のcです。
	 */	
	public function Line( a:Number, b:Number, c:Number ){
		this._a	= a;
		this._b	= b;
		this._c	= c;
 
		this._update();
	}
 
 
	/**
	 * 直線を(ax + by + c = 0)で表す場合のaです。 
	 * @return ax + by + c = 0 の a です。
	 */		
	public function get a():Number{
		return this._a;
	}
	public function set a(n:Number):void{
		this._a = n;
		this._update();
	}
	private var _a:Number;
 
}

コメントはHTMLタグが使える。
参考:一般に使用される HTML エレメントの一覧

ASDocを書き出した時に改行くらい無いと読みづらいものあるので<p>タグは良く使う。
<br>はうまく利かない気がする。あと<a>タグも使えるので参考がある時は記述しておきますね。
コメントを勢いで書くとよく失敗するのが特殊文字。参考の下にある実体参照は注意した方がいい。&lt; &gt; &amp;みたいなHTMLオーサでもよく使うものはすぐ気づいたのだけど、@(アットマーク)とか*(アスタリスク)ってあまり実体参照で使わないと思うので。これがあると書き出しの時エラーになります。

@ = &#64;
* = ~~ (実体参照じゃないけど、ASDoc書式ではチルダ二つで書く)


コメントの記述はこの位にしてASDocの書き出し方。
Flex SDKとターミナルを使います。
FlashDevelop環境ならGUIからいけるっぽいが、自分はMac環境。Mac版作って欲しい。
なのでここからターミナルの操作要点になります。(自分はこの使い方をいつも忘れるのでこっちの方が重要メモ)

ターミナルを立ち上げたら、asdocの実行ファイルディレクトリに移動します。実行ファイルはFlex SDKのbinディレクトリの中にある。コマンドラインを見ると気分が悪くなる僕みたいな人向けの移動する方法。

ターミナルにcd を入力してbinフォルダをドロップ。


次にコマンドを入力します。
参考: ASDoc ツールの実行
コマンドオプションが沢山ありますが必要に応じて指定します。
僕は書き出すファイルをまとめて-doc-sourcesを指定するだけで書き出してます。

まず、asdocを指定する。./を忘れないように注意。

./asdoc


Flex SDKのバージョンによりますが、FlashPlayer10以降のクラスが含まれる場合(Matrix3DとかVector3Dとか)-target-player オプションを入れます。
参考:Flex Builder 3をFlash Player 10に対応させる

./asdoc -target-player=10.0.12


ライブラリにSWCを使っている場合(Progressionとか)、-library-path オプションを入力してSWCファイルのディレクトリを指定します。これもフォルダドロップでOK。

./asdoc -target-player=10.0.12 -library-path /Users/name/project/libs/


書き出したいディレクトリを-doc-sourcesで指定します。フォルダドロップでOK。

./asdoc -target-player=10.0.12 -library-path /Users/name/project/libs/ -doc-sources /Users/name/project/src/jp/nipx/

これを実行すればbinの中にasdoc-outputフォルダが生成され中にASDocが入っています。


知っておくと便利そうなターミナル操作
Control + A 行頭へ移動
Control + E 行末へ移動
Control + U カーソルより左の文字をすべて消去
Control + W カーソルより左の文字を単語単位で消去
Control + K カーソルより右の文字をすべて消去
(ターミナルの設定 > キーボード > メタキーとしてOptionキーを使用にチェックを入れて)
Option + B 単語単位で左に移動
Option + F 単語単位で右に移動

ところで世に配布されているありがたいライブラリなんかは、間違いなく整然としたコメントがつけられています。
数式と文章って真逆なようでそうでもないものだと思う。わかりやすいソースはわかりやすい言葉で説明できるものだと思いました。

外部SWFに埋め込んだフォントを使う

2010 年 1月 4 日 月曜日 kosuke

年末の忘年会で、外部SWFに埋め込んだフォントを使う話題がありました。埋め込んだフォントがうまく有効にならないってことで、その時は多分出来るんじゃない?位に思ってましたけど、やってみたらすんなりとはいかなかったので記録しておきます。

話題に上がったのは、たぶんこういうファイル構成だと思う。

これを前提にして試してみた。

fonts.swfのステージにダイナミックテキストを置いて、ヒラギノ角ゴ Pro W6、ヒラギノ明朝 Pro W6を配置。
両方とも“日本語(かな)(318文字)”を埋め込んでパブリッシュ。
コンストラクタで、

public function Fonts(){
	var arr:Array	= Font.enumerateFonts( false );
	for each( var i:Font in arr ){
		trace( i.fontName );
	}
}

としてトレースして結果は期待どおり。

続いてpreloader.swfからfonts.swfを読み込んでトレースしてみる。
埋め込みが参照できれば、—▼Preloader▼—の下にフォント名が出力されるはず。

public function Preloader(){
	var loader:Loader = new Loader();
	loader.load( new URLRequest( "fonts.swf" ) );
	loader.contentLoaderInfo.addEventListener( Event.INIT, function(e:Event):void{
		trace( "---▼Preloader▼---" )
		var arr:Array	= Font.enumerateFonts( false );
		for each( var i:Font in arr ){
			trace( "Preloader:" + i.fontName );
		}
	});
}

あれ?出力されない…。どうもこの時点でfonts.swfの埋め込みを参照できないっぽい。
で、色々試してみたらFont.registerFont()でグローバルフォントリストに追加しないといけないようだ。
Font.registerFont()にはクラスを渡すことになっているのだけど、今回みたいな一部フォントの場合、どうやればよいか?(フォントシンボルですべての文字を埋め込む場合は、フォントシンボルのクラスでOK。)
これはメタタグを使えばうまくいった。

fonts.swfのダイナミックテキストによる埋め込みをやめて、Embedタグで埋め込む。

public class Fonts extends Sprite{
 
   [Embed(source='../../assets/ヒラギノ角ゴ Pro W6.otf',
    	fontName= "Hiragino Kaku Gothic Pro W6",
		mimeType='application/x-font',
		unicodeRange="U+3000-U+303F,U+3041-U+309F,U+30A0-U+30FF,U+FF61-U+FF9F" /* Japanese Kana */
    )]
	private static var HIRAGINO_KAKUGO_PRO_W6:Class;
 
   [Embed(source='../../assets/ヒラギノ明朝 Pro W6.otf',
    	fontName= "Hiragino Mincho Pro W6",
		mimeType='application/x-font',
		unicodeRange="U+3000-U+303F,U+3041-U+309F,U+30A0-U+30FF,U+FF61-U+FF9F" /* Japanese Kana */
    )]
	private static var HIRAGINO_MINCHO_PRO_W6:Class;
 
	public function Fonts(){
		Font.registerFont( HIRAGINO_KAKUGO_PRO_W6 );
		Font.registerFont( HIRAGINO_MINCHO_PRO_W6 );
	}
 
}

embedの書式について詳しくは、Flexのヘルプ参照。
埋め込みフォントの使用

sourceにはフォントファイルへのパスを書く。相対パスの基準はEmbedを書くasファイルから。
fontNameはasから参照するときのフォント名をつける。上記みたいなそのまんまのネーミングは混乱のもとになる予感。My 〜 とかの方がよさそうな気がする。
unicodeRangeで埋め込む範囲を指定するのだけど、これについては後ほど。
Embedタグの下の変数はEmbed参照用の変数ね。これをFont.registerFontで登録する。
ひとまずこれでPreloaderから参照できるか試してみる。


今度は参照できるようになった。

グローバルに追加されていればindex.swfからも問題なく使える。

public function Index(){
	trace( "---▼Index▼---" );
	var arr:Array	= Font.enumerateFonts( false );
	for each( var i:Font in arr ){
		trace( "Index:" + i.fontName );
	}
 
	var field:TextField	= new TextField();
	field.autoSize	= TextFieldAutoSize.CENTER;
	field.defaultTextFormat	= new TextFormat( "Hiragino Mincho Pro W6", 24 );
	field.embedFonts	= true;
	field.x	= 200;
	field.y = 138;
	field.rotation= 15;
	field.text	= "あけましておめでとうございます";
	this.addChild( field );
}

最後にunicodeRangeについて。
埋め込み文字の範囲はこれにUnicodeの文字コードを指定し行うようだ。
指定の仕方は、
文字範囲の設定
に詳しく書かれているようです。
がっつり英語が読めるならこれらしい。
15 Fonts

で論より実践でいきます。上記リンクに記載がありますが、Flex SDKの中に、
flash-unicode-table.xmlってファイルがあって、日本語のかなだけとか、漢字 JIS第一水準みたいなFlashにある文字セットが記載されているのでまとめて埋め込む時はこれを使えばよさそう。

個別の文字だけど、U+3041でいうところの3041の部分、U+~の~が16進数で表された文字コードになっているようなのでActionScriptで以下のような変換を使って出せると思う。

var str:String	= "あ";
//Unicode 10進数表記
var num:Number	= str.charCodeAt(0);
//Unicode 16進数表記
var code:String	= num.toString( 16 );
//文字
var txt:String	= String.fromCharCode( parseInt( code, 16 ) );
trace( num );
trace( code );
trace( txt );
 
/*
出力
12354
3042
あ
*/

ってことで、めんどくさいけどいけるんじゃないんですかね。 >> Shuさん

AMFのこと

2009 年 12月 9 日 水曜日 kosuke

airアプリを作った際、その初期設定を保存するようにしました。
その保存フォーマットはオブジェクトをJSONでエンコードしてテキスト形式を使っていたのだけど、良くみてみるとActionScriptではAMFってフォーマットがある。
これを使えばActionScriptで生成したオブジェクトを丸ごと保存するってことが出来そうだ。

とりあえず、見つけた資料はこのあたり。
AS2.0までに対応する規格AMF0とAS3.0以降に対応する規格AMF3がある。
AMF 0 規格仕様
AMF 3 規格仕様

ググった感じサーバーとのやり取りに使うケースが主目的っぽいのと、なぜMovieClipは渡せないの?(上の規格資料だとAMF0規格時からサポート外となってるっぽい)とかまだ不明なところがあるのだが、今回のような目的ならJSONで保存するより便利そうと気づいた点が2点あります。

まず、JSONでうまくエンコードできなかったSpriteなどのオブジェクトもそのまま保存出来た。

以下のようなSpriteを含むオブジェクトをJSONでエンコードした場合(エンジンはas3corelibで試してます。)エラーになったけど、AMFではそのまま保存出来る。
次にAMFにある型のオブジェクトは、そのままの型で受け取れる。

var file:File;
var stream:FileStream		= new FileStream();
	stream.objectEncoding	= ObjectEncoding.AMF3;
var read:Object
var sp:Sprite	= new Sprite();
	sp.x		= 10;
 
//保存するオブジェクト
var write:Object	= {
	arr:[ "a", "b", "c" ],
	date:new Date(),
	xml:<root>root</root>,
	sprite:sp,
	byte:new ByteArray()
}
 
 
//AMF------------------------------------------
file	= File.desktopDirectory;
file	= file.resolvePath( "AMF.dat" );
 
stream.open( file, FileMode.UPDATE );
stream.writeObject( write );
 
stream.position	= 0;
read = stream.readObject();
 
trace( "AMF:", read.arr, read.arr is Array ); 
trace( "AMF:", read.date, read.date is Date );
trace( "AMF:", read.xml, read.xml is XML );
trace( "AMF:", read.sprite.x, read.sprite is Sprite ); 
trace( "AMF:", read.byte, read.byte is ByteArray );
 
file.clone();

traceの結果は、
AMF: a,b,c true
AMF: Tue Dec 8 18:52:43 GMT+0900 2009 true
AMF: root true
AMF: 10 false
AMF: true

JSONで試すと以下の結果でした。

//JSON------------------------------------------
file	= File.desktopDirectory;
file	= file.resolvePath( "JSON.txt" );
 
stream	= new FileStream();
stream.open( file, FileMode.UPDATE );
 
var json:String	= JSON.encode( write );
stream.writeUTF( json );
 
stream.position = 0;
read = stream.readUTF();
read	= JSON.decode( json );
 
trace( "JSON:", read.arr, read.arr is Array ); 
trace( "JSON:", read.date, read.date is Date );
trace( "JSON:", read.xml, read.xml is XML );
trace( "JSON:", read.byte, read.byte is ByteArray );
 
file.clone();

traceの結果は、
JSON: a,b,c true
JSON: [object Object] false
JSON: [object Object] false
JSON: [object Object] false

ひとまず、型が記録されるAMFの方が読み込み後のチェックなど便利そうに思える。
もう一点、AMFはバイナリデータである為、保存したファイルをテキストエディタで開いてもなんのことかわからない。

JSONもなんのことかわからないといえば、わからない訳だけどフォーマットを手書きで記述することが出来ないこともないと思う。
この辺も用途によるかなぁと思った。

BreakとReturn

2009 年 12月 7 日 月曜日 kosuke

Progression 4 にある中断コマンド、BreakとReturnについて、その違いをメモ。

シーン /index/a/1 から シーン /index に移動する時、
シーン /index/a/1 のGotoで Breakの場合とReturnの場合で試します。

protected override function atSceneGoto():void{
	this.addCommand(
		new Break(),  // ←これを使う場合と
		new Return(), // ←これを使う場合の違い
		new Trace( "a/1 - atSceneGoto" )
	)
}

Breakを使った場合
new Trace( “a/1 – atSceneGoto” ) は実行されないが、その後はシーン遷移に基づき シーン /index まで実行される。
つまりBreakの場合、処理中のコマンドリストはBreak実行された時点で完了となり、その後のコマンドリストは引き続き処理される。

試しに、Gotoを以下にした場合、

protected override function atSceneGoto():void{
	this.addCommand(
		new SerialList( {
				onComplete:function():void{
					trace("onComplete");
				}
			}, 
			new Break(),
			new Trace( "SerialList" )
		),
		new Trace( "a/1 - atSceneGoto" )
	)
}

new Trace( “SerialList” )は処理されないけど、
trace(“onComplete”)
new Trace( “a/1 – atSceneGoto” )
は実行される。

Returnを使った場合
new Trace( “a/1 – atSceneGoto” ) は実行されず、Returnが実行された時点でシーン遷移が停止する。

Breakの時と同様以下を試すと

protected override function atSceneGoto():void{
	this.addCommand(
		new SerialList( {
				onInterrupt:function():void{
					trace("onInterrupt");
				}
			}, 
			new Return(),
			new Trace( "SerialList" )
		),
		new Trace( "a/1 - atSceneGoto" )
	)
}

new Trace( “SerialList” )
new Trace( “a/1 – atSceneGoto” )
とも処理されず、
trace(“onInterrupt”)は実行される。つまり中断になるわけです。
ちなみに、Progressionクラスの stop() メソッドもReturnと同じっぽい。
試しに以下を実行した場合も結果は同じだった。

protected override function atSceneGoto():void{
	this.addCommand(
		new SerialList( {
				interruptType:CommandInterruptType.SKIP,
				onInterrupt:function():void{
					trace("onInterrupt");
				}			}, 
			function():void{
				manager.stop();
			},
			new Trace( "SerialList" )
		),
		new Trace( "a/1 - atSceneGoto" )
	)
}

Proxyクラスのこと

2009 年 11月 9 日 月曜日 kosuke

Proxyクラスはどんな時使うものか見当のつかない状態だったのだけど、便利に使えそうなケースが見つかったのでエントリー。

Proxyについてはここに説明がある。
ActionScript 3.0 言語およびコンポーネントリファレンス
これ読んでも正直なんのこっちゃって思っていた。

思いついた方法は、複数のオブジェクトのいくつかのプロパティを一斉に変更したい時、Proxyを介して変更するってもの。いろんな方法があると思いますが、Proxyを介して行うのもなかなか便利そうです。

以下のように複数の表示オブジェクトがあり、それらのプロパティを変更したいとします。

This movie requires Flash Player 10.0.0

まず、表示オブジェクトを配列なりVectorなりに格納する。

var vector:Vector.<MovieClip>	= new Vector.<MovieClip>();
vector.push( this.arrow0 );
vector.push( this.arrow1 );
vector.push( this.arrow2 );
vector.push( this.arrow3 );

これをProxyを継承したクラスに渡す。

 
this.proxy	= new ProxyMcGroup( vector );
 

Proxyを継承したクラスではVectorを変数に代入する。
継承する時はdynamicで宣言しておいた方が、proxy.xのようにドットシンタックスでプロパティを参照してもエラーにならないのでいいと思う。

public dynamic class ProxyMcGroup extends Proxy{
 
	private var _item:Vector.<MovieClip>;
 
	public function ProxyMcGroup( _item:Vector.<MovieClip> ){
		super();
 
		this._item		= _item;
	}
 
}

Proxyを継承したクラスのsetPropertyをオーバーライトし、Vectorを繰り返し処理でプロパティを変更する。

override flash_proxy function setProperty(name:*, value:*):void{
	for each( var i:MovieClip in this._item ){
		i[name] = value;
	}
}

ひとまずこれだけで、複数オブジェクトのプロパティを一斉に変更できるようになる。
複数オブジェクトが同じプロパティを持つことを前提に、どのプロパティも変更できる(これが便利なポイント)はずです。

このままだと、setterだけなので取得が出来ない。
また例えばTweenerで動かす時はTweenerがhasOwnPropertyを使ってプロパティの有無を確認したりするので、必要に応じて拡張していく。
サンプルのは以下のようプロパティ値をDictionaryに保存するようにしたり、for…inステートメントでプロパティの参照が出来るようにしてみた。

package{
 
 
	import __AS3__.vec.Vector;
	import flash.display.MovieClip;
	import flash.utils.Proxy;
	import flash.utils.flash_proxy;
	import flash.utils.Dictionary;
 
 
	public dynamic class ProxyMcGroup extends Proxy{
 
 
		private var _item:Vector.<MovieClip>;
		private var _dictionary:Dictionary;
		private var _prop:Array;
 
 
		public function ProxyMcGroup( _item:Vector.<MovieClip> ){
			super();
 
			this._item			= _item;
			this._dictionary	= new Dictionary(true);
			this._prop			= [];
		}
 
 
		override flash_proxy function callProperty(methodName:*, ...args):*{
			trace( "callProperty:" + methodName + "," + args );
			return;
		}
 
 
	    override flash_proxy function getProperty(name:*):*{
	    	trace( "getProperty:" + name );
	    	return this._dictionary[name];
	    }
 
 
	    override flash_proxy function setProperty(name:*, value:*):void{
	    	trace( "setProperty:" + name + "," + value );
	    	if( !this._dictionary.hasOwnProperty(name) ){
	    		this._prop.push( name );
	    	}
	    	this._dictionary[name] = value;
	    	for each( var i:MovieClip in this._item ){
	    		i[name] = value;
	    	}
	    }
 
		override flash_proxy function hasProperty(name:*):Boolean{
	    	trace( "hasProperty:" + name );
			return this._dictionary.hasOwnProperty(name);
		}
 
 
		override flash_proxy function deleteProperty(name:*):Boolean{
			trace( "deleteProperty:" + name )
			this._prop = this._prop.filter(
				function( item:*, index:int, array:Array ):Boolean{
					if( item == name ) return false;
					else return true;
				}
			);
			return delete _dictionary[name];
		}
 
 
	    override flash_proxy function getDescendants(name:*):*{
	    	return;
	    }
 
 
		override flash_proxy function nextNameIndex(index:int):int{
			trace( "nextNameIndex:" + index );
			if( index < this._prop.length){
				return index + 1;
			}
			else{
				return 0;
			}
		}
 
 
		override flash_proxy function nextName(index:int):String{
			trace("nextName:" + index);
			return this._prop[index - 1];
		}
 
 
		override flash_proxy function nextValue(index:int):*{
			trace("nextValue:" + index);
			return this._dictionary[ this._prop[index - 1] ];
		}
 
 
	}
}

動かしているところはProgressionのSerialListを使っています。
動かす前に使うプロパティに値を入れておく。

this.proxy.rotation = 0;
this.proxy.alpha	= 1;
this.proxy.scaleX	= 1;
this.proxy.scaleY	= 1;
 
var comm:SerialList	= new SerialList( { onComplete:function():void{ this.execute(); } },
	new DoTweener( this.proxy, { rotation:360, transition:"easeInOutExpo", time:2 } ),
	new DoTweener( this.proxy, { alpha:0.5, transition:"easeNone", time:1 } ),
	new DoTweener( this.proxy, { scaleX:1, scaleY:1, _bezier:{ scaleX:0, scaleY:0 }, transition:"easeInOutExpo", time:1 } ),
	new DoTweener( this.proxy, { rotation:0, transition:"easeInOutExpo", time:2 } ),
	new DoTweener( this.proxy, { alpha:1, transition:"easeNone", time:1 } )
);
 
comm.execute();

ProxyはTweensy FXの時に久しく出てきて今回の使い方を気づいた。
思いもよらないところでつながることは良くある。何かを調べるってことは良いことだ。

インバースキネマティックを試す

2009 年 11月 6 日 金曜日 kosuke

Flash CS4で目玉機能の一つでありながら、なんとなく影が薄いインバースキネマティック。
その理由か否か試してみてわかったことで、スクリプトからはちょっと使いづらいなぁと思った。

試してみたのはこちら。なんだか夢に出てきそうな気持ち悪さ。

This movie requires Flash Player 10.0.0

rotation関連のスライダーは花の幹の部分の全ジョイントを変更。
IKMoverは花のアイコンを動かしています。

まず、インバースキネマティックの構造定義をアーマチュアといい、シンボルをつなぐ場合、表示オブジェクトが骨(ボーン)でつながった構造になります。ちなみにシェイプにボーンを通すことも出来る。


▲オーサリングツールで設定

アーマチェアはIKArmatureクラスで定義されるのだけど、これがスクリプトでは作れない。
Flashのオーサリングツールからしか作れないということ。

つまり、new IKArmature()とかして、スクリプトから表示オブジェクトをボーンでつないでいくことは出来ません。定義されたアーマチェアの構造を変更するのも無理。たとえばスクリプトでボーンを追加するとかも出来ない。


じゃあ、スクリプトで何が出来るかっていうとオーサリングツールで設定できるプロパティの変更とボーンの接続部(ジョイント)を移動させるとかです。
たとえば今回使った回転関連のプロパティは以下のように対応しています。

蛇足ですが、上のキャプチャで最小の-45°のところ、スクリプトで取得するとラジアンになって返ってくる。でもスクリプトで設定するときは角度。うーん…。

ik2

普通の使用方法ならスクリプトですることも大抵はポーズの変更をすることかと思う。
そうなるとポーズを作るのに単純なアニメーションはスクリプトでもと思いますが、いわゆる機械的じゃないアニメーションというか…規則的でないアニメーションはタイムラインで作り込んでいく方が効率いいってなるんだよなぁ。何か工夫を考えたい。