サイトトップ

Director Flash 書籍 業務内容 プロフィール

Adobe Flash非公式テクニカルノート

たくさんのインスタンスのアニメーションをビットマップでキャッシュする

ID: FN1111003 Product: Flash CS4 and above Platform: All Version: 10 and above/ActionScript 3.0

Flashのベクターグラフィックスは、画面に描かれるときビットマップに変換されます。このビットマップをキャッシュしてうまく使い回せると、アニメーションの描画が速められます。その手法をふたつご紹介します。


01 DisplayObject.cacheAsBitmapプロパティで平行移動のアニメーションを速める
まずは、MovieClipシンボルをひとつ用意します。そして、インスタンスを動的につくれるように、[シンボルプロパティ]ダイアログボックスで[クラス]に「MyClass」と入力します(図001)。インスタンスはタイムラインに大量に配置しますので、シンボルは小さめにしてください。

図001■[ライブラリ]のMovieClipシンボルに[クラス]を設定する

メインタイムラインには何も置かずに、つぎのスクリプト001をフレームアクションに書きます。[ムービープレビュー]で試すと、インスタンスがステージのランダムな位置に100個つくられ、下に向かってアニメーションします(図002)。

図002■ランダムに置かれた100個のインスタンスが下に向かってアニメーションする

スクリプト001■動的につくったインスタンスを下に向かってアニメーションさせる
    // フレームアクション
  1. var nStageHeight:int = stage.stageHeight;
  2. var nCount:uint = 100;
  3. var instances:Vector.<MovieClip> = new Vector.<MovieClip>(nCount);
  4. for (var j:uint = 0; j < nCount; j++) {
  5.   var _mc:MovieClip = new MyClass();
  6.   instances[j] = _mc;
  7.   addChild(_mc);
  8.   _mc.x = Math.random() * stage.stageWidth;
  9.   _mc.y = Math.random() * stage.stageHeight;
  10.   // _mc.cacheAsBitmap = true;
  11. }
  12. addEventListener(Event.ENTER_FRAME, xMove);
  13. function xMove(eventObject:Event):void {
  14.   for (var i:uint = 0; i < nCount; i++) {
  15.     var instance:MovieClip = instances[i];
  16.     instance.y += 5;
  17.     if (instance.y > nStageHeight) {
  18.       instance.y -= nStageHeight;
  19.     }
  20.   }
  21. }

インスタンスの数をさらに増やすと、見る見る動きが遅くなります。本稿のお題はいかに速さを稼ぐかです。インスタンスを(平行)移動するだけの場合には、DisplayObject.cacheAsBitmapプロパティtrueにします。すると、ベクターデータをステージに描くための、ビットマップがキャッシュされるので、画面への表示が速まります。前掲スクリプト001では、第10行目のコメントアウトを外して、有効なステートメントにします。

  1.   _mc.cacheAsBitmap = true;

インスタンスのDisplayObject.cacheAsBitmapプロパティをtrueにすると、インスタンスの数が増えたことにより遅くなったアニメーションの描画は速まります(図003)。

図003■DisplayObject.cacheAsBitmapプロパティをtrueにすると平行移動のアニメーションは速まる


02 インスタンスが回転するイメージのビットマップをキャッシュして切替える
前掲スクリプト001の下に移動するアニメーションに、インスタンスの回転を加えましょう。すると、またインスタンスの動きは重くなります。キャッシュしたインスタンスのイメージが、回転してしまったら使えないからです。つまり、DisplayObject.cacheAsBitmapプロパティでは役に立ちません。

    // 追加
    var nMaxDegrees:uint = 360;
    var nDegrees:uint = 5;
    var nRotation:uint = 0;
  1. function xMove(eventObject:Event):void {
      // 追加
      nRotation += nDegrees;
      nRotation %= nMaxDegrees;
  2.   for (var i:uint = 0; i < nCount; i++) {
  3.     var instance:MovieClip = instances[i];
        instance.rotation = nRotation;   // 追加
  4.     instance.y += 5;
  5.     if (instance.y > nStageHeight) {
  6.       instance.y -= nStageHeight;
  7.     }
  8.   }
  9. }

けれど、回転は360度分のイメージが繰返します。そうであれば、1周360度分に必要なイメージを、予めまとめてキャッシュすればよいのではないでしょうか。この考え方は正しいです。ただ、あいにくそのような機能はFlashに備わっていません。したがって、スクリプトでそのキャッシュをつくります。

インスタンスが回転するイメージはビットマップ、つまりBitmapDataインスタンスでつくります。360度の回転に応じてビットマップがいくつも要りますので、ベース型をBitmapDataとするVectorオブジェクトに納めます。そして、MovieClipはBitmapのインスタンスに置換えて、実際に回転はせずにBitmapインスタンスにもたせるビットマップ(BitmapDataインスタンス)を切替えればよいのです。

MovieClipインスタンスから、回転したBitmapDataオブジェクトを取出すには、BitmapData.draw()メソッドを用います。回転などの変形は第2引数のMatrixオブジェクトで定めます。ビットマップを描くBitmapDataオブジェクトの1辺の長さは、回転することを考えてMovieClipインスタンスの対角線に等しくしました(図004)。背景は透明です。

BitmapDataオブジェクト.draw(描画もとオブジェクト, 変換Matrixオブジェクト)

new BitmapData(幅, 高さ, 透明設定, 背景色)

図004■BitmapDataインスタンスの1辺の長さは描画もとインスタンスの対角線の長さにする

BitmapDataインスタンスの基準点は左上角です。それに対して、描画もとMovieClipインスタンスは、回転させるために中心を基準点にしています(前掲図004参照)。BitmapData.draw()メソッドをデフォルトのまま用いれば、BitmapDataオブジェクトにはインスタンスの右下1/4しか描かれません(図005)。MovieClipインスタンス全体を描画するには、メソッドの第2引数となるMatrixオブジェクトでイメージを右下に移動しなければなりません。

図005■BitmapData.draw()メソッドのデフォルトは描画もとインスタンスの基準点が左上角になる

MovieClipインスタンスの1回転360度分のBitmapDataオブジェクトを1度刻みでつくってキャッシュするフレームアクションはつぎのスクリプト002のとおりです。記した行番号は、後に全体を掲げたスクリプト003にもとづきます。

スクリプト002■インスタンスが回転する360度分のイメージをVectorオブジェクトにキャッシュする
    // フレームアクション
  1. var _mc:MovieClip = new MyClass();
  2. var nWidth:Number = _mc.width;
  3. var nHeight:Number = _mc.height;
  4. var nDiagonal:Number = Math.sqrt(nWidth * nWidth + nHeight * nHeight);
  5. var nMaxDegrees:uint = 360;
  6. var nRotation:uint = 0;
  1. var rotationData:Vector.<BitmapData> = new Vector.<BitmapData>(nMaxDegrees);
  2. for (var i:uint = 0; i < nMaxDegrees; i++) {
  3.   var myBitmapData:BitmapData = new BitmapData(nDiagonal, nDiagonal, true, 0x0);
  4.   var myMatrix:Matrix = new Matrix();
  5.   myMatrix.rotate(i / 180 * Math.PI);
  6.   myMatrix.translate(nDiagonal / 2, nDiagonal / 2);
  7.   myBitmapData.draw(_mc, myMatrix);
  8.   rotationData[i] = myBitmapData;
  9. }
  10. // 確認用
    var myBitmap:Bitmap = new Bitmap();
    addChild(myBitmap);
    myBitmap.bitmapData = rotationData[nRotation];   // 角度: 0度
    myBitmap.x = (stage.stageWidth - nDiagonal) / 2;
    myBitmap.y = (stage.stageHeight - nDiagonal) / 2;

確認用のステートメントを加えましたので、[ムービープレビュー]を確かめると、回転角0度のBitmapインスタンスがひとつステージ真ん中に表れます(図006)。

図006■角度0のMovieClipインスタンスのイメージがステージ中央に表示される

あとは、Bitmapインスタンスを好きなだけつくって、DisplayObject.enterFrameイベント(定数Event.ENTER_FRAME)のリスナー関数(xMove())で回転と移動のアニメーションを加えます。フレームアクション全体は以下のスクリプト003のとおりです。MovieClipシンボルの大きさにもよるでしょうが、数千のBitmapインスタンスをつくっても速さはそれほど衰えません。

スクリプト003■大量のBitmapインスタンスを使った回転と平行移動のアニメーション
    // フレームアクション
  1. var _mc:MovieClip = new MyClass();
  2. var nWidth:Number = _mc.width;
  3. var nHeight:Number = _mc.height;
  4. var nDiagonal:Number = Math.sqrt(nWidth * nWidth + nHeight * nHeight);
  5. var nMaxDegrees:uint = 360;
  6. var nRotation:uint = 0;
  7. var nStageHeight:int = stage.stageHeight;
  8. var nCount:uint = 500;
  9. var instances:Vector.<Bitmap> = new Vector.<Bitmap>(nCount);
  10. var rotationData:Vector.<BitmapData> = new Vector.<BitmapData>(nMaxDegrees);
  11. for (var i:uint = 0; i < nMaxDegrees; i++) {
  12.   var myBitmapData:BitmapData = new BitmapData(nDiagonal, nDiagonal, true, 0x0);
  13.   var myMatrix:Matrix = new Matrix();
  14.   myMatrix.rotate(i / 180 * Math.PI);
  15.   myMatrix.translate(nDiagonal / 2, nDiagonal / 2);
  16.   myBitmapData.draw(_mc, myMatrix);
  17.   rotationData[i] = myBitmapData;
  18. }
  19. for (var j:uint = 0; j < nCount; j++) {
  20.   var myBitmap:Bitmap = new Bitmap();
  21.   instances[j] = myBitmap;
  22.   addChild(myBitmap);
  23.   myBitmap.bitmapData = rotationData[nRotation];
  24.   myBitmap.x = Math.random() * stage.stageWidth - nDiagonal / 2;
  25.   myBitmap.y = Math.random() * stage.stageHeight - nDiagonal / 2;
  26. }
  27. addEventListener(Event.ENTER_FRAME, xMove);
  28. function xMove(eventObject:Event):void {
  29.   nRotation += 5;
  30.   nRotation %= nMaxDegrees;
  31.   for (var i:uint = 0; i < nCount; i++) {
  32.     var instance:Bitmap = instances[i];
  33.     instance.bitmapData = rotationData[nRotation];
  34.     instance.y += 5;
  35.     if (instance.y > nStageHeight) {
  36.       instance.y -= nStageHeight;
  37.     }
  38.   }
  39. }

[ムービープレビュー]を確かめると、たくさんのインスタンスが回りながら下に向かって移動します(図007)。インスタンスの数がきわめて多いときは、インスタンスをプロパティで実際に回すより、ビットマップ(BitmapDataオブジェクト)のイメージを切替える方がアニメーションはずっと速くなります[*1]

図007■数多くのインスタンスが回りながら下に移動し続ける

[*1] 本稿ではビットマップ(BitmapDataオブジェクト)をキャッシュする仕組みについてできるだけ簡潔に示すため、フレームアクションのスクリプトでご紹介しました。クラスを定義したサンプルについては、「手動によるビットマップキャッシュ」や@IT「Flashで吹雪のごとき描画を実現するチューニング3策」、wonderfl「BitmapDataで配列に格納すると高速化するよ」などをご参照ください。



作成者: 野中文雄
作成日: 2011年11月11日


Copyright © 2001-2011 Fumio Nonaka.  All rights reserved.