フレームレートとパフォーマンスモニタについて

フレームレートとは元々は映像で用いられる言葉である。
アニメーションなどで1秒間に何コマ絵を動かしますか、という意味だ。1秒間に24コマの場合は24FPS(フレームパーセコンド)などと表される。FlashやJavaScriptなどで動作させるアニメーションでも用いられることが多い。

このフレームレート、Flashの場合直接フレームレートの指定が可能(デフォルトは12)だが、Javascriptの場合はtimer(setTimeoutなど)で逆算して指定することになる。
timerはブラウザにより大きく性能が異なるため、ゲーム等を制作する場合大きな障害となる。例えばキャラクタが画面を移動するとき、あるブラウザでは0.5秒かかり、別のブラウザでは2秒かかるなど、差異が大きすぎるとゲームとして成立しない。

この問題はオライリー・ジャパンの「JavaScriptグラフィックス」では以下の方法で対処している。

  1. どの程度のフレームレートが実現しているか調べる
  2. 実現したい速度と実際の速度の比率(=係数)を調べる
  3. 1フレームあたりのキャラクターの移動距離を標準の距離×係数で再設定する
  4. フレーム毎にキャラクターを再設定した距離移動させる

一方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;
}

 

「ActionScript3.0アニメーション」をcanvasで置き換える 引き合うパーティクル 応用(メタボール)

先日作った「引きあうパーティクル」のFlash側を少し応用し、メタボール表示してみる。
メタボールとは何か?見てもらったほうが早い。
http://nouv.biz/labo/20120902_nodeGardenMetaBall/NodeGardenMetaballTest.html

1.中央から周辺へ、非透明→透明のグラデーションのボールを描画
2.パレットマップ用の配列を作成(透明度200以下は全て透明それ以上は全て非透明の配列を作成)
var separateNum:int = 200 //どこでグラデーションの切れ目とするか=metaのレベルとともにballの大きさにも影響する
alphas = new Array();
//200まではすべて透明
for (i = 0 ; i < separateNum ; i++) {
alphas.push(0);
}
//200以上は全てffになる。単独では200以下で透明でも、ふたつのグラデーションが重なって200以上になると非透明に→メタボール状態になる
for (i = separateNum ; i < 256 ; i++) {
alphas.push(0xFF000000);
}
このalphasをパレットマップに適用する。
bmd.paletteMap(bmd , bmd.rect , bmd.rect.topLeft , null , null , null , alphas);

外周付近が半透明な円が2つ以上重なり透明度が200を超えた場合、真っ黒に色が変換される。結果円が繋がったように見える。
メタボールについては webDesigning 2006年12月号に掲載されたFlashPlayer8向けの記事を参考にさせていただいた。6年近く前の記事なのにいまだ色褪せない。
梅津岳城氏に感謝。

「ActionScript3.0アニメーション」をcanvasで置き換える 引き合うパーティクル

今回はChapter12のノードガーデンを作成する。
パーティクルをランダムに移動させ、それぞれの距離が200ピクセルより近ければ、近いパーティクル方向へより加速させる。
[Flash]
http://nouv.biz/labo/20120902_nodeGarden/flash/NodeGardenTest.html
[ javascript]
http://nouv.biz/labo/20120902_nodeGarden/javascript/nodeGarden.html

線を描画するときの透明度指定が少々ぎこちない。javascriptではこんな風なのだろうか。
var alpha=1-dist/minDist;//距離が遠いほど小さい→遠いほど透明になる
var rgba=’rgba(255, 255, 255,’ + alpha + ‘)’;
context.strokeStyle=rgba;

「ActionScript3.0アニメーション」をcanvasで置き換える 3D回転(及びJavascriptにおける継承)

Flashがz軸に対応した今となってはもはや古典か?「ActionScript3.0アニメーション」。
とはいえ内容は多いに役立つので一読をおすすめする。

今回はchapter15 3Dの基本の最後、3D回転。
[Flash]
回転公式忘れ。
+-入れ替えてzが後ろに来る、と。
var nx:Number = cos * ball.xpos – sin * ball.zpos;
var nz:Number = cos * ball.zpos + sin * ball.xpos;
それ以外は特に問題なし

http://nouv.biz/labo/20120901_rotate/flash/RotateXYZ.html


[Javascript]
先日作ったPoint3Dクラスを継承した、Point3DExtendSampleを作る。

Point3D.js:自身の座標保持。3D座標を2D座標に変換
Point3DExtendSample.js:上記クラスを継承し、drawメソッドをオーバーライドする。

作るにあたり、Javascriptの継承の方法を調べたらいくつかあることがわかった。今回はもっともオーソドック(教科書的)とオライリーに記載されてた方法を用いる。
即ち

function Point3DExtendSample(radius , xpos , ypos , zpos , fl){
this.radius=radius;
Point3D.call(this , xpos , ypos , zpos , fl);//親のオブジェクトのコンストラクタを呼び出す
}

//Point3Dの継承を実現する
Point3DExtendSample.prototype=new Point3D();

//Point3Dのメソッドをoverrideする
Point3DExtendSample.prototype.draw=function(myCanvas){
with(this){
var context=myCanvas.getContext(“2d”);
context.setTransform(getScale() , 0 , 0 , getScale() , x , y);
context.strokeStyle=”#cccccc”;
context.fillStyle=”#ff0000″;

context.beginPath();
context.arc(0 , 0 , radius , 0 , Math.PI*2 , true);
context.fill();
context.stroke();
context.setTransform(1 , 0 , 0 , 1 , 0 , 0);
}
}
の手法を用いる。
callってのがよくわからないうえcallした後にprototypeに引数無しの
new Point3D()がきてるのもよくわからん。後ほど調べる
(http://taiju.hatenablog.com/entry/20100515/1273903873 後で読ませて頂く)。

 2012年9月21日 以下自分なりの解釈を追記。

//とりあえず関数を作る
function Point3DExtendSample(radius , xpos , ypos , zpos , fl){
...
}

//その関数にPoint3D(インスタンス?機能)を埋め込む。
Point3DExtendSample.prototype=new Point3D();

//prototypeにオブジェクト(Point3Dのインスタンス)が設定される
//new Point3D()でのreturnは
//オブジェクト=Point3Dのインスタンス。
//これはPoint3DExtendSampleにPoint3Dインスタンスを生成(Point3Dをコピー)
//している、とも言える
//そのコピーしたPoint3Dに拡張したいメソッドなどを追記していく
//関数が起動するまえ即ちhtmlに 
//<script type="text/javascript" 
//src="nouvdotbiz/Point3DExtendSample.js"></script>
//と記載された時点で以下は実行される。
//その時点でxpos,yposなどのインスタンス変数が生成される。
//しかし上記では引数が設定されていないため、undefinedが代入されると思われる。
//スーパークラス(この場合Point3D)のコンストラクタ状態によってはエラーが発生する。
//その後、継承クラスのインスタンスが生成された時点で引数に代入されるのは通常通り。

初回時ボールが2重に描画されてしまった。 これはFlashの元ロジックをそのまま踏襲したことが仇になったようだ。x軸回転とy軸回転をそれぞれ別の関数にて起動しており、その関数内で Point3DExtendSampleのdrawメソッドを呼び出していたことが原因。x軸回転とy軸回転のわずかな時間差で描画されるため、2重になっていた。この処理をenterFrame内に統合し解決。

http://nouv.biz/labo/20120901_rotate/javascript/RotateXYZ.html

「ActionScript3.0アニメーション」をcanvasで置き換える 3Dラッピング応用

chapter15 3dの基本 ラッピングの応用。Tree部にImageを用いる。
オリジナルクラスImageLoadAndDraw.jsで画像を読込み描画させる手もあったが、ひとつだけなので今回は使用しない。
結果以下のクラスを用いる。
Point3D.js :
自身の座標保持。3D座標を2D座標に変換
applyImage3DMoving.js :
画像の読込
Point3Dの生成
Point3Dの変換2D座標へのImage描画

今回は1枚しか画像を使わないが、複数枚使う場合は上記
1.ImageLoadAndDraw.jsにて読込
2.すべての画像の読込判定
3.Point3D生成
4.画像配列( imageLoadAndDraws)と3D座標配列(point3ds)の対応付け(ソートがあるので少々面倒か?)が必要となる。

操作は前回同様↑キーで直進↓キーで後退。→キー←キーでそれぞれの方向に移動。spaceキーで上昇。

http://nouv.biz/labo/20120816_tree/javascript/applyImage3DMoving.html

発展系としてスプライトシートを作って走るアニメを用いるのが考えられる。またいずれ。
それにしてもFlashを使わないってのはスマホ・タブレット対策。にも関わらず操作に矢印キーだのスペースキーだの用いるって時点でもう…。この対策もまたいずれ。