- 2007-03-05 (月)
- action script
BIG SPACESHIP LABS さんの、Flash の再生パフォーマンスを上げる工夫のパート2が公開されていますね。
「BIG SPACESHIP LABS / » Flash Performance Tips Part II」
前にパート1も訳したので、このパート2も訳しました。今回も快諾を頂けましたので、公開します。
いつものように、僕の翻訳力、理解力、語彙力を超えている箇所があります。可能な限り、元ソースをご参考下さい。
記事内容を校正していただける方を心からお待ちしています。なので、今のこの翻訳を鵜呑みにせず、一週間程様子を見て、皆さんからのコメント補足が入った後に再訪問するのがおすすめです。
The article below is a translation of the article:"BIG SPACESHIP LABS / » Flash Performance Tips Part II". I appreciate the author Jamie and members of Big Spaceship.
related article:
trick7.com blog: 「Flash Performance Tips Part 1」の日本語訳 (Japanese)
(以下翻訳記事)
前に書いた記事に対する多くの好意的な反響には驚きました。さらに役立つ tips をかき集めて、ご紹介しないといけませんね。
今回の記事では、いくつかの tips の合わせ技をご紹介いたします。AS2のみならず、AS3に移行する際にも役に立つような、具体的な tips を取り上げてみました。
トゥイーンの方程式と計算のパフォーマンス
プログラミングによるモーション付けに関しては、言うべきことが沢山あります。 プロトタイプ拡張でのトゥイーン機能の実装は(例えば、こことかここみたいに)いろいろ存在しています。内部では Adobe のトゥイーンパッケージを使っているわけです。それらはシンプルで分かりやすく、とても使いやすいです。コンポーネントのような複雑さも必要ありませんしね。
つまりは、トゥイーンする際には、どの方法を使うかではなく、どんな数式(方程式)を実行するかを考えることが重要なのです。記述した式の差異が、パフォーマンスの善し悪しに影響するのです。例えば、「10進数の小数の乗算(かけ算)の方が、除算(割り算)する場合よりも高速に処理できる」などの例があります。
そのことを念頭に置いて、Adobe の Strong 設定と、Penner イージングの Expo 設定とを比べてみましょう。結果はほとんど同じです(ある状況下で Penner イージングの方が高速なこともありますが)。式を実行する前に、他にもっと良い方法があるかどうかを試したり、探したりするのがベストです。また、自分でコードを書いてみてください。
トレース文は消しておこう
出来の良いクリーンなコードを書くためだけではなく、トレースという行為自体が Player にとっては実際に悪影響を与えているように思います。少なくとも、最終パブリッシュ時にはトレース文は消しておきましょう。もし、まだ "Out クラス"を使っていない方がおられましたら、こちらのリンクをご覧になってください。(Out クラスとは BIGSPACESHIP さんが作ったデバッグ用カスタムクラスで、この記事の後日、専用の解説ページをエントリされています。)
この Out クラスには、状況に応じて、エラー文・詳細情報・デバッグ情報・ステータスや重要文、といった、いくつかの異なるトレースの"レベル"が用意されています。これを使い分けることで、デベロッパは、より正確なデバッグ情報を確認できるのです。
我々(BIG SPACESHIPの人)は、Out クラスを使いまくっています。プロジェクトが複雑になっていった時には、デバッグ関連の箇所以外は一旦全て無効にして、バグを素早く、かつ簡単に、つぶしていくようにしています。
ライブラリ内のビットマップやサウンドを個別に最適化する
これは骨の折れる作業に思えます(実際そうだったり)が、個別にパーツサイズを抑えておくことで、コンパイル時のファイルサイズをより抑えることができます。
サウンドに関しては、我々は AIF ファイルを使い、書き出し時に MP3 形式で出力するようにしています。こんな風に、より良いサウンド品質の元データを用意しておいて、圧縮してから徐々に調整していく方がいいと思いませんか。いかが思われますか?
あ、そうそう、OSX での IDE のバグについて言及しておきます。これにはいつもイライラさせられます。サウンドを 64kbps で出力(※パブリッシュ時)すると、たまにひどい金切り音がすることがあります。この現象は、ランダムな確率で、OSX プラットフォームでのみ発症するように思います。だから我々は、最近は64kbps にはしないようにしています。(我々のチームのサウンドデザイナーにそう命令されています。)
我々は "Fuel's Library Items panel (download)" が、大のお気に入りです。これを使えば、ライブラリ内のアイテムに対し、簡単な一括処理を施すことができるのです。(※設定パネル内にささいな誤表記があったそうですが、既にツール制作者が修正対応されています。by tera)
複雑なベクターデータやテキストデータ
Flashに、毎フレーム複雑なベクターデータの再描画を強いることがあります。これはテキストにおいても同様で、ダイナミックテキストフィールドをムービークリップの中に配置して、それを cacheAsBitmap させた時、一見それらは動いていないようでも、プロセッサーには負荷がかかっているのです。
バラバラとJPG画像を配置しないようにJPG画像を分解しないように
これは最悪です。Player は毎フレームごとに JPG を調べ、何を描画すべきかを計算するという仕様になっています。この処理すら大変なのに、さらに JPG 画像から複雑なシェイプに変換するのは、なおのこと悪いです。Flash に二重の苦役を強いることになります。JPG の特定の部分だけが必要な場合は、うーん、まだマスクを使った方がましでしょう。できることなら、Photoshop に戻って必要なパーツだけを切り出すようにしましょう。
onEnterFrame 内でループしないこと
for ループを毎フレーム実行させるよりも、必要なものを直接定義しておく方が高速です。インクリメントさせたい少量のデータがあるような場合は、それらをハードコードするようにしましょう。
// 遅い
var a = [0,1,2];
mySlow.onEnterFrame = function():Void
{
for(var i=0;i<a.length;i++) { a[i]++; }
};
// 高速
var a = [0,1,2];
myFast.onEnterFrame = function():Void
{
a[0]++;
a[1]++;
a[2]++;
}
では、その辺をもう少し掘り下げて考えてみましょう。
余計なループは避ける
複雑なデータセットを扱うとしましょう。そのデータを ActionScript の配列内でソートするのが、最善の作戦のように思えます。でもそれは間違いです。それはトラブルの元になります。次の基本的な例で考えてみましょう:
var error_array = []; error_array[0] = [404,"File Not Found"]; error_array[1] = [500,"Misconfiguration"]; error_array[2] = [403,"Forbidden"]; error_array[3] = [200,"Okay"];
// この関数が外部イベントから実行されたとします
onNewError($error_pre:Number):Void
{
for(var i=0;i<error_array.length;i++)
{
if(error_array[i][0] == $error_pre)
{
trace("Error Found!: " + i);
break;
}
}
};
function doesErrorExist($error_pre:Number):Boolean
{
for(var i=0;i<error_array.length;i++)
{
if(error_array[i][0] == $error_pre) return true;
}
return false;
};
この場合、要素数は少ないですが、onNewError や doesErrorExist 関数が実行される度に、該当エラー文(あるいは該当なし)を探すために、配列内の全てをループで調べないといけません。実は、同様の作業をもっとスマートに実行できる方法があるのです:
var error_obj = {};
error_obj["e_404"] = "File Not Found";
error_obj["e_500"] = "Misconfiguration";
error_obj["e_403"] = "Forbidden";
error_obj["e_200"] = "Okay";
onNewError($error_pre:Number):Void
{
trace("Error Found!: " error_obj["e_" + $error_pre.toString()]);
};
function doesErrorExist($pre:Number):Boolean
{
return error_obj["e_" + $error_pre.toString()] != null;
}
インデックスを持たせたオブジェクトを使うことで、ループをしなくて済むようになりました。There are times when using arrays of objects (objects populated with arrays) can be useful, too. この手法を使えば、いちいちループさせる必要がなくなります。
フレームベースでないいくつかのイベントを避ける
onMouseMove や Stage.onResize は、負荷のかかるプログラミングという点では、使用を避けるべき関数の代表例です。それらの関数はフレームレートとは無関係に実行されるので、1フレーム分更新される前に、5〜10回の Mouse Move イベントが実行されてたりするのです。我々は、マウス位置の検出は onEnterFrame ベース行った方が高速だと思います。
ムービークリップの逆再生はしない
もちろん、理論上はいいアイデアのように思えます。しかし深刻な問題があるのです。
まず初心者は、タイムラインを逆再生させても大丈夫なようなスクリプティングを知っておく必要があります。You'll need to know whether you need to ignore these events or not, which means before you know it you’ll be rewriting the Timeline entirely in ActionScript.
第二に、Player に対して2重の労働を課していることになります。通常でも Player は何を更新するかを毎フレーム調査しなければいけないのに、…now you're making it run pre to move a timeline backwards every frame on top of that.
もし、タイムラインが複雑になっている場合に、それを逆再生させようとすると、思いがけないバグに遭遇することでしょう。タイムラインが単純なようなら、30秒ぐらいの作業でキーフレームを反転して、別のラベルに gotoAndPlay させたほうが賢明だと思います。Player やエンドユーザーにとっては、その方が好ましいのです。
フィルタの値は2の倍数乗数にしておくと良い
ブラーフィルタを使う時は、2の倍数乗数(2、4、8、16、32 など)を指定しましょう。(参考記事)その方が Player にとっては良さそうです。我々側できちんとベンチマーク調査をしたわけではありませんが、念のために、そうしておいた方が良さそうですよ。:)
Kill とか Destroy とか Dispose とか、それ関係のこと
この内容は、特にAS3に関わってきます。ガベージコレクションが、現行のFlashよりも、さらにシビアになるのであれば、この手法を習慣づけておく方が良いでしょう。我々は、自作したクラス全てに、次のような関数(メソッド)を組み込んでいます:)
function kill():Void {};
kill は全ての onEnterFrame を削除し、interval 処理をクリアし、ムービークリップの参照も削除し、全ての変数を初期化、又は null にし、オブジェクトの参照も削除する、とにかくいろいろクリアする関数です。このアイデアは、ガベージコレクション処理によって、まず最初に、不要なものをクリアしておくための、唯一の方法です。この方法で、メモリー上に重複したクラスを保持しないよう、保証しておけます。Flash にとっては大変良いことです。
ガベージコレクションについての詳細情報は Grant Skinner氏の AS3 Resource Management をご覧下さい。また、彼が作った Janitor クラスは、あなたが移行する際に役立つでしょう。
高度な衝突判定
hitTest は、インスタンス同士のちょっとした衝突を判定する方法としては使えるのですが、世の中には、より機能的なカスタムビルドされた方法があるのです。ここでは、我々が以前見つけた、高度な判定アルゴリズムへのリンクを2つ紹介します。"Recursive Dimension Clustering" では、スクリーンをエリア分けし、どのエレメントがどのエリアに進入したかに基づいて判定します。とても気の利いた、かつ高速な方法です。and you can wow all of the babes with your newfound math babble.もう一方の方法は、Grant Skinner 氏の "Shape Based Hit Detection" です。これは BitmapData を上手に使った方法で、2つのオブジェクトの差異を draw して、それらが交差した(あるいは交差していない)境界を返すのです。
(上記2例については後日調査します。その際に内容修正するかもです。by tera)
また、簡単な方法として、判定させたい形そのものに対してではなく、その複雑な形のオブジェクトの周りに、空のムービークリップをいくつか配置して、それらとの hitTest をすることで衝突判定する方法もあります。 単純なオブジェクトで複数回 hitTest する方が、複雑な形のオブジェクトで一回だけhitTest を行うよりも、Player の負担は少なく済みます。
Comment:4
- colm 2007-03-05 (月) 22:57
-
はじめまして、いつも参考にさせていただいています。
さてJPGのあたり、ちょっと気になったので原文に当たって見ました。おそらくこれはひとつのJPGを"Break Apart(修正>分解)"してはいけない。ということではないでしょうか。
パフォーマンステストはしてないですが、分解するとシェープをビットマップ塗りした状態になるので、いかにも負荷が増しそうです。 - tera 2007-03-06 (火) 08:04
-
>>colm様
はじめまして。コメントいただき、ありがとうございます。
なるほどー、大きな勘違いをしておりました。
ステージ上に複数JPGは駄目とか言われても困りますもんね。w
早速修正しました。しばらくは明示的に、打ち消し線で修正しますが、そのうちキレイにします。
今後ともよろしくお願い致します。 - NYO 2007-03-15 (木) 15:47
-
はじめまして。
誤訳というか、原文があいまいな表現なので、
指摘させていただきます。訳:フィルタの値は2の倍数にしておくと良い
正:フィルタの値は2の乗数にしておくと良い2の倍数では偶数ということになってしまいます。
正しくは2の乗数(2,4,8・・・)ですね。
ヘルプにも記載されています。http://www.adobe.com/jp/devnet/flash/articles/graphic_effects_guide_04.html
(ページ一番下の灰字の箇所) - tera 2007-03-15 (木) 16:13
-
>>NYO様
はじめまして。ご指摘ありがとうございます。
原文を読むと、どうも倍数っぽい翻訳になってしまったので、誤訳してしまいました。
ご丁寧に、参考リンクまで頂き、助かります。
今後ともよろしくお願いいたします。
Trackback:0
- TrackBack URL for this entry
- http://www.trick7.com/blog/mt-tb.cgi/494
- Listed below are links to weblogs that reference
- 「Flash Performance Tips Part 2」の日本語訳 from trick7.com blog



