サイトトップ

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

Optimizing Performance of ActionScript 3.0

Chapter 01 文法の基本から

□01-04 何度もアクセスするプロパティやオブジェクトは変数に入れる

オブジェクトからプロパティを取出すより、変数に入れたプロパティにアクセスする方が処理は速いです。つまり、何度もアクセスするプロパティは、初めに変数に納めてしまえば処理が最適化できます。プロパティやオブジェクトを変数に入れた方が望ましいいくつかの例と、そのときの注意点をご説明します。

    【プロパティやオブジェクトを変数に入れる例と注意】
  1. プロパティから参照するオブジェクトにいくつもの処理を加えるとき
  2. 配列から取出したオブジェクトに何度もアクセスするとき
  3. 最後に代入が必要なプロパティに注意

01-04-01 プロパティから参照するオブジェクトにいくつもの処理を加えるとき
プロパティから参照を得たオブジェクトに、いくつもの処理を加える場合があります。たとえば、SpriteやShapeインスタンスに備わるGraphicsオブジェクトを取出して描画するときです。

以下のフレームアクション(スクリプト01-04-001)は、タイムラインに置いた空のMovieClipインスタンス(my_mc)に、線が青で塗りがシアンの半径50ピクセルの円を描きます(図01-04-001)。しかし、毎回インスタンスからSprite.graphicsプロパティの参照を求めて、そのGraphicsオブジェクトに対してメソッドを呼出しています(第2〜5行目)。インスタンスに何度も繰返し問合せるのは、無駄があります。

スクリプト01-04-001【△】同じプロパティからオブジェクトを繰返し参照する
    // フレームアクション
    // 空のMovieClipインスタンスmy_mcを配置
  1. my_mc.graphics.lineStyle(2, 0x0000FF);
  2. my_mc.graphics.beginFill(0x00FFFF);
  3. my_mc.graphics.drawCircle(0, 0, 50);
  4. my_mc.graphics.endFill();

図01-04-001■タイムラインに置いた空のMovieClipインスタンスに線が青で塗りがシアンの円を描く

インスタンスに備わるGraphicsオブジェクトを取出して、そのメソッドで描画した。

インスタンスからは、初めに1度だけGraphicsオブジェクトの参照を求め、変数に入れてしまいます。変数からオブジェクトを取出す方が、インスタンスにプロパティを問合せるよりアクセスが速いのです。したがって、最適化されたフレームアクションはは、つぎのスクリプト01-04-002のようになります。

スクリプト01-04-002【○】プロパティから得たオブジェクトの参照をまず変数にとる
    // フレームアクション
    // 空のMovieClipインスタンスmy_mcを配置
  1. var myGraphics:Graphics = my_mc.graphics;   // 初めにプロパティのオブジェクトを変数に代入
    // 変数のインスタンスを操作する
  2. myGraphics.lineStyle(2, 0x0000FF);
  3. myGraphics.beginFill(0x00FFFF);
  4. myGraphics.drawCircle(0, 0, 50);
  5. myGraphics.endFill();

01-04-02 配列から取出したオブジェクトに何度もアクセスするとき
配列に納めたインスタンスを取出して、何度もアクセスする場合があります。そのときは、取出したインスタンスを、型指定した変数に入れましょう。配列エレメントを毎回取出すより、変数に入れたオブジェクトを得る方が速いからです。さらに、配列エレメントにはデータ型がありません。変数を用いれば型指定できます(前述01-01「変数と関数にはデータ型を指定する」)。

お題は前述01-02「配列アクセスよりドットアクセスを使う」と同じく、タイムラインに配置した3つのMovieClipインスタンス(my0_mc〜my2_mc)に水平スクロールのアニメーションをさせることにします(図01-04-002)。スクリプトは、インスタンスを置いたタイムラインに、フレームアクションとして書きます(スクリプト01-04-003)。アニメーションは、DisplayObject.exitFrameイベント(定数Event.EXIT_FRAME)で扱います。

図01-04-002■タイムラインに置いた3つのMovieClipインスタンスを水平にスクロールさせる

3つのインスタンスは水平にスクロールし、ステージの端を超えると反対側の端から表れる。

スクロールするインスタンスは、あらかじめ配列に納めます(スクリプト01-04-003第2行目)。forステートメントで配列からインスタンスを順に取出して水平にスクロールさせ、ステージの端を超えたら反対側の端に動かします(第5〜10行目)。この処理の中で、インスタンスのDisplayObject.xプロパティに何度かアクセスしています。

スクリプト01-04-003【×】継続条件のプロパティや配列のエレメントに繰返しアクセスする
    // フレームアクション
    // 3つのMovieClipインスタンス(my0_mc〜my2_mc)を配置
  1. var nWidth:int = stage.stageWidth;
  2. var mcs_array:Array = [my0_mc, my1_mc, my2_mc];   // インスタンスの参照を配列に納める
  3. addEventListener(Event.ENTER_FRAME, xScroll);
  4. function xScroll(eventObject:Event):void {
  5.   for (var i:uint = 0; i < mcs_array.length; i++) {   // ループするたびにプロパティを調べる
        // 同じインスタンスを何度も配列から取出す
  6.     mcs_array[i].x += 5;
  7.     if (nWidth < mcs_array[i].x) {
  8.       mcs_array[i].x -= nWidth;
  9.     }
  10.   }
  11. }

同じインスタンスを何度も配列から取出すのは無駄です。以下のスクリプト01-04-004は、初めにMovieClipで型指定した変数に入れることにより最適化しています(第7行目)。また、forステートメントの継続条件に用いるArray.lengthプロパティの値も、ループするたびに調べられるので、予め変数にとっておきます(第3行目。後述03-01「配列エレメントのすべてを素速く処理するには」参照)。ステートメント数は増えても、速さが稼げます。

スクリプト01-04-004【○】プロパティ値や配列エレメントのインスタンスを予め変数にとる
    // フレームアクション
    // 3つのMovieClipインスタンス(my0_mc〜my2_mc)を配置
  1. var nWidth:int = stage.stageWidth;
  2. var mcs_array:Array = [my0_mc, my1_mc, my2_mc];   // インスタンスの参照を配列に納める
  3. var nLength:uint = mcs_array.length;   // プロパティ値を変数にとっておく
  4. addEventListener(Event.ENTER_FRAME, xScroll);
  5. function xScroll(eventObject:Event):void {
  6.   for (var i:uint = 0; i < nLength; i++) {
  7.     var my_mc:MovieClip = mcs_array[i];   // インスタンスを型指定した変数に入れる
        // 変数のインスタンスを操作する
  8.     my_mc.x += 5;
  9.     if (nWidth < my_mc.x) {
  10.       my_mc.x -= nWidth;
  11.     }
  12.   }
  13. }

Tips 01-04-001■型指定した変数にはコードヒントが働く
インスタンスを配列エレメントとして取出したときと異なり、型指定した変数に入れればコードヒントが働きます(図01-04-003)。

図01-04-003■型指定した変数にコードヒントが表示される

型指定した変数を使えば、コードヒントが働く。


01-04-03 最後に代入が必要なプロパティに注意
オブジェクトの参照が得られるプロパティについては、扱い方の異なるものがあります。DisplayObject.transformプロパティを例にしてその注意をご説明します。このプロパティから得られるTransformオブジェクトには、インスタンスを変換する情報の納められたさまざまなオブジェクトがプロパティとして備わっています。その中の、Transform.matrix3Dプロパティは、インスタンスを3次元空間で座標変換するMatrix3Dオブジェクトへの参照です。

Matrix3Dオブジェクトを操作するときは、前述01-04-01「プロパティから参照するオブジェクトにいくつもの処理を加えるとき」のGraphicsオブジェクトの例と同じく、参照を予め変数にとっておくとよいでしょう。たとえば、タイムラインに置いたMovieClipインスタンスmy_mcを、基準点を中心に水平に回してみます(図01-04-004)。インスタンスのMatrix3Dオブジェクトを使うと、平行移動(Matrix3D.appendTranslation())と回転(Matrix3D.appendRotation())のメソッドを組合わせて呼出すことになります。インスタンスを置いたタイムラインには、以下のようなフレームアクション(スクリプト01-04-005)を書きます。

図01-04-004■タイムラインに置いたMovieClipインスタンスを水平に回す

インスタンスのMatrix3Dオブジェクトに対して、平行移動と回転のメソッドを呼出す。


スクリプト01-04-005【○】何度も参照するプロパティのオブジェクトは予め変数にとる
    // フレームアクション
    // MovieClipインスタンスmy_mcを配置
  1. var my_mc:MovieClip;
  2. var nX:Number = my_mc.x;
  3. var nY:Number = my_mc.y;
  4. my_mc.z = 0;   // 3次元の操作を加える
    // 初めにプロパティのオブジェクトを変数に代入
  5. var myMatrix3D:Matrix3D = my_mc.transform.matrix3D;
  6. my_mc.addEventListener(Event.ENTER_FRAME, xRotate);
  7. function xRotate(eventObject:Event):void {
      // 変数のインスタンスを操作する
  8.   myMatrix3D.appendTranslation(-nX, -nY, 0);
  9.   myMatrix3D.appendRotation(5, Vector3D.Y_AXIS);
  10.   myMatrix3D.appendTranslation(nX, nY, 0);
  11. }

Tips 01-04-002■DisplayObject.transformから得られるTransform.matrix3Dはデフォルト値がnull
インスタンスのプロパティDisplayObject.transformから得られるTransform.matrix3Dは、デフォルトでは値がnullになっています。オーサリング時またはスクリプトで3次元の操作を加えることにより、Matrix3Dオブジェクトがつくられます。

上記スクリプトでDisplayObject.zプロパティを操作しているのは、そのためです。z座標値はデフォルトが0ですので、見た目には変わりません。けれど、この設定によりインスタンスにMatrix3Dオブジェクトができあがるのです。


Basics 01-04-001■Matrix3Dクラスによる3次元空間座標の扱い
Matrix3Dクラスの扱いについて詳しくは、gihyo.jp連載「ActionScript 3.0で始めるオブジェクト指向スクリプティング」第35回「Matrix3Dクラスによる座標変換」<http://gihyo.jp/dev/serial/01/as3/0035>以降、およびAdobeデベロッパーセンター「Matrix3Dクラス − 変換行列2」<http://www.adobe.com/jp/devnet/flash/articles/matrix3d_class.html>をお読みください。

DisplayObject.transformプロパティのTransformオブジェクトには、2次元空間の座標変換を扱うTransform.matrixプロパティもあります。前掲スクリプトに準じて、2次元平面(z軸)でインスタンスmy_mcを回すフレームアクションを考えます。ところが、my_mcを置いたタイムラインにつぎのフレームアクション(スクリプト01-04-006)を書いても、インスタンスはまったく動きません。

スクリプト01-04-006【×】変数に取出したオブジェクトを操作しただけでは動かないプロパティもある
    // フレームアクション
    // MovieClipインスタンスmy_mcを配置
  1. var my_mc:MovieClip;
  2. var nX:Number = my_mc.x;
  3. var nY:Number = my_mc.y;
    // 初めにプロパティのオブジェクトを変数に代入
  4. var myMatrix:Matrix = my_mc.transform.matrix;
  5. my_mc.addEventListener(Event.ENTER_FRAME, xRotate);
  6. function xRotate(eventObject:Event):void {
      // 変数のインスタンスを操作する
  7.   myMatrix.translate(-nX, -nY);
  8.   myMatrix.rotate(0.05);
  9.   myMatrix.translate(nX, nY);
  10. }

プロパティTransform.matrixTransform.matrix3Dと異なり、参照したMatrixオブジェクトに対してメソッドを呼出して操作しただけでは、インスタンスにその変換が加わりません。つぎのスクリプト01-04-007のように、改めてDisplayObject.transformTransform.matrix3Dプロパティに、そのMatrixオブジェクトを設定する必要があるのです(第11行目)。これでインスタンスは、時計回りに回転します(図01-04-005)。

スクリプト01-04-007【○】操作した変数のオブジェクトをプロパティに再設定する
    // フレームアクション
    // MovieClipインスタンスmy_mcを配置
  1. var my_mc:MovieClip;
  2. var nX:Number = my_mc.x;
  3. var nY:Number = my_mc.y;
    // 初めにプロパティのオブジェクトを変数に代入
  4. var myMatrix:Matrix = my_mc.transform.matrix;
  5. my_mc.addEventListener(Event.ENTER_FRAME, xRotate);
  6. function xRotate(eventObject:Event):void {
  7.   // 変数のインスタンスを操作する
  8.   myMatrix.translate(-nX, -nY);
  9.   myMatrix.rotate(0.05);
  10.   myMatrix.translate(nX, nY);
  11.   my_mc.transform.matrix = myMatrix;   // Matrixオブジェクトをプロパティに再設定
  12. }

図01-04-005■タイムラインに置いたMovieClipインスタンスを時計回りに回転

インスタンスのMatrixオブジェクトに対して、平行移動と回転のメソッドを呼出す。


Basics 01-04-002■Matrixクラスによる2次元平面座標の扱い
Matrixクラスの扱いについて詳しくは、Adobeデベロッパーセンター「Matrixクラス − 変換行列1」<http://www.adobe.com/jp/devnet/flash/articles/matrix_class.html>をお読みください。

このように、プロパティから取出したオブジェクトには、プロパティに再設定しないとその効果が適用されないものもあります。[ヘルプ]を確かめるなど、注意が必要です。

Tips 01-04-003■オブジェクトの参照か複製かの違い
Transform.matrix3Dプロパティは、Matrix3Dオブジェクトの参照をやり取りします。つまり、変数に入れたMatrix3DオブジェクトをTransform.matrix3Dプロパティに設定すれば、変数とプロパティとは同じオブジェクトを参照します。したがって、MovieClipインスタンスmy_mcのあるタイムラインにつぎのフレームアクションを書くと、trueが[出力]されます。

var myMatrix3D:Matrix3D = new Matrix3D();
my_mc.transform.matrix3D = myMatrix3D;
trace(myMatrix3D == my_mc.transform.matrix3D);   // 出力: true

それに対して、Transform.matrixプロパティでは、複製したMatrixオブジェクトが読み書きされます。そのため、以下のスクリプトでは、変数とプロパティは同じ内容の(複製された)異なるMatrixオブジェクトを参照するので、falseが[出力]されることになるのです。

var myMatrix:Matrix = new Matrix();
my_mc.transform.matrix = myMatrix;
trace(myMatrix == my_mc.transform.matrix);   // 出力: false


作成者: 野中文雄
更新日: 2011年1月23日 スクリプトに連番と行番号を追加。基礎知識としてのgihyo.jpのリンクをBasicsとした。
更新日: 2011年1月15日 例のタイトルを具体化
作成日: 2010年12月28日


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