« August 2006 | Main | November 2006 »

October 15, 2006

ビットマップキャッシュ(JP)

 MovieClip.cacheAsBitmapはFlashがムービークリップ(MC)の表示されている状態を内部にビットマップとしてキャッシュさせるプロパティである。
ではどういう場合にキャッシュ機能を使えば良いのだろうか。ベクター形式の背景画像上で他の要素をアニメーションさせると速度が低下するので、背景画像に対してopaqueBackground=trueとする。背景画像はビットマップとしてレンダリングされるので高速にアニメーションを行うことが出来る。テキストフィールドで大量のテキストをスクロールさせる時はMCの中にテキストフィールドを配置し、MCのscrollRectにRectangleを設定することで中に配置されたテキストフィールドのスクロール速度を改善できる。中に配置されたコンテンツはMCのscrollRectのサイズでビットマップ化されたデータが使われる。通常mainStageに対して画面全体が描画される。bitmapキャッシュを使用すると個々のMCはbitmap化された状態で既にグラフィックがキャッシュされるので再計算する負荷が減るというわけである。逆にパフォーマンスを低下させることもある。この機能は処理を高速化する反面ビットマップデータとしてキャッシュするためメモリ消費が大きくなるので、250x250ピクセルのデータの場合メモリ消費は1KBから250KBへと増大する。キャッシュした画像を拡大すると大量のメモリ消費につながる。また、MC自体が大きくグラフィックが変更するようなアニメーションなどを持つ場合性能に悪影響となる場合がある。

October 14, 2006

名前空間(JP)

前回はXMLの名前空間という考え方について説明した。名前空間が定義されていれば同じタグ要素であっても別ものとして扱うことができた。この名前空間の概念はActionScript3ではクラスの中にも及び、名前空間と結びつけられたクラス内の要素は同じ名前であっても別のものとして扱うことができる。

NameSpaceを定義する
クラス内に定義する場合。

package sample {
  public class SampleClass {
    // クラス内に名前空間を定義
    public static namespace version1;
    public static namespace version2;
  }
}

NameSpaceを指定
変数に名前空間を指定した例。ローカル変数には指定できない。

package sample {
  public class SampleClass {
   version1 var foo:int = 0;
   version2 var foo:int = 10;
  }
}
trace(version1::foo);//0
trace(version2::foo);//10

オープンな名前空間
名前空間を定義して変数と結びつけるところまで出来た。しかし今までのように、名前空間を指定しないで扱えないのだろうか。trace(foo)を使えるようにするには暗黙で名前空間が指定してあるように知らせておく必要がある。

trace(version1::foo);//0
use namespace version1;
trace(foo);//0
同じ名前の属性が複数あった場合、public関数とオープンな名前空間指定された関数では指定された関数が優先される。
public function A(){}//invoked
version1 function A(){}
use namespace version1;
public function A(){}
version1 function A(){}//invoked

XML名前空間(JP)

ActionScript3からXML名前空間という概念が実装される。名前空間とは一体何だろうか?Web上でXMLを共有していくと様々な要素が同じタグ名(例えば)で表現されることがあり得る。アクションスクリプトのクラスを管理する場合パッケージという概念があった。ドメインベースのネーミング(com.f60k.viewsなど)をすることで同じクラス名を別のものと扱うことが出来た。クラスの場合importを使うことでそのパッケージ内ではクラス名にあたるオブジェクトを使う場合、接頭辞にあたる部分を省略することが出来た。XMLでも同様の考え方がある。

QName
接頭辞のついたタグ名をQNameという。このXMLに名前空間を2つ与えてあるので、nameタグは2重に存在しているが、別の存在であることを判別して扱うことが出来る。例えるとというタグは'http://60000feet.com/xmlns/blog/'というフォルダ名の中にあるタグといったとこだろうか。

<body xmlns:blog="http://60000feet.com/xmlns/blog/" xmlns:domain="http://60000feet.com/xmlns/domain/">
<blog:name>movabletype</blog:name>
<domain:name>f60k</domain:name>
</body>

複数の構造
例えばあるサイトに新しいトピック情報を羅列するためにxmlから読み込んだ情報を使う場合を考える。そのxmlをこのように用意したとする。

<news>
<title></title><comment></comment><url></url>
</news>
htmlはaタグはリンクであるし、bタグはボールドであるなどタグの名前ごとに機能が決まっている。flashでも同じ考え方で機能を載せようと考えるのもごく普通であるし、flashでは次のようにタグを利用しようと制作したとする。

titleタグ:新しい空のMCを作成し太文字のテキストフィールドを作る。
commentタグ:最後に作られたtitleの下にcommentの内容を表すテキストフィールドを作る。
urlタグ:最後に作られたtitleの脇にURLを表示してボタン機能をつける。

ロールオーバーすると詳細表示を出すために、新たにxmlに新しく詳細を表す要素を追加しようと考えたのが次のxmlである。

<news>
<title></title><comment></comment><url></url>
<detail>
<comment></comment>
</detail>
</news>
detailタグのcommentタグはロールオーバーしたときのポップアップ内に表示したい内容にも関わらず、タグ名から別の処理がされてしまうことが予想できる。これではcommentタグがあったら...という条件に対しての機能追加だけでは不十分であるのだ。そこでxmlに名前空間を使って別のものと識別できるように再定義することができる。commentがpopupに属しているのか、newsに属しているのかがしっかり識別され、popupに属しているcommentタグはtitleの下に内容を書くのではないことをflashに伝えることが出来る。
<news xmlns:news="http://60000feet.com/xmlns/news/">
<news:title></news:title><news:comment></news:comment><news:url></news:url>
<news:detail xmlns:popup="http://60000feet.com/xmlns/popup/">
<popup:comment></popup:comment>
</news:detail>
</news>

embedFont(JP)

2回にわたって、ランタイム共有とオーサリング共有について書く予定である。今回はフォントをランタイム共有するというflashの機能を説明したい。考え方として特殊なものであるので本でわざわざ説明しているものもなさそうだ。実際の制作現場では日本語フォントをすべて埋め込むことはまずない。通常日本語の場合文字数が多いのが致命的で、部分的に選択的にフォントをTextFieldごとに埋め込む事が多い。ライブラリにランタイム共有という機能があるが使う機会が無いので覚えている人はあまり多くないのではないだろうか。今回のプロジェクトでは映像がメインコンテンツであるなどユーザがブロードバンドターゲットであることからフォントを読込む案も考えた。最終的には利用しないことにしたが、一旦4M程度の日本語フォントデータを非同期か何かでユーザにストレス無く読み込んでさえしまえば、ローカルにキャッシュされるのでswf内のページ遷移では再び読み込むこともなく全ページでフォントフェースを利用できる利点もありそうだ。

フォントファイルとは?
web上にOSのフォントをそのままアップしてもフォントは利用できない。swfから読み込める形式はswfであるので利用できるカタチにするためにはswfを作ることが必要である。フォント1個に対して1個のflaファイルを作成するのが日本語全体のファイルサイズを考えると向いている。
フォントはライブラリパレットからしか作成できない。ライブラリのメニューから新しいフォントを選択する。フォントシンボルプロパティに名前とフォントを指定する。シンボルプロパティの名前というのはMCでいうシンボル名。フォントはリストから選択する。サイズというのはビットマップフォントの時だけ指定する。ビットマップフォントはflashのフォントでは8ptを使っているものが多いので8にすれば通常は問題ない。

ランタイム共有書き出し
リンケージ名(識別子)はテキストフィールドにフォントを指定するときに使う。ランタイム共有用に書き出すと理由は分からないが必ずこのフォントswfのアップロードをするURLを入れなければならず、後でアップロードする場所を変えることになったら、このURLを変えなければならない。

ライブラリには一個のシンボルが登録された状態。このままswfにする。

ランタイム共有読み込み
まず、URLにフォントのswfをアップロードする。次に利用したいflaのライブラリを開き、フォントflaのライブラリからドラッグする。

このシンボルのプロパティを選択すると、チェックが読み込みだけでフォントflaで指定したURLや識別子が自動的に埋め込まれている状態で開く。このURLは必ずフォントflaのフォントシンボルの指定したURLと一致しなければいけない。

フォントを利用する
このフォントをオーサリングで配置したテキストフィールドに指定するには、フォントの識別子をそのままフォント名に入れるだけである。

October 13, 2006

ケーススタディ[4] SeekState(JP)

前回の記事で連続してフレームを正方向、逆方向に再生するクリップを制作に焦点を当てた。では、ある特定のフレーム(実際のプロジェクトでは特定のアンカーを押した時にドアの向き、正面の向きを表示する)の絵を見せたい場合にはどうしたらいいだろうか。その位置まで現在のフレーム位置からフレームを移動しなければならないわけだ。

シーク方向の問題
ここで問題となるのはどっちの向きに移動させればよいかということである。このシーク動作では正方向にのみ回転させるという条件にしてしまうと、例えば正面の1フレーム右の絵が現在表示していると、正面表示には約一回転しなければいけなくなってしまう。そこで実際表示したいフレームの番号と現在のフレーム番号から正方向と逆方向の再生どちらが移動数が少なくて済むかを計算した。

回転方向を決定する
シークしたい位置と現在の位置から単純に移動した場合の移動量がフレームの全体量の半分より超えなければよい。まず単純に移動した場合から移動方向を計算するその移動量が半分より超えていた場合は-1を掛けてその逆に再生する。

var dir = 0;
var rev = Math.abs(rtc._currentframe - frame) > Math.floor(rtc._totalframes / 2) ? -1 : 1;

if(frame == rtc._currentframe){
	dir = 0;
}else{
	if(frame < rtc._currentframe){
		dir = -1 * rev;
	}else{
		dir = 1 * rev;
	}
}

移動をする
endがターゲットとなるフレーム番号という違いがあるものの、RoundtripClipと全く同じである。

rtc.onEnterFrame = function(){
	var next:Number = this._currentframe + dir;
	var end:Number = frame;
	if(next < 1){
		next = this._totalframes;
	}else if(next > this._totalframes){
		next = 1;
	}
	this.gotoAndStop(next);
	if(next == end){
		delete this.onEnterFrame;
		stop();
	}
};

ケーススタディ[3] RotatoryClip(JP)

前回まで伏せていたが、先日プロジェクトがローンチしたので公開できる。このプロジェクトではプロダクトの回転に360度回転可能なメニューを使っている。

ふきだしを拡張する
このドラッグ可能な回転メニューは前回紹介した記事でふきだしの表現方法を実現するスクリプトを紹介したが、その拡張から実現している。このふきだしは別のプロジェクトの中で使っている。ふきだしの表現はフレームに配置した一連のグラフィックが正方向、逆方向に再生することで膨らむ、しぼむを表していた。このクリップでは回転メニュー正方向の再生で最終フレームまで到達したときには1フレームに行き、最終フレームから1フレームへ向かう逆方向の再生の場合は1フレームに到達したときは、最終フレームに移動するようになっている。

連続再生の仕組み
このクリップ(RotatoryClip)と前回のRoundtripClipが違うのは、一定方向に再生していた場合連続して再生するところにある。RotatoryClipには正方向に連続再生をさせるproceed()と連続逆再生させるregress()の2つのメソッドを追加した。これは単純にRoundtripClipのメソッドを呼ぶだけであるが、新たに連続再生を記述した2つのstate(ContinuousForwardState,ContinuousBackwardState)を呼ぶので実際の動作は全く違ったものとなる。

import ContinuousForwardState;
import ContinuousBackwardState;
class RotatoryClip extends RoundtripClip{
	public function proceed():Void{
		expand();
	}
	public function regress():Void{
		compress();
	}
	public function getForwardState():IState{
		return new ContinuousForwardState(this);
	}
	public function getBackwardState():IState{
		return new ContinuousBackwardState(this);
	}	
}
正方向も逆再生も基本は同じであるので、逆再生のstateだけで説明をする。regress()が呼ばれるとcompress()が呼ばれる。compressはstateにContinuousBackwardStateクラスのインスタンスを渡すのでターゲットとなるrtcのonEnterFrameで以下の動作が実行されることになる。rtcはMovieClipの拡張なのでonEnterFrameを持つ。speedという変数を設定しておけば再生速度を変えることも出来る。
public function ContinuousBackwardState(rtc:RoundtripClip) {
	this.rtc = rtc;
	rtc.onEnterFrame = function(){
		var step:Number = this.speed == undefined ? 1 : this.speed;
		var next:Number = this._currentframe - step;
		var end:Number = 1;
		if(next < end){
			next = this._totalframes + next - end - 1;
		}
		this.gotoAndStop(next);
		
	};
}

移動量の問題
スピードが1であれば、どのフレームでも次のフレームは+1又は-1で計算できる。しかし、例えばスピードが2の場合は以下の図のように移動先が変わるので、最初又は最後のフレームに来たときに反対側にジャンプする時に移動量を以下の方法で調整する。

next = this._totalframes + next - end - 1;

October 6, 2006

ケーススタディ[2] mp3の再生(JP)

前回に引き続き今回はコンテンツ中にロードされたmp3を再生する時のコツを一つ載せようと思う。mp3はflashで再生する場合、swf内に読込んでライブラリに登録しておく方法とmp3ファイルを動的にURLから読込む2つの方法がある。例えば、as2.0を選択することでflaにアクションスクリプトを記述せず別のasファイルで管理することができる。これは完全にスクリプトとオーサリング作業を分担し平行してできること、ファイルの更新がしやすい(スクリプトだけのバグや修正に互いにmailなどの通信でflaファイルを送受信しないで済むなど)利点がある。

swfは軽く作る
同様にmp3といったメディアファイルを扱う場合も、swf自体から出来るだけメディアを排除することが望ましい。mp3自体を管理しやすいこと、さらにURLから読み込むことでfla自体が無駄に大きくならないことで制作作業がしやすいことも実際の制作現場ではかなりプラスになる。

MP3を読み込む

_snd = new Sound(c);
_snd.onLoad = function(success:Boolean){
	if(success){
		this.setVolume(vol);
		this.start(0);//repeat from beginning
	}
};
_snd.loadSound(url);
ここで一つ注意したいことがある。mp3を読み込むときはかならずボリュームの設定をロード後にしなければその前に設定されたボリュームの設定がクリアされてしまう。

音量を保存する
今回このプロジェクトではミュートの設定を保存し、音を嫌うユーザに対して再びサイトの訪れた時にミュートの状態を復帰するように制作した。突然映像が流れて同時に音声が流れることを嫌う人も少なからずいるのである。ローカルにボリューム値が設定されていなければ、デフォルトの値を設定する。ローカル共有オブジェクトには現在のボリューム値をセーブする。

_so = SharedObject.getLocal(SO_NAME, "/");
_so.data.top_vol = _so.data.top_vol == undefined ? DEFAULT_VOLUME : _so.data.top_vol;
muteBtn.addEventListener("click", this);
muteBtn.toggle = true;
muteBtn.selected = _so.data.top_vol == 0 ? true : false;

October 5, 2006

ケーススタディ[1] FLVの無限ループ(JP)

今回は制作したプロジェクトの中で使われた技術の解説と応用出来る形で提供していく。今後数回に渡りこのプロジェクトに関連したアクションスクリプトの解説を行う。今回はサイトトップで映像を繰り返し流したいという要件を満たすためにNetStreamクラスを拡張しNetStreamLoopクラスを作成した。

NetStreamクラス
NetStreamクラスは一般にhttpサーバ又はFMS(旧FCS)から配信された映像や音声データをストリーミング再生するために使われる。以前の記事でplay()メソッドは詳しく説明してあるので参照していただきたい。

var conn:NetConnection = new NetConnection();
conn.connect(URL);
var stream:NetStream = new NetStream(conn);
stream.play(URI);
NetStreamクラスはストリーミングの再生の開始停止ポーズやバッファの状態が変化するとonStatusイベントを呼び出す。play()メソッドが実行されて再生が停止するまでの一連のステータス通知は次のようになる。

NetStream.Play.Reset
NetStream.PlayStart
NetStream.Buffer.Full
NetStream.Buffer.Empty
..バッファを埋めながら再生..
NetStream.Buffer.Full
NetStream.Buffer.Empty
NetStream.Buffer.Full
NetStream.Play.Stop
NetStream.Buffer.Empty

通常の再生環境ではバッファが減ったり増えたりしながら再生が行われる。バッファの容量が空になれば再生が続かずに停止するが、再生中は受信したデータでバッファを満たそうとする。バッファが空になる時は通常の再生中と再生が停止した時の2回あることが分かる。映像の再生はNetStream.Play.Stopの後にNetStream.Buffer.Emptyが呼ばれたときに初めて停止するので、NetStream.Play.Stopそのものが映像の再生停止を意味していない。

再生停止を取得する
前述したonStatusイベントから再生停止はどういうコードかが分かったので、これをもとにonStatusを記述してみる。stopped変数を使い、NetStream.Buffer.Emptyが呼ばれたときに、NetStream.Play.Stopが既に呼ばれているのかで条件分岐することで終了を見分けることが出来る。

var _stopped:Boolean;
var url:String = "movie";
function onStatus(info:Object):Void{
	switch (info.code) {
		case 'NetStream.Play.Start' :
			_stopped = false;
			break;
		case "NetStream.Play.Stop":
			_stopped = true;
			break;
		case "NetStream.Buffer.Empty":
			if(_stopped){
				//終了
			}
			break;
	}
}
終了が分かれば同じ映像をplay()メソッドで呼ぶだけでループ再生が実現する。ここではNetStreamクラスを拡張して新たにNetStreamLoopクラスを作成した。
var conn:NetConnection = new NetConnection();
conn.connect(URL);
var stream:NetStreamLoop = new NetStreamLoop(conn);
stream.play(URI);
このクラスのサンプルは自由にダウンロード出来る。