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

ランタイム共有ライブラリのさらなるローディング方法

2011 年 5月 28 日 土曜日 kosuke

Adobeのヘルプからもリンクが貼られているブログ記事で、Using a Custom Preloader Loop With TLF Textというエントリーを読みました。

TLFテキストは符号付きRSLが用意されていますが、独自のプリローダーで読込もうとするといろいろなエラーが出てうまく読込めません。
その対処法として、ActionScriptのクラスを書き出すフレームを2フレームにして、1フレーム目ではフレームスクリプトを記述し、RSLとSWF本体の読込みが完了したら2フレームに移動する方法が書かれています。1フレーム目にプリロードを持たせるってのは、昔からやってる人なら懐かしく思う方法なんじゃないでしょうか。

なるほど。そういう手もあるかと思いまして、前回のエントリでは、単体SWFでRSLを読込む時、Event.INITが発生するまで、SWF自身の容量を取得できませんでしたが、この方法ならいけるんじゃないかと。

しかも、先のページのサンプルだと、フレームスクリプトで、

this.addEventListener(RSLEvent.RSL_LOAD_COMPLETE, onRSLComplete);

なんて記述があって、えぇ?!そんなところでRSLの進行具合取れるの?と驚いたりもします。
ということで試してみました。今度もまた入り組んでいてわかりにくいのですが。

1.SWZをカスタムプリローダーで読込んでエラーを確認
まずサンプルをそのまま試してみます。1フレームにTLFTextFieldを配置。

ひとまずエラーを確認する為、クラスを書き出すフレームは1のままでカスタムプリローダーを使う設定だけします。

パブリッシュ。Flash Player 10 & 10.1をターゲットにしました。ご覧のようなエラー出る。コンパイル段階でも警告が出てます。そして1フレームに配置したTLFTextFieldが表示されません。このエラーはプリローダーを利用しない「コードにマージ」をした場合はもとい、デフォルトのプリローダーSWFを使った時は発生せず、TLFTextFieldも表示されることを確認しています。つまり、このエラーはカスタムプリローダーを設定したとき特有のもの。

2.jeffkamerer.comのフレームスクリプトを入れて確認
次にjeffkamerer.comの方法でエラー解消を確認する。TLFTextFieldを2フレームに移動。

クラスを書き出すフレームを2に変更。

パブリッシュすると2フレームで無事TLFTextFieldが表示される。traceは以下の通り。

1:Frame Script Start
ProgressEvent.PROGRESS: 36092 36092
Event.COMPLETE: 36092 36092
RSLEvent.RSL_PROGRESS: 1208 186404
•
•
•
RSLEvent.RSL_PROGRESS: 186404 186404
RSLEvent.RSL_LOAD_COMPLETE: 0 0

SWF自身のProgressEvent.PROGRESSが取得できている。SWF自身の読込みが完了してからRSLEvent.RSL_PROGRESSが動き始めているように見えるけど、これはSWFの容量が小さいからで、容量を大きくして、ダウンロードのシュミレートで見るとProgressEvent.PROGRESSとRSLEvent.RSL_PROGRESSは平行して実行されています。

3.通常のRSLを加えてみる
SWZのTLFを読込む確認ができたので、ここにRSLの読込みを加えてみる。前回のエントリーで使った、写真の枠のシンボル、PhotoTemplateをRSLに書き出したShare.swfを使います。
まず、SWFのライブラリにPhotoTemplateをRSL読込み設定をする。そのシンボルを2フレームに配置しました。

これをパブリッシュすれば2フレームに移動した後、Share.swfのRSLが表示されると思ったのですがダメでパブリッシュするとエラー。

これなんだか意味不明なエラーなんですけど。エラーに関していえばこれに始まったことではなく他にも不思議なエラーいろいろ出ますので…。そもそもTLFが普通にカスタムプリローダーで読込めないって話自体、意味不明なわけでこの位で挫けていてはRSLは使えません。
ここはひとまず、2フレームに配置したPhotoTemplateのインスタンスを削除します。そして前回同様、PhotoTemplateを入れ子にしたシンボル(PhotoContainer)をつくりActionScriptに書き出すとします。さらにこのままだと2フレームにPhotoTemplateは表示されないので、2フレームのフレームスクリプトでインスタンスを表示させるスクリプトを加えました。

import share.PhotoTemplate;
 
var photo:PhotoTemplate = new PhotoTemplate();
photo.x	= 250;
photo.y	= 300;
addChild( photo );

これでパブリッシュすると、エラーはでないのですが正しく動かない。1フレームから動かない状態になる。ブラウザでみるとShare.swfも読込んでいるし、trace結果はEvent.COMPLETEもRSLEvent.RSL_LOAD_COMPLETEも動いているのでgotoAndStop(2)が動かないという状況。ちなみに2フレーム目にいれたフレームスクリプトはこの不具合に関係ありません。消しても同様だった為。
もうこのあたりから、何が仕様で、正しく動いているのか、バグなのか、こちらのエラーなのかわからなくなってくるのですが…。
ひとまず解決策として、入れ子にしたPhotoContainerのActionScript書き出しで「2フレーム目に書き出し」のチェックを外します。

パブリッシュすると、今度は2フレーム目まで動き、TLFTextFieldが表示されます。しかし、2フレーム目に書いたフレームスクリプトインスタンスを追加してもShare.swfのもつRSLが表示されません。ブラウザで確認してみるとShare.swfを読込んでいない状況です。

4.RSLPreloaderを加えてみる
3の結果からそれなら前回使ったRSLPreloaderを使ってあらかじめShare.swfを読込んだらどうだろうと思いました。
その前に気になることがあります。1フレームに書いた以下のフレームスクリプトでSWZの読込みはイベントとして取得できています。

addEventListener(RSLEvent.RSL_PROGRESS, onRSLProgress);
addEventListener(RSLEvent.RSL_LOAD_COMPLETE, onRSLComplete);

だったら、通常のRSLも同じようにイベント取得できないか?
試しに3の状態からTLFTextFieldを削除して、1フレームにPhotoTemplateを配置してみたのですが…。表示はされるもののイベントは取得できずでした。また前回と同じでEvent.INITが発生するまでSWF自身の容量がわかりません。

更にPhotoContainerの「2フレーム目に書き出し」にチェックを入れた場合でも同様。この場合は2フレーム目に入らないのでShare.swfも読込まれないという状況です。

つまり、フレームスクリプトで書いた、

addEventListener(RSLEvent.RSL_PROGRESS, onRSLProgress);
addEventListener(RSLEvent.RSL_LOAD_COMPLETE, onRSLComplete);

で取得できるのは、SWZの場合のみのようです。SWZはユーザーがローカルへのキャッシュを許可していない場合、フェイルオーバー RSLという通常のRSLが代替えで読込まれますがこのRSLは上記のイベントで取得できました。(ややこしい。)

なので、符号付きRSLもRSLも読込む場合、符号付きRSLのプリロードは上記のイベントで取得し、RSLはRSLPreloaderでプリロードするってことになるんですかね?ともあれRSLPreloaderを試してみます。

RSLPreloaderでShare.swfを読込む。読込みの完了がどういう順で起きるかはわからないので、RSLPreloaderの完了フラグとしてpreCompleteを追加し、swfComplete と rslComplete をあわせて3つのフラグが処理されたらフレーム2に移動させる。

import fl.events.RSLEvent;
import fl.rsl.RSLInfo;
import fl.rsl.RSLPreloader;
 
import flash.events.Event;
import flash.events.ProgressEvent;
 
trace( "1:Frame Script Start" );
stop();
 
var rslPreloader:RSLPreloader;
var info:RSLInfo
 
var swfComplete:Boolean = false;
var rslComplete:Boolean = false;
var preComplete:Boolean = false;
 
loaderInfo.addEventListener(ProgressEvent.PROGRESS, onProgress);
loaderInfo.addEventListener(Event.INIT, onInit);
loaderInfo.addEventListener(Event.COMPLETE, onComplete);
 
addEventListener(RSLEvent.RSL_PROGRESS, onRSLProgress);
addEventListener(RSLEvent.RSL_LOAD_COMPLETE, onRSLComplete);
 
rslPreloader= new RSLPreloader();
info		= new RSLInfo();
info.addEntry( 'Share.swf' );
rslPreloader.addRSLInfo( info );
rslPreloader.addEventListener( RSLEvent.RSL_PROGRESS, onRslProgress );
rslPreloader.addEventListener( RSLEvent.RSL_LOAD_COMPLETE, onRslLoadComplete );
rslPreloader.start();
 
 
//SWFのロード確認リスナー
function onProgress(e:ProgressEvent):void{
	trace( "ProgressEvent.PROGRESS:", e.bytesLoaded, e.bytesTotal );
}
 
function onInit(e:Event):void{
	trace( "Event.INIT:", e.target.bytesLoaded, e.target.bytesTotal );
}
 
function onComplete(e:Event):void{
	trace( "Event.COMPLETE:", e.target.bytesLoaded, e.target.bytesTotal );
	loaderInfo.removeEventListener(ProgressEvent.PROGRESS, onProgress);
	loaderInfo.removeEventListener(Event.COMPLETE, onComplete);
	swfComplete = true;
	if(rslComplete && swfComplete && preComplete){
		gotoAndStop(2);
	}
}
 
//RSLのロード確認リスナー
function onRSLProgress(e:RSLEvent):void{
	trace( "RSLEvent.RSL_PROGRESS:", e.bytesLoaded, e.bytesTotal );
}
 
function onRSLComplete(e:RSLEvent):void{
	trace( "RSLEvent.RSL_LOAD_COMPLETE:", e.bytesLoaded, e.bytesTotal );
	removeEventListener(RSLEvent.RSL_PROGRESS, onRSLProgress);
	removeEventListener(RSLEvent.RSL_LOAD_COMPLETE, onRSLComplete);
	rslComplete = true;
	if(rslComplete && swfComplete && preComplete){
		gotoAndStop(2);
	}
}
 
//RSLPreloaderのロード確認リスナー
function onRslProgress( e:RSLEvent ):void{
	trace( "RSLPreloader RSL_PROGRESS:", e.bytesLoaded, e.bytesTotal );
}
 
function onRslLoadComplete( e:RSLEvent ):void{
	rslPreloader.removeEventListener(RSLEvent.RSL_PROGRESS, onRslProgress);
	rslPreloader.removeEventListener(RSLEvent.RSL_LOAD_COMPLETE, onRslLoadComplete);
	trace( "RSLPreloader RSL_LOAD_COMPLETE:", e.bytesLoaded, e.bytesTotal );
	preComplete = true;
	if(rslComplete && swfComplete && preComplete){
		gotoAndStop(2);
	}
}

これでやっと期待どおりに動いた。

traceを確認すると以下でした。

1:Frame Script Start
Event.INIT: 17931 17931
ProgressEvent.PROGRESS: 17931 17931
Event.COMPLETE: 17931 17931
RSLPreloader RSL_PROGRESS: 1024 3613
・
・
RSLPreloader RSL_PROGRESS: 3613 3613
RSLPreloader RSL_LOAD_COMPLETE: 0 0
RSLEvent.RSL_PROGRESS: 1208 186404
・
・
RSLEvent.RSL_LOAD_COMPLETE: 0 0

Progressが順番に並んで発生していますが、これもダウンロードのシュミレートで確認するとバラバラに発生するので、読込みが完了する順番は必ずしも一定ではありません。

さて、これで完璧と思ったのですが落とし穴が他にもありました。
それはドキュメントクラス。この状態でドキュメントクラスを設定しました。内容は以下。

package sample{
 
	import flash.display.MovieClip;
 
	public class Sample2 extends MovieClip{
 
		public function Sample2(){
			trace( "Document Class Sample1" );
		}
 
	}
}

コンストラクタでトレースを吐いている以外、何もしていません。
さて、この「クラスを書き出すフレーム」を2フレームに設定したflaファイルで、ドキュメントクラスを設定した場合、コンストラクタのトレースはいつ実行されるでしょうか?

確認してみると、一番最初。1フレームのフレームスクリプトでtraceしている「1:Frame Script Start」よりも前に実行されます。
そのドキュメントのコンストラクタってことだから、冷静に考えるとこれでいいような気もしますが、「クラスを書き出すフレーム」との整合性がわかりにくいです。しかも、このドキュメントクラスを設定すると、これまた正しく2フレームに移動しません…。
traceを確認すると

rslPreloader.addEventListener( RSLEvent.RSL_PROGRESS, onRslProgress );
addEventListener(RSLEvent.RSL_LOAD_COMPLETE, onRSLComplete);

のイベントのRSLEvent.RSL_LOAD_COMPLETEが戻ってきていないのでこれが原因のようなのですが、解決策的にはRSLEvent.RSL_PROGRESSの完了で判定してもダメで、(VerifyError: Error #1014: クラス flashx.textLayout.container::TextContainerManager が見つかりません。となる。)
今のところ解決策的にはドキュメントクラスを使わず、コンテナとなるインスタンスを2フレームで追加する方法。

こんな感じで、コンテナとなるクラスを作って、

package sample{
 
	import flash.display.Sprite;
	import share.PhotoTemplate;
 
	/** 2フレームで追加されコンテナとなるクラス。 */
	public class Container extends Sprite{
 
		public function Container(){
			var photo:PhotoTemplate = new PhotoTemplate();
			photo.x	= 250;
			photo.y	= 300;
			addChild( photo );
		}
	}
}

2フレームのフレームスクリプトで配置する。

import sample.Container;
 
var container = new Container();
addChild( container );

これだとうまくいっているけど…。僕はよくProgressionを使っているので、ドキュメントクラスを使えないのはいたいな。
やはり一筋なわではいかないか。RSL。

蛇足ですが、キャッシュされたSWZは、それぞれ以下にあります。

Windows 95/98/ME/2000/XP
C:¥Documents and Settings¥user_name¥Application Data¥Adobe¥Flash Player¥AssetCache¥

Windows Vista
C:¥Users¥user_name¥AppData¥Roaming¥Adobe¥Flash Player¥AssetCache¥

Linux
/home/user_name/.adobe/Flash_Player/AssetCache/

Mac OSX
/Users/user_name/Library/Cache/Adobe/Flash Player/AssetCache/

キャッシュを消して読込みを確認するには上記ディレクトリの中のファイルを削除します。
またSWZではなく、フェイルオーバー RSLを試すには、
Adobe – Flash Player : 設定マネージャー – グローバルストレージ設定パネル
で、共通のFlashコンポーネントを格納して、ダウンロード回数を削減しますのチェックを外したり、記憶容量を0にするなどすると確認出来ました。

ランタイム共有ライブラリを調べる

2011 年 5月 27 日 金曜日 kosuke

Flashには「ランタイム共有ライブラリ(RSL)」という機能がある。
これはAS2のころからある機能でSWFの実行時にあるSWFがもつライブラリを他のSWFから利用するもの。
たとえば、A.swfでもB.swfでも利用するシンボルをShare.swfでRSLとして登録し、実行時に参照する。
するとA.swfとB.swfは自身のライブラリに同じシンボルのデータを持つ必要がなくなり、結果的にファイル容量が軽量化しダウンロードにかかる時間が短縮されたり、一意に修正をすることが出来たりするわけです。

便利そうな機能なのだけど、これが以外に使われない。RSLにすることで、(単一ファイルではありえない)読込みエラーなどが発生する機会が増えるなど構造的なデメリットもあるのだけど、一番の理由は読込みのタイミングがうまく制御できないからではないかと思っている。制御に関してはっきり挙動がわかる資料が無かった。最近は事情が変わってきて、RSLを進化させFlashPlayerにキャッシュするアドビ公式のランタイム共有ライブラリとでも言おうか。符号付きRSLであるSWZ形式のファイルが登場しRSL読込み用のクラスなども実装されてきた。

しかし、昔RSLを試したことあるけど…って方は鼻っからRSL使い物になる?と疑念もあってあまり関心がなかったりしないだろうか。(←早い話、僕。)そこで実際仕事で使いたかったって事情もあり調べてみた。

例として下のような、フチあり写真のようなシンボルがあり、このシンボルをRSLとして他のSWFから使うものとします。

ランタイム共有用に書き出しをチェックして、SWFを書き出します。SWFはShare.swfとしました。

RSLを利用する側のRSLTest1.flaではRSLの読込みを設定します。挿入→新規シンボルで空のシンボルを作り、ランタイム共有用に読込みをチェック。URLにShare.swfを指定します。(今回の場合、RSLTest1.flaを書き出したSWFとShare.swfは同じディレクトリに配置します。)

ここでは「いいえ」で。

タイムラインに適当に配置してみる。

パブリッシュ。

これでRSLに書き出したシンボルを読込み、表示するまでの確認ができました。
今はタイムラインに配置していますがクラスベースで開発する時は、通常インスタンスはスクリプトで制御して表示します。そこでタイムラインに配置したシンボルはすべて削除してドキュメントクラスからRSLを使う場合の検証をしました。

package rsl{
 
	import flash.display.MovieClip;
	import flash.events.Event;
	import flash.events.ProgressEvent;
	import share.PhotoTemplate;
 
	public class RSLTest1 extends MovieClip{
 
		public function RSLTest1(){
			loaderInfo.addEventListener( Event.OPEN, onOpen );
			loaderInfo.addEventListener( ProgressEvent.PROGRESS, onProgress );
			loaderInfo.addEventListener( Event.INIT, onInit );
			loaderInfo.addEventListener( Event.COMPLETE, onComplete );
		}
 
		private function onOpen(e:Event):void{
			trace("RSLTest1 OPEN:", loaderInfo.bytesLoaded, loaderInfo.bytesTotal );
		}
 
		private function onProgress(e:ProgressEvent):void{
			trace("RSLTest1 PROGRESS:", e.bytesLoaded, e.bytesTotal );
		}
 
		private function onInit( e:Event ):void{
			trace( "RSLTest1 INIT:", loaderInfo.bytesLoaded, loaderInfo.bytesTotal );
			var temp:PhotoTemplate = new PhotoTemplate();
			addChild( temp );
		}
 
		private function onComplete( e:Event ):void{
			trace( "RSLTest1 COMPLETE:", loaderInfo.bytesLoaded, loaderInfo.bytesTotal );
		}
	}
}

ドキュメントクラスのSWF(RSLTest1.swf)のloaderInfoを監視しアクセス可能(Event.INIT)となったらPhotoTemplateのインスタンスを生成し表示リストに加える。表示されましたか?この検証では表示されませんでした。
traceは以下になっています。

Safariの構成ファイルなど読込んでいるファイルが見えるもので確認するとわかるのですがそもそもShare.swfが読込まれていません。

ランタイム共有用に読込みのシンボルはActionScript用に書き出しがチェックできない為、タイムラインに配置なしだとコンパイルに含まれないのだと思われます。そこでPhotoTemplateを含むシンボルを生成してActionScript用に書き出しをチェックしてみます。

今度はShare.swfは読込まれ、PhotoTemplateのインスタンスも表示されました。

しかし、今回はtraceは妙な出力をしています。

ProgressEvent.PROGRESSとEvent.COMPLETEが返ってこない。

ちなみにActionScript用に書き出しをやめて、先ほどのタイムラインに配置した状態でこのドキュメントクラスを使った場合も、PhotoTemplateのインスタンスは表示されますがProgressEvent.PROGRESSとEvent.COMPLETEが返ってきません。

ここまでの様子をまとめると、ランタイム共有ライブラリが読込まれる場合、そのドキュメントクラスでは、

・ProgressEvent.PROGRESSとEvent.COMPLETEは返ってこない。
・Event.INIT以降、ランタイム共有ライブラリにアクセス可能。
・loaderInfoのbytesLoadedとbytesTotalに、ランタイム共有ライブラリのSWFのファイルの容量は含まれない。

ものと思われる。
とすると、Flashで開発する場合にランタイム共有ライブラリを使うなら、RSLを読込むSWF単体にそれぞれについては、初期化時のEvent.INITを起点に開発することさえ気をつければ問題なさそうに思います。

しかしこのままだとRSLTest1.swfはEvent.INITがくるまで何のアクションもなく待ち続けることになる。そこでRSLTest1.swfとShare.swfの読込みを管理する為のプリローダー(Preloader1.swf)を考えてみる。

まず、RSLの読込みに使えるクラスがある。

RSLPreloader

RSLPreloaderは、SWZも含めてRSLのプリロードを管理するクラス。
Preloader1.swfがShare.swfのRSLをライブラリに読込んでいると意味がないのでPreloader1.swfではRSLを利用しないこと。

ちなみにActionScript用に書き出しがない状態でRSLを読込んでいる場合(ドキュメントクラスを使った最初の検証の場合)、RSLTest1.swfでRSLPreloaderクラスを使ってローディングしてもうまくいく。

package rsl{
 
 
	import fl.events.RSLErrorEvent;
	import fl.events.RSLEvent;
	import fl.rsl.RSLInfo;
	import fl.rsl.RSLPreloader;
 
	import flash.display.Loader;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.ProgressEvent;
	import flash.net.URLRequest;
 
 
	public class Preloader1 extends Sprite{
 
		private var rslPreloader:RSLPreloader;
		private var loader:Loader;
 
		public function Preloader1(){
			super();
			rslPreloader 			= new RSLPreloader();
			var info:RSLInfo	= new RSLInfo();
			info.addEntry( 'Share.swf' );
			rslPreloader.addRSLInfo( info );
			rslPreloader.addEventListener( RSLErrorEvent.RSL_LOAD_FAILED, onRslLoadFailed );
			rslPreloader.addEventListener( RSLEvent.RSL_PROGRESS, onRslProgress );
			rslPreloader.addEventListener( RSLEvent.RSL_LOAD_COMPLETE, onRslLoadComplete );
			rslPreloader.start();
 
			loader	= new Loader();
			addChild( loader );
			loader.contentLoaderInfo.addEventListener( Event.OPEN, onOpen );
			loader.contentLoaderInfo.addEventListener( ProgressEvent.PROGRESS, onProgress );
			loader.contentLoaderInfo.addEventListener( Event.INIT, onInit );
			loader.contentLoaderInfo.addEventListener( Event.COMPLETE, onComplete );
			loader.load( new URLRequest( "RSLTest1.swf" ) );
		}
 
 
		private function onRslLoadFailed( e:RSLErrorEvent ):void{
			trace( "RSL_LOAD_FAILED:"  );
		}
 
		private function onRslProgress( e:RSLEvent ):void{
			trace( "RSL_PROGRESS:", e.bytesLoaded, e.bytesTotal );
		}
 
		private function onRslLoadComplete( e:RSLEvent ):void{
			trace( "RSL_LOAD_COMPLETE:", e.bytesLoaded, e.bytesTotal );
		}
 
 
		private function onOpen(e:Event):void{
			trace("Preloader1 OPEN:", loader.contentLoaderInfo.bytesLoaded, loader.contentLoaderInfo.bytesTotal );
		}
 
		private function onProgress(e:ProgressEvent):void{
			trace("Preloader1 PROGRESS:", e.bytesLoaded, e.bytesTotal );
		}
 
		private function onInit(e:Event):void{
			trace("Preloader1 INIT:", loader.contentLoaderInfo.bytesLoaded, loader.contentLoaderInfo.bytesTotal );
		}
 
		private function onComplete(e:Event):void{
			trace("Preloader1 COMPLETE:", loader.contentLoaderInfo.bytesLoaded, loader.contentLoaderInfo.bytesTotal );
		}
 
 
	}
}

trace出力は以下だ。

Share.swfのプログレスが取得できていることと、RSLTest1.swfのプログレスも取得できていることが確認できる。
さらにRSLTest1.swf単体だと動かなかった、Event.COMPLETEも無事動いている。

RSLPreloaderは、APIドキュメントではランタイム10.1以上ってことなんだけどどうなんだろう。サンプルはPlayer 9書き出しにして、Player 9(9.0.47.0)で見てみましたが動いていますね。。。SWZとか完全に動かないよってことなのかな。

他にFlex SDKで、
PreloaderDownloadProgressBar
を使う方法も試した。この方法でもうまく読込めます。

Preloader2.as

package rsl{
 
 
	import fl.rsl.RSLInfo;
 
	import flash.display.Loader;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.ProgressEvent;
	import flash.net.URLRequest;
 
	import mx.preloaders.Preloader;
 
 
 
	/** RSLの読込みをFlexのPreloaderで行ってみる。 */
	public class Preloader2 extends Sprite{
 
		private var rslPreloader:Preloader
		private var info:RSLInfo
		private var loader:Loader;
 
		public function Preloader2(){
			super();
 
			rslPreloader	= new Preloader();
			addChild( rslPreloader );
			rslPreloader.initialize( true, CustomProgressBar, 0x000000, 1, null, "", 1, 1, [ "Share.swf" ] );
 
			loader	= new Loader();
			addChild( loader );
			loader.contentLoaderInfo.addEventListener( Event.OPEN, onOpen );
			loader.contentLoaderInfo.addEventListener( ProgressEvent.PROGRESS, onProgress );
			loader.contentLoaderInfo.addEventListener( Event.INIT, onInit );
			loader.contentLoaderInfo.addEventListener( Event.COMPLETE, onComplete );
			loader.load( new URLRequest( "RSLTest1.swf" ) );
		}
 
 
 
		private function onOpen(e:Event):void{
			trace("Preloader1 OPEN:", loader.contentLoaderInfo.bytesLoaded, loader.contentLoaderInfo.bytesTotal );
		}
 
		private function onProgress(e:ProgressEvent):void{
			trace("Preloader1 PROGRESS:", e.bytesLoaded, e.bytesTotal );
		}
 
		private function onInit(e:Event):void{
			trace("Preloader1 INIT:", loader.contentLoaderInfo.bytesLoaded, loader.contentLoaderInfo.bytesTotal );
		}
 
		private function onComplete(e:Event):void{
			trace("Preloader1 COMPLETE:", loader.contentLoaderInfo.bytesLoaded, loader.contentLoaderInfo.bytesTotal );
		}
 
 
	}
}

Preloaderから利用するProgressBarを用意。DownloadProgressBarを継承して各メソッドを上書きすればよい。

package rsl{
 
 
	import mx.events.RSLEvent;
	import mx.preloaders.DownloadProgressBar;
 
 
	public class CustomProgressBar extends DownloadProgressBar{
 
 
		public function CustomProgressBar(){
			super();
		}
 
 
		protected override function rslProgressHandler(event:RSLEvent):void{
			trace( "rslProgressHandler:", event.bytesLoaded, event.bytesTotal );
		}
 
 
		protected override function rslCompleteHandler(event:RSLEvent):void{
			trace( "rslCompleteHandler", event.bytesLoaded, event.bytesTotal );
		}
 
 
	}
}

trace出力はこんな感じ。

一応、このプリローダーからRSLとRSLを使うSWFを読込む方法なら、読込みのタイミングを処理できるんじゃないかと思う。
一応としているのは、SWZでないRSL、つまりユーザーが自由につくれるRSLはプレイヤーにはキャッシュされない。キャッシュされるのはブラウザのキャッシュであるということ。この当たりを読むとわかる。

Flash Playerキャッシュの使用によるFlexアプリケーション性能の改善
フレームワーク RSL の使用

ってことはやっぱり上のプリローダーを使った場合も、RSLPreloaderを先に処理しても、RSLTest1.swfを読込んだ後、RSLTest1.swfがRSLをリクエストするんじゃないかと。実際Safariの構成ファイルを見ると2度Share.swfが読込まれているので。2回目はキャッシュを参照するから瞬間的に読込まれるものと思われるけど、キャッシュを消していたら読込みに時間がかかるんじゃないかなと思っている。これはもうしょうがないんじゃないかと思う。サイト全体としてキャッシュ消された時の対策までを考えるなら、あるSWFを読込む時、そのSWFがRSLを利用しているものならRSLPreloaderで毎回プリロードかけるとか、プログレス表示なしでいいならEvent.INITを待つとか…。やっぱり今もRSLを使うことで手間になることはあると思う。

さてそんなRSLですが調べるきっかけとなった状況がありました。僕が直面したのは仕事で16ファイルのSWFを作る時。この16ファイルには共通するシンボルがあるのだけれど、モック段階から進めて作っていく為、アップデートを重ねる度にシンボルを16のflaファイル変更しなくちゃいけない。この時はオーサタイム共有ライブラリにして対応したので差し替えは半自動的に行っていたのですが、それでも16ファイルパブリッシュするとかってばからしいじゃない。試行錯誤の段階においては。
この時は結局RSLは使わなかったのですが、今度同じような時があったらRSLが使えるか調べておこうと思いました。
Flashで作るならアプリケーションで素早くデザインが見える・作れるというのが利点なので、最後にこの利点を踏まえてRSLの使い方を自分なりにまとめておきたい。

この記事では最初にPhotoTemplateというシンボルをShare.swfに作り、RSLTest1.swfでは空のシンボルを作ってRSL読込みの設定をしましたが、空のシンボルである必要はありません。なので、サイズは固定されているけど、中身の要素やデザインは変わっていくなんて時には途中段階のデザインでシンボルにし、RSL読込みを設定、最新のデザインはRSLから読込まれるようにしておくという方法が便利かと思いました。

先ほど作ったPhotoTemplateシンボルを編集する。今回は「はい」として一旦、RSL読込み設定を解除。

その時点の仮デザインを入れる。入れ子のシンボルなどに使う場合、仮のデザインでも有無でこの後の作業の進み具合が全然違ってくるものです。

RSL読込みがはずれていますので再設定。

そのシンボルをステージにおいてパブリッシュしてみると、きちんとShare.swfが書き出しているRSLで表示されます。

ProgressionでFlash Player判定後、無かったらリダイレクトする場合。

2011 年 2月 9 日 水曜日 kosuke

これよりもっと手前でコールバック関数に別のものを指定するなどでもよいですが、なるべく元のソースを変更せずリダイレクトするならprogression_unpack.jsの以下の部分を修正すればいいと思う。

progression_unpack.js

var complete = function( e ) {
	$p( $( "disabled_javascript" ), { style:{ display:"none" } } );
 
	if ( e.success ) {
		$render( config );
 
		var target = d.getElementById( config.attributes.id );
 
		if ( target ) {
			target.style.outline = "none";
			target.focus();
		}
	}
	else {
		//ここに追記
		window.location = 'リダイレクト先のURL';
		$css( "#" + config.htmlContentId, { display:"block" } );
	}
};

上の部分の修正をしてprogression.jsとして使えばOK。
このJavaScriptによる実行(JavaScriptによる判定、expressinstall.swf実行結果の正否)なので、JavaScript自体が無効の場合、リダイレクトしない。なのでリダイレクトの場合もhtmlにリンクを設置しておくべきでしょう。

iPhoneアプリ トイレの音消しアプリ ToiletTime をリリースしました。

2011 年 1月 4 日 火曜日 kosuke

トイレに入っているときの消音アプリを作りました。いわゆる音姫アプリ。
packager for iphoneで作ったiPhoneアプリです。再生時間を100件まで記録し平均時間を算出する少しジョークな機能がついています。サウンドを3つの中から選べます。

ToiletTime - nipx




ホリデーシーズン前に申請したのですが、直後にiTunesConnectの一時停止もあって予想通りというか今日やっと公開の連絡いただきました。

音の種類で凄くジョークなものを思いついたのですけど、多分審査通らないだろうなということで普通に使える流水音と鳥の鳴き声、ジョークでししおどしを選べるようにしました。

このアプリはAndroid版も作りました。
Flex SDK 「Hero」のコンポーネントを使わず作った場合、1ソースでどの程度iPhoneとAndroidに対応できるか試した部分もあるので時間があれば記事にまとめようかと思ってます。

Androidアプリ ToiletTime をリリースしました。

2011 年 1月 4 日 火曜日 kosuke

トイレに入っているときの消音アプリを作りました。
Air for Androidで作ったAndroidアプリです。iOS版も作っています。
アプリの内容はiOS版も同じなので次のエントリーで説明します。

ToiletTime


(さらに…)

iPhoneアプリ Picasaを表示するデジタルフォトフレーム ni-frame(ニフレーム)をリリースしました。

2010 年 12月 22 日 水曜日 kosuke

packager for iphoneを使ってiPhoneアプリを作りました。
Picasaウェブアルバムを表示するデジタルフォトフレームアプリです。
packager for iphoneがRetina displayに対応していないという点を逆手にとって買い替えで不要になったiPhone3GとかiPodTouch第二世代などデジタルフォトフレームに活用してはいかがでしょうか。
表示するPicasaのアカウントとログインするPicasaアカウントを設定できるので友人や家族があなたに共有しているアルバムを表示できるように出来ています。

ni-frame - nipx





9月に規約が変更されたとはいえ、packager for iphoneで制作ということでちゃんとリリースまでたどり着けるのか半信半疑でしたが(実際別の理由で一度リジェクトされましたので)無事リリースできて良かったです。

使ってみてわかったことですがpackager for iphoneはまだまだ未熟な部分があって、UIKitに限らず利用できないiPhoneのAPIが沢山あるということ。たとえばメールアプリに添付するとか、カメラロールを参照するとか、カメラを利用するとか、え、これも実装されていないの?っていう部分が多いです。

これは単純に未完なのかもしれないし、Appleとの関係で実現できなかったのかもしれないし、まったく別の理由なのかもしれない。というのも興味深いと思うことがあって、airの1ソースで複数プラットフォームに対応という理念がandroid向けとも思えるair2.5ではクロスプラットフォームの基本は守りつつも、それに執着するよりは特定プラットフォームに特徴的な機能には専用のAPIを用意するような方針になってきていると思えること。
もしそうなら開発を再開した(という)packager for iphoneは今後どうなるんだろう?

いずれにしても特にUIパーツは1から作るのがあまりに効率的でないことが多いので、次期Flex SDKで採用されるモバイルプロジェクト用コンポーネントやクラスライブラリがそのままの形でiPhoneでも使えるようになるのか、iPhone用にUIKitへアクセスできるAPIが実装されるかなったらいいなと思う。希望的には両方実装してくれと願うが。

Appleがクロスコンパイラを禁止した時の理由の一つにサイドパーティの開発環境に依存して最新のSDKの機能がなかなか浸透しないといった理由がありましたが実際に使ってみるとその憂いも理解出来ると思った。反面、次期Flex SDKのandroid向け機能の完成度を見ると、そんなにFlashを嫌っていなければpackager for iphoneはもっと完成された状態だったんじゃないかと思えちゃうのだよね。

Appleは今も決して積極的にpackager for iphoneのような開発環境を良く思っているように思えないし、今だFlash Playerすら許可される気配すらないわけですけどいい加減利用できるようにしてもいいんじゃないでしょうかね…。
そしてadobeももう一度本腰をいれてpackager for iphoneを開発してくれたらなと思う。

なんでかっていったらiPhoneとandroidを比べたらやっぱりプロダクトとしてはiPhoneが数段好きだ、だけども開発はActionScriptで作りたい。俺は。

packager for iphoneでprogressionを使う時の俺的プロジェクト設定。

2010 年 12月 14 日 火曜日 kosuke

air 2.5の公開でandroidアプリ開発の話題が過熱する中、すっかり忘れ去られた感のあるpackager for iphoneですが最近いろいろ試しています。

packager for iphoneでは新しいFlex SDK Heroのmobile用クラスライブラリやコンポーネントが使えない上、iOSが元々備えるUIKitも使えない。
これはつまりボタンもスクロールバーもなにかもすべて1から自分で作らなきゃいけないってことを意味します。
といってもWebコンテンツだと大抵の場合ボタンやスクロールバーといったUIは毎回のように作ることが多い訳で普段通りだぜ!と思うのですが、スマートフォンアプリの場合、小さい画面ゆえの切り替えの多さ、タッチパネルゆえのユーザーアクションの多さ・煩雑さが相まってはるかに面倒です。
とくにHeroをかじった後なんかだとあらかじめ用意されているコンポーネントの有り難みがよく解る感じ。

さらにpackager for iphoneでは使えないiPhoneデバイス機能が沢山あって、これならandroidのほうがとか、似たような技術でもJSでUIKitにアクセスできるってことでこれまた旬な感じのtitanium mobileなんかのほうが魅力的だったりとか…。あちこちに目移りしてしまいますね。

で思うに比較していくと現状のpackager for iphoneはそれほど魅力的でないものになって行っちゃうので、使うなら普段作りなれているスタイルをそのまんま使って作るっていうメリットを十分に活かしていく感じがいいんじゃないかと。Heroのモバイル機能使って作るandroidアプリですら普段作りなれている方法っていうのと変わってくるんじゃないかなと思うところもありますので。

ということで、僕の場合ウェブコンテンツなら大抵の場合Progressionで囲っちゃうので、Progressionありきでpackager for iphoneのプロジェクトをセットアップする手順を書いておきます。

1)アプリケーション名でフォルダを生成。

2)Progression プロジェクトパネルから新規プロジェクトを生成。

基本タブ
プロジェクト名:アプリケーション名
書き出し先:先に生成したアプリケーション名のフォルダ
種類:クラス
プレイヤー:AIR 2.0
環境設定:AIRアプリケーション

Flashタブ
幅:320
高さ:480
フレームレート:12〜20(iPodTouch第2世代だと安定するのこのあたり)

ActionScriptタブ
メインクラス名:アプリケーション名
FlashBuilder(Eclipse)に対応させるにチェック

拡張ライブラリ
SWFProfilerにチェック(一応。実機確認ではStatsとか半透明で重ならないものがパフォーマンス低下少ない。)

3)Progressionが書き出したファイルをリネームなどする。

index.fla → アプリケーション名.fla

パブリッシュ設定を変更
形式タブ
Flashにチェック、../bin-debug/index.swf → アプリケーション名.swf

Flashタブ
Player:iPhone OS
ここで一度パブリッシュ設定を閉じる。

iPhone OS 設定
一般タブ
出力ファイル:アプリケーション名.ipa
アプリーエション名:アプリケーション名

デプロイタブ
.p12証明書、プロビジョニングプロファイルを設定
アプリケーションID:逆読みドメイン(jp.nipx) + アプリケーション名(プロビジョニングプロファイルに設定していれば自動的に入る。)

OKを押してパブリッシュ設定を完了。
bin-debugに、アプリケーション名-app.xmlが出来る。

4)FlashBuilderでプロジェクトを生成

ファイル / Flex プロジェクトを読み込み → プロジェクトフォルダの参照から生成したフォルダを指定 → 終了 → キャンセル(なぜか終了を押してもウインドウが閉じないので。)

パッケージエクスプローラーにプロジェクトが出来ているのでプロジェクトを右クリック。
プロジェクトタイプを追加または変更 → Flex プロジェクトタイプを追加 → 次へ(SDKは4.0以上を選択する) → 終了

再度プロジェクトを右クリック。
プロジェクトタイプを追加または変更 → Flex デスクトッププロジェクトに変換(Adobe AIRで実行)

プロジェクトのプロパティを設定。

Flex コンパイラー
非埋め込みファイルを出力フォルダにコピー、アクセス可能な SWF ファイルを生成する、警告を有効にする のチェックを外す。

Flex ビルドパス
ソースパスにlibsを追加。

5)ファイルの削除

プロジェクト、自動的にビルドのチェックを外す。
src内、mxmlとapp.xmlを削除。
bin-debug内のアプリケーション名-app.xmlをsrcフォルダにコピー。
bin-debugからアプリケーション名-app.xml以外削除。

アプリケーション名.asを右クリック。デフォルトアプリケーションに設定。

6)実行/デバックを設定をする。

プロパティ → 実行/デバックの設定

コマンドライン引数:-screensize iPod
プロファイル:mobileDevice

これでflaファイルからパブリッシュ出来るし、FBからデバック・実行可能です。
シンボルはflaファイルのライブラリに直接作るんじゃなくて、別のflaからswc書き出しするなり、Flexコンポーネントに書き出すなり、Flexのメタタグ使うなどして作る。flaファイルに直接作っちゃうとFBでデバックできなくなるので。お好みでlibsの中のProgressionの各パッケージを削除してswcファイルのProgression4_ext_AIR.swcに差し替えておくとSWFの書き出しが早くなる。

デバイスフォントに透明度を設定

2010 年 12月 11 日 土曜日 kosuke

Flash Player 10未満でパブリッシュした場合、デバイスフォントのテキストフィールドを半透明にしようとalphaを設定しても透明度は反映されません。テキストフィールドの親表示オブジェクトのalphaを設置しても同じです。たとえば以下のソースはFlash Player 9書き出しだと半透明になりません。

デバイスフォントでalpha = 0.5。Flash Player 9書き出し

var field1:TextField	= new TextField();
field1.x		= 20;
field1.y		= 20;
addChild( field1 );
var format:TextFormat	= new TextFormat();
format.color		= 0x000000;
format.size		= 14;
format.align		= TextFormatAlign.CENTER;
field1.defaultTextFormat= format;
field1.width		= 280;
field1.multiline	= false;
field1.wordWrap		= false;
field1.embedFonts	= false;
field1.defaultTextFormat= format;
 
var field2		= new TextField();
field2.defaultTextFormat= format;
field2.width		= 280;
field2.multiline	= false;
field2.wordWrap		= false;
field2.embedFonts	= false;
field2.defaultTextFormat= format;
var sprite:Sprite	= new Sprite();
sprite.x		= 20
sprite.y		= 60
sprite.graphics.lineStyle( 2 );
sprite.graphics.drawRect( 0,-2,280,22 );
addChild( sprite );
sprite.addChild( field2 );
 
field1.alpha	= 0.5;
sprite.alpha	= 0.5;	
 
field1.text	= "field1.alpha=" + field1.alpha + " / ver." + this.loaderInfo.swfVersion;
field2.text	= "sprite.alpha="+ field1.alpha + ", field2.alpha=" + field2.alpha + " / ver." + this.loaderInfo.swfVersion;

Flash Player 10以降ではこの問題がついに解決されて、デバイスフォントでも半透明に表示されます。

デバイスフォントでalpha = 0.5。Flash Player 10書き出し

しかし以下のようにColorMatrixFilterが利用できるなら、環境によって文字が少し滲んだりcacheAsBitmapが有効になったりとFilter利用の弊害はありますが、Flash Player 10未満でもデバイスフォントの透明度を変更できます。

ColorMatrixFilterで半透明

//field1.alpha	= 0.5;
//sprite.alpha	= 0.5;	
 
var filter:ColorMatrixFilter	= new ColorMatrixFilter();
var mat:Array			= filter.matrix
mat[ 18 ]			= 0.5;
filter.matrix			= mat;
field1.filters			= [ filter ];
sprite.filters			= [ filter ];
 
field1.text	= "field1.alpha=" + field1.alpha + " / ver." + this.loaderInfo.swfVersion;
field2.text	= "sprite.alpha="+ field1.alpha + ", field2.alpha=" + field2.alpha + " / ver." + this.loaderInfo.swfVersion;

 

ColorTransformメモ

2010 年 11月 2 日 火曜日 kosuke

自分の為にメモ。それぞれ白黒を指定した時、ColorTransformの各色プロパティがどうなっているか。

new ColotTransform();

redMultiplier	= 1
greenMultiplier	= 1
blueMultiplier	= 1
alphaMultiplier	= 1
redOffset	= 0
greenOffset	= 0
blueOffset	= 0
alphaOffset	= 0

var color:Color = new Color();
color.brightness = -1;

redMultiplier	= 0
greenMultiplier	= 0
blueMultiplier	= 0
alphaMultiplier	= 1
redOffset	= 0
greenOffset	= 0
blueOffset	= 0
alphaOffset	= 0

var color:Color = new Color();
color.brightness = 1;

redMultiplier	= 1
greenMultiplier	= 1
blueMultiplier	= 1
alphaMultiplier	= 1
redOffset	= 255
greenOffset	= 255
blueOffset	= 255
alphaOffset	= 0

Tweener.addTween( target, { _brightness:-2.55 } );

redMultiplier	= 1
greenMultiplier	= 1
blueMultiplier	= 1
alphaMultiplier	= 1
redOffset	= -255
greenOffset	= -255
blueOffset	= -255
alphaOffset	= 0

Tweener.addTween( target, { _brightness:2.55 } );

redMultiplier	= 1
greenMultiplier	= 1
blueMultiplier	= 1
alphaMultiplier	= 1
redOffset	= 255
greenOffset	= 255
blueOffset	= 255
alphaOffset	= 0

Tweener.addTween( target, { _tintBrightness:-1 } );

redMultiplier	= 0
greenMultiplier	= 0
blueMultiplier	= 0
alphaMultiplier	= 1
redOffset	= 0
greenOffset	= 0
blueOffset	= 0
alphaOffset	= 0

Tweener.addTween( container, { _tintBrightness:1 } );

redMultiplier	= 0
greenMultiplier	= 0
blueMultiplier	= 0
alphaMultiplier	= 1
redOffset	= 255
greenOffset	= 255
blueOffset	= 255
alphaOffset	= 0

beautiful people

2010 年 10月 22 日 金曜日 kosuke

ファッションブランド beautiful people のサイトを作りました。
2010.10.18に東京ミッドタウンで行われたJapan Fashion Week in TOKYOのインスタレーションショーの告知及びレポートサイトです。

beautiful people
http://www.beautiful-people.jp
ArtDirection / Design : Munehiro Machida.nssgraphica
Flash & HTML : KOSUKE,Nakamura.nipx
Photo : Motofumi Sannomiya, Fumihito Katamura, Takahiro Motonami

アートディレクションは今年何かとよく絡ませてもらっている、nssgraphica 町田さん。サイトに掲載されているだけでも400枚以上ある写真は3人のカメラマンがイベント当日に撮影しています。僕はFlashを担当させていただきました。

企画段階から絡ませていただいていて一般参加型のショーということでWEBではイベント当日までのカウントダウンを表示しつつtwitterのタイムラインを表示。
イベント翌日に写真を公開する運用方法などイベントを盛り上げるアイデアを出し合って進めたプロジェクトです。撮影から公開までうまくいって良かったですね。皆様おつかれさまでした。

beautiful peopleを着たモデルさん。イベント中は自由に撮影出来ました。カメラを向けると笑ってくれる。

詳細写真にディープリンクを設置し、twitterかメールでシェア出来るようにしています。

告知ページはFlashを使わずAjaxで構築しました。