サイトトップ

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

ActionScript 3.0 for 3D

□Samples サンプルをクラスで定義する

SPL-03 立方体のワイヤーフレームをマウスポインタの位置に応じて回す
3つ目のサンプルは、Vector3Dインスタンスで立方体の座標をマウスポインタの位置に応じて回し、2次元平面にワイヤーフレームで描きます(再掲図06-009)。前掲スクリプト06-003のお題です。

図06-009■ふたつの正方形と各4頂点を結んで立方体のワイヤーフレームが描かれる(再掲)
頂点番号の組にしたがって、ふたつの正方形と4つの頂点をそれぞれ結んで、立方体のワイヤーフレームができる(左図)。手前の辺が奥にあるように錯覚すると、立方体がゆがんで逆に回っているように見える(右図は一番手前の垂直の辺を隠した)。

ドキュメントクラスから立方体のワイヤーフレームを操作する
今回のお題は、さほど難しくありません。まず、クラスの組立ては前節SPL-02「立方体のインスタンスをマウスポインタの位置に応じて回す」と基本的に同じでよいでしょう。つまり、ドキュメントクラスで立方体インスタンスの設定とアニメーションの指示を行い、もうひとつのクラスは立方体インスタンスの操作に応じたワイヤーフレームを描きます。ドキュメントクラスをMain、立方体のワイヤーフレームを描くクラスはWireCubeとします。

クラスMainは、前節の同じ名前のクラスよりさらに簡潔になります。クラスWireCubeも、座標計算がおもですので、フレームアクションをあまり修正する必要がありません。早速、ドキュメントクラスMainから定義していきます。

クラスMainは、以下のスクリプトSPL-008のとおり、コンストラクタメソッドとひとつのメソッドrotate()で成立ちます。コンストラクタは立方体のインスタンスを生成・配置し(スクリプト第10〜16行目)、rotate()メソッドはDisplayObject.enterFrameイベント(定数Event.ENTER_FRAME)のリスナーとして登録され、マウスポインタの位置に応じて立方体のインスタンスを回します(スクリプト第17〜20行目)。

スクリプトSPL-008■ドキュメントクラスに設定するクラスMain
    // ActionScript 3.0クラス定義ファイル: Main.as
    // ドキュメントクラスに設定
  1. package {
  2.   import flash.display.Sprite;
  3.   import flash.events.Event;
  4.   import flash.geom.Point;
  5.   public class Main extends Sprite {
  6.     private var nX:Number = stage.stageWidth / 2;
  7.     private var nY:Number = stage.stageHeight / 2;
  8.     private var nDeceleration:Number = 0.3;
  9.     private var mySprite:WireCube;
  10.     public function Main() {
  11.       mySprite = new WireCube(75, 3, 0x0000FF);
  12.       addChild(mySprite);
  13.       mySprite.x = nX;
  14.       mySprite.y = nY;
  15.       addEventListener(Event.ENTER_FRAME, rotate);
  16.     }
  17.     private function rotate(eventObject:Event):void {
  18.       var myRotation:Point =
          new Point(mySprite.mouseX * nDeceleration, mySprite.mouseY * nDeceleration);
  19.       mySprite.rotate(myRotation);
  20.     }
  21.   }
  22. }

ドキュメントクラスMainからクラスWireCubeへのアクセスを確かめておきましょう。コンストラクタメソッドは、スクリプト第11行目でクラスWireCubeのコンストラクタを呼出しています。このとき、オプションの引数を3つ渡せるようにします。

new WireCube(1辺の長さ, 線の太さ, 線のカラー値)

クラスWireCubeはSpriteクラスを継承しますので、スクリプト第12〜14行目のように表示リストに加え、位置を定めることができます。

メソッドrotate()からは、スクリプト第19行目でWireCubeインスタンスのrotate()メソッドを呼出しています。引数は、y軸回りとx軸回りの回転角をプロパティに納めたPointインスタンスで渡します。


立方体のワイヤーフレームを描くクラスの定義
前掲スクリプト06-003のフレームアクションから立方体の座標計算と描画の処理を抜出して定義したクラスがWireCubeです(後掲スクリプトSPL-009)。初期設定をコンストラクタメソッドにまとめ、若干の追加と修正をしただけで、基本的な処理はフレームアクションと変わりません。

コンストラクタメソッドは、以下のとおりです。前述のとおり、引数には立方体の1辺の長さとワイヤーフレームの線の太さ、およびそのカラー値を受取って、プロパティに代入します(スクリプト第13〜17行目)。それらのプロパティ宣言も、クラスに加えました(スクリプト第11〜12行目)。なお、少し名前を変えたプロパティ(focalLength)もあります。立方体の頂点座標(変数vertices)と頂点番号の組(変数indices)をそれぞれVectorインスタンスで納める処理は、前掲スクリプト06-003と同じです。

  1.     private var unit:Number;
  2.     private var vertices:Vector.<Vector3D> = new Vector.<Vector3D>();
  3.     private var indices:Vector.<Vector.<uint>> = new Vector.<Vector.<uint>>();
  4.     private var focalLength:Number;
  5.     private var thickness:Number;
  6.     private var color:uint;
  7.     public function WireCube(edge:Number = 100, nThickness:Number = 2, nColor:uint = 0) {
  8.       unit = edge / 2;
  9.       focalLength = unit * 10;
  10.       thickness = nThickness;
  11.       color = nColor;
  12.       vertices.push(new Vector3D(-unit, -unit, -unit));
  13.       vertices.push(new Vector3D(unit, -unit, -unit));
  14.       vertices.push(new Vector3D(unit, unit, -unit));
  15.       vertices.push(new Vector3D(-unit, unit, -unit));
  16.       vertices.push(new Vector3D(-unit, -unit, unit));
  17.       vertices.push(new Vector3D(unit, -unit, unit));
  18.       vertices.push(new Vector3D(unit, unit, unit));
  19.       vertices.push(new Vector3D(-unit, unit, unit));
  20.       indices.push(Vector.<uint>([0, 1, 2, 3]));
  21.       indices.push(Vector.<uint>([4, 5, 6, 7]));
  22.       indices.push(Vector.<uint>([0, 4]));
  23.       indices.push(Vector.<uint>([1, 5]));
  24.       indices.push(Vector.<uint>([2, 6]));
  25.       indices.push(Vector.<uint>([3, 7]));
  26.     }

すでに確かめたとおり、ドキュメントクラスMainはDisplayObject.enterFrameイベント(定数Event.ENTER_FRAME)に登録したリスナーメソッド(rotate())から、publicなメソッドrotate()を呼出します。メソッドの引数にはつぎのようにPointインスタンスで、y軸とx軸回りの角度が渡されます(スクリプト第33行目)。これは、前掲スクリプト06-003のリスナー関数xRotate()の処理を、クラスMainとWireCubeで分け合ったかたちになります。

  1.     public function rotate(myRotation:Point):void {
  2.       transformVertices(vertices, myRotation);
  3.       var vertices2D:Vector.<Point> = getVertices2D(vertices);
  4.       drawCube(vertices2D, indices);
  5.     }

以下のクラスWireCubeには、ほかに4つのメソッドtransformVertices()、getVertices2D()、drawCube()、drawLines()が定められています。これらは前掲スクリプト06-003の関数xTransform()、xGetVertices2D()、xDraw()、xDrawLines()と、それぞれ名前を少しずつ変えたほかはほぼそのまま内容の処理です。

メソッドdrawCube()とdrawLines()についてはもうひとつ、描画するGraphicsオブジェクトの参照が以下のようにWireCubeインスタンス自身のプロパティになっています(スクリプト第58〜83行目)。前掲スクリプト06-003では、描画用のSpriteインスタンスを生成しそのSprite.graphicsプロパティに線描しました。けれど、WireCubeはSpriteクラスを継承するので、自身のSprite.graphicsプロパティをもつからです(Tips SPL-004「スクリプトを[検索して置換]」参照)。

  1.     private function drawCube(vertices2D:Vector.<Point>, myIndices:Vector.<Vector.<uint>>):void {
  1.       graphics.clear();
  1.     }
  2.     function drawLines(vertices2D:Vector.<Point>):void {
  1.       graphics.lineStyle(thickness, color);   // 引数をプロパティで指定
  2.       graphics.moveTo(myPoint.x, myPoint.y);
  3.       for (var i:uint = 0; i < nLength; i++) {
  4.         myPoint = vertices2D[i];
  5.         graphics.lineTo(myPoint.x, myPoint.y);
  6.       }
  7.     }

Tips SPL-004■スクリプトを[検索して置換]
スクリプト内のプロパティやメソッド名、あるいは参照先をまとめて変えたいときには、[編集]メニューの[検索して置換]が使えます(図SPL-010)。ただ、[検索する文字列]が対象外の文字列の一部と一致することなどもありますから、慎重に行いましょう。

図SPL-010■[検索して置換]ダイアログボックス
スクリプト内から[検索する文字列]を探して、[置換]する文字列に替えられる。

以上の追加・修正を加えて定義したのが、つぎの立方体を座標でつくってワイヤーフレームで描くクラスWireCubeです(スクリプトSPL-009)。

スクリプトSPL-009■立方体を座標でつくってワイヤーフレームで描くクラスWireCube
    // ActionScript 3.0クラス定義ファイル: WireCube.as
    // 立方体のワイヤーフームインスタンスを生成・表示する
  1. package {
  2.   import flash.display.Sprite;
  3.   import flash.geom.Matrix3D;
  4.   import flash.geom.Vector3D;
  5.   import flash.geom.Point;
  6.   public class WireCube extends Sprite {
  7.     private var unit:Number;
  8.     private var vertices:Vector.<Vector3D> = new Vector.<Vector3D>();
  9.     private var indices:Vector.<Vector.<uint>> = new Vector.<Vector.<uint>>();
  10.     private var focalLength:Number;
  11.     private var thickness:Number;
  12.     private var color:uint;
  13.     public function WireCube(edge:Number = 100, nThickness:Number = 2, nColor:uint = 0) {
  14.       unit = edge / 2;
  15.       focalLength = unit * 10;
  16.       thickness = nThickness;
  17.       color = nColor;
  18.       vertices.push(new Vector3D(-unit, -unit, -unit));
  19.       vertices.push(new Vector3D(unit, -unit, -unit));
  20.       vertices.push(new Vector3D(unit, unit, -unit));
  21.       vertices.push(new Vector3D(-unit, unit, -unit));
  22.       vertices.push(new Vector3D(-unit, -unit, unit));
  23.       vertices.push(new Vector3D(unit, -unit, unit));
  24.       vertices.push(new Vector3D(unit, unit, unit));
  25.       vertices.push(new Vector3D(-unit, unit, unit));
  26.       indices.push(Vector.<uint>([0, 1, 2, 3]));
  27.       indices.push(Vector.<uint>([4, 5, 6, 7]));
  28.       indices.push(Vector.<uint>([0, 4]));
  29.       indices.push(Vector.<uint>([1, 5]));
  30.       indices.push(Vector.<uint>([2, 6]));
  31.       indices.push(Vector.<uint>([3, 7]));
  32.     }
  33.     public function rotate(myRotation:Point):void {
  34.       transformVertices(vertices, myRotation);
  35.       var vertices2D:Vector.<Point> = getVertices2D(vertices);
  36.       drawCube(vertices2D, indices);
  37.     }
  38.     private function transformVertices(myVertices:Vector.<Vector3D>, myRotation:Point):void {
  39.       var nLength:uint = myVertices.length;
  40.       var myMatrix3D:Matrix3D = new Matrix3D();
  41.       myMatrix3D.appendRotation(myRotation.x, Vector3D.Y_AXIS);
  42.       myMatrix3D.appendRotation(myRotation.y, Vector3D.X_AXIS);
  43.       for (var i:int = 0; i<nLength; i++) {
  44.         myVertices[i] = myMatrix3D.transformVector(myVertices[i]);
  45.       }
  46.     }
  47.     private function getVertices2D(myVertices:Vector.<Vector3D>):Vector.<Point> {
  48.       var vertices2D:Vector.<Point> = new Vector.<Point>();
  49.       var nLength:uint = myVertices.length;
  50.       for (var i:uint = 0; i < nLength; i++) {
  51.         var myVector3D:Vector3D = myVertices[i].clone();
  52.         myVector3D.w = (focalLength + myVector3D.z) / focalLength;
  53.         myVector3D.project();
  54.         vertices2D.push(new Point(myVector3D.x, myVector3D.y));
  55.       }
  56.       return vertices2D;
  57.     }
  58.     private function drawCube(vertices2D:Vector.<Point>, myIndices:Vector.<Vector.<uint>>):void {
  59.       var nLength:uint = myIndices.length;
  60.       graphics.clear();
  61.       for (var i:uint = 0; i < nLength; i++) {
  62.         var myVertices:Vector.<Point> = new Vector.<Point>();
  63.         var myIndex:Vector.<uint> = myIndices[i];
  64.         var nLength2:uint = myIndex.length;
  65.         for (var j:uint = 0; j < nLength2; j++) {
  66.           myVertices.push(vertices2D[myIndex[j]]);
  67.         }
  68.         drawLines(myVertices);
  69.       }
  70.     }
  71.     function drawLines(vertices2D:Vector.<Point>):void {
  72.       var nLength:uint = vertices2D.length;
  73.       var myPoint:Point = vertices2D[nLength - 1];
  74.       if (nLength < 3) {
  75.         --nLength;
  76.       }
  77.       graphics.lineStyle(thickness, color);
  78.       graphics.moveTo(myPoint.x, myPoint.y);
  79.       for (var i:uint = 0; i < nLength; i++) {
  80.         myPoint = vertices2D[i];
  81.         graphics.lineTo(myPoint.x, myPoint.y);
  82.       }
  83.     }
  84.   }
  85. }

MainクラスはWireCubeクラスのコンストラクタに3つの引数を渡しましたので、その指定にしたがった大きさおよび線の太さとカラーの立方体ワイヤーフレームが描画され、マウスポインタの位置に応じて回ります(図SPL-011)。

図SPL-011■指定した大きさと線の太さ・カラーの立方体がマウスポインタに合わせて回る
コンストラクタの引数で、立方体の大きさおよび線の太さ・カラーが指定できる。

Tips SPL-005■エレメントを渡してVectorインスタンスを生成する新しいシンタックス
前述Column 05「Vectorインスタンスの型変換」でご紹介したとおり、Flash Professional CS5からエレメントを納めたインスタンスの生成がシンタックスとして定められました。このシンタックスを使うと、WireCube()コンストラクタメソッドはVector()関数を用いることなく、つぎのように書けます(スクリプト第26〜31行目)。

  1.     public function WireCube(edge:Number = 100, nThickness:Number = 2, nColor:uint = 0) {
  2.       unit = edge / 2;
  3.       focalLength = unit * 10;
  4.       thickness = nThickness;
  5.       color = nColor;
  6.       vertices.push(new Vector3D(-unit, -unit, -unit));
  7.       vertices.push(new Vector3D(unit, -unit, -unit));
  8.       vertices.push(new Vector3D(unit, unit, -unit));
  9.       vertices.push(new Vector3D(-unit, unit, -unit));
  10.       vertices.push(new Vector3D(-unit, -unit, unit));
  11.       vertices.push(new Vector3D(unit, -unit, unit));
  12.       vertices.push(new Vector3D(unit, unit, unit));
  13.       vertices.push(new Vector3D(-unit, unit, unit));
  14.       indices.push(new <uint>[0, 1, 2, 3]);
  15.       indices.push(new <uint>[4, 5, 6, 7]);
  16.       indices.push(new <uint>[0, 4]);
  17.       indices.push(new <uint>[1, 5]);
  18.       indices.push(new <uint>[2, 6]);
  19.       indices.push(new <uint>[3, 7]);
  20.     }

[Prev/Next]


作成者: 野中文雄
作成日: 2010年4月23日


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