フレームレートとは元々は映像で用いられる言葉である。
アニメーションなどで1秒間に何コマ絵を動かしますか、という意味だ。1秒間に24コマの場合は24FPS(フレームパーセコンド)などと表される。FlashやJavaScriptなどで動作させるアニメーションでも用いられることが多い。
このフレームレート、Flashの場合直接フレームレートの指定が可能(デフォルトは12)だが、Javascriptの場合はtimer(setTimeoutなど)で逆算して指定することになる。
timerはブラウザにより大きく性能が異なるため、ゲーム等を制作する場合大きな障害となる。例えばキャラクタが画面を移動するとき、あるブラウザでは0.5秒かかり、別のブラウザでは2秒かかるなど、差異が大きすぎるとゲームとして成立しない。
この問題はオライリー・ジャパンの「JavaScriptグラフィックス」では以下の方法で対処している。
- どの程度のフレームレートが実現しているか調べる
- 実現したい速度と実際の速度の比率(=係数)を調べる
- 1フレームあたりのキャラクターの移動距離を標準の距離×係数で再設定する
- フレーム毎にキャラクターを再設定した距離移動させる
一方Flashサイトの場合、ゲーム用途よりバナーやトップページフラッシュなど演出目的のものが多いため、滑らかな動作が重要視される。滑らかに動かした方がより高級感を醸し出せるからだ。
滑らかに動かすためには、アニメーションは1秒のコマ数を増やせばよい。そこで多くの製作者は極力高いフレームレートを設定することになる。
ところが高くすればするほどパソコンに負荷がかかり、スペックの低いパソコンでは設定したフレームレートの半分も出せないことがある。結果カクカクした動きになっしまう。
その対策については、こちらもオライリー・ジャパンの「Flash Hacks」にわかりやすく記載されている。
まず上記1同様、「どの程度のフレームレートが実現しているか」を調べ、オブジェクトが多数動くことが負担になっている場合は、動作しているオブジェクト(キャラクターや粒子など)の数を減らしている。
上記書籍は2005年発売でActionScript1.0又は2.0で記述されているので、今回はActionScript3.0に置き換えつつ、より実践的にしたPerformanceMonitorクラスを作成した。
以下がそのサンプルとなる(http://nouv.biz/labo/20121004_performance_montor/)。
大量のパーティクル(粒子)を生成し、ランダムにY回転させている(重いので他作業中の方は注意)。
右下に、実現しているフレームレートと、パーティクルの数を表示している。
フレームレートは24FPS、パーティクルの初期生成数は10万個を初期値にしているが、フレームレートが24フレームに達しないと、序々にパーティクルの個数を減らし調整する。
フレームレートが目標に達しているか調査する部分のスクリプトを以下に記載。
private function onEnterframe(event:Event):void {
var nowTime:Number = new Date().getTime();
var pastTime = nowTime-oldTime ;
var fps:Number = 1000 / pastTime;
if (interCount > 100) {
//過去の累積が悪影響をおよぼすので、ある程度の回数でクリアする
totalFPS = 0;
interCount = 0;
}
totalFPS += fps;
interCount++;
oldTime = nowTime;
}
/*
*
*/
public function get _averageFps():int {
var averageFPS:Number = Math.floor(totalFPS / interCount );
return averageFPS;
}
/**
*達成したいフレームレートを指定する
* 達成できているかどうかを返す
*/
public function fpsBool(targetFPS:int):Boolean {
var bool:Boolean;
if (_averageFps >= targetFPS) {
bool = true;
}else {
bool = false;
}
return bool;
}
現時刻から前に通過した時刻を引き、そこからフレームレートを求めその値を返す。
一方、パーティクルはその結果を受けて個数調整を行なっている。
numBallsは生成するパーティクルの個数。上限下限を設けている。
performanceBool = performanceMonitor.fpsBool(24);
if (!performanceBool) {
numBalls = (numBalls - 100) > 0? numBalls - 100 : numBalls;
}else {
numBalls = (numBalls + 100) <= makeNum ? numBalls + 100 : makeNum;
}