サイトトップ

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

ActionScript 3.0 for 3D

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

SPL-04 マウスポインタの位置により立方体の頂点座標を回してテクスチャマッピング
4つ目のサンプルは、やはりVector3Dインスタンスで立方体の座標をマウスポインタの位置に応じて回し、2次元平面に透視投影したうえでテクスチャマッピングします(再掲図07-022)。前掲スクリプト07-009のお題です。

図07-022■立方体がマウスポインタの位置に応じて上下左右に回る(再掲)

立方体の8頂点座標が透視投影された2次元平面に、矩形の6面がカリングされたうえでテクスチャマッピングされる。

前掲スクリプト07-009と同じように、テクスチャマッピングする展開図は、ビットマップとして[ライブラリ]に納めます(再掲図07-021)。そして、ビットマップには、[クラス]としてImageを設定しておきます。

図07-021■立方体の6面を展開したビットマップ(再掲)

4面の上下に、上面と底面を加える。すると、u座標値が変わる。

ドキュメントクラスから立方体のワイヤーフレームを操作する
このお題も、クラスの組立ては前節SPL-03「立方体のワイヤーフレームをマウスポインタの位置に応じて回す」と基本的に同じです。ドキュメントクラスMainで立方体インスタンスの設定とアニメーションの指示を行い、もうひとつのクラスCubeが立方体インスタンスの操作に応じた座標計算とテクスチャマッピングを行います。

クラスMainは、以下のスクリプトSPL-010のとおり、前節のドキュメントクラスMain(スクリプトSPL-008)とほとんど同じ内容です。コンストラクタメソッドは立方体のインスタンスを生成・配置し(スクリプト第9〜15行目)、rotate()メソッドはDisplayObject.enterFrameイベント(定数Event.ENTER_FRAME)のリスナーとして登録され、マウスポインタの位置に応じて立方体のインスタンスを回します(スクリプト第16〜21行目)。

前節のドキュメントクラスMain(スクリプトSPL-008)と異なる点として、コンストラクタメソッドはスクリプト第10行目で立方体のクラスCubeのコンストラクタを呼出し、引数にテクスチャとなるビットマップ(クラスImage)のインスタンスを渡します。また、rotate()メソッドは、スクリプト第19行目でCubeインスタンスの同名のメソッドrotate()を呼出すとき、ふたつの引数として水平および垂直方向の回転角を指定しています(Maniac! SPL-002「クラスMainのrotate()メソッドの引数」参照)。

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

Maniac! SPL-002■クラスMainのrotate()メソッドの引数
前節のスクリプトSPL-008では、ドキュメントクラスMainのrotate()メソッドには水平および垂直方向の回転角をPointインスタンスで渡しました。それに対して前掲スクリプトSPL-010のMainクラスは、本文に述べたとおり、rotate()メソッドにふたつの数値を引数として指定しています。けれども、この違いには大きな意味はありません。

理由としては第1に、もととなったフレームアクション(スクリプト07-009)の処理と合わせたこと。第2に、引数はこのふたつの数値だけですので、わざわざPointインスタンスにする手間を省いたことです。


立方体の座標を計算してテクスチャマッピングするクラスの定義
お題とした前掲スクリプト07-009のフレームアクションから立方体の座標計算とテクスチャマッピングの処理を抜出して定義したクラスがCubeです(後掲スクリプトSPL-011)。初期設定をコンストラクタメソッドにまとめ、若干の追加と修正をしただけで、基本的な処理はもとのフレームアクションと変わりません。

コンストラクタメソッドCube()の修正部分は、以下に抜書きしたとおりです(スクリプト第19〜60行目)。引数は全部で3つです。第1引数(myTexture)は必須のBitmapDataインスタンスを受取ります。あとのふたつはオプションで、第2引数(edge)が立方体の1辺の長さのピクセル数、第3引数(nDistance)は焦点距離となる視点から投影面までの距離を示す数値です。オプションの引数には、デフォルト値が指定されています。

これらの引数値はスクリプト第20〜22行目で、それぞれプロパティ(第10〜11、15行目で宣言したtextureとunitおよびdistance)に代入します。これらのプロパティは、もとのフレームアクション(スクリプト07-009)と少し名前を変え、コンストラクタの引数で設定するようにしたほかは、とくに役割も変えていません。

  1.     private var unit:Number;
  2.     private var texture:BitmapData;
  1.     private var distance:Number;
  1.     public function Cube(myTexture:BitmapData, edge:Number = 100, nDistance:Number = 300) {
  2.       texture = myTexture;
  3.       unit = edge / 2;
  4.       myPerspective.focalLength = distance = nDistance;
  1.     }

立方体の頂点座標や頂点番号の組、そしてテクスチャのuvt座標値をそれぞれ納めるVectorインスタンス(vertices3Dとindices、およびuvtData)や透視投影用のMatrix3Dインスタンス(viewMatrix3D)の初期設定(後掲スクリプト第23〜58行目)は、もとのフレームアクション(スクリプト07-009)の処理をそのまま移しています。

つぎに、publicなメソッドrotate()のおもな修正部分を以下に抜出しました(後掲スクリプトSPL-011第61〜72行目)。このメソッドは、ドキュメントクラスMain(前掲スクリプトSPL-010)がDisplayObject.enterFrameイベント(定数Event.ENTER_FRAME)に登録したリスナーメソッド(rotate())から、水平および垂直方向の回転角を引数として呼出されます。

処理内容は、もとのフレームアクション(スクリプト07-009)とほぼ変わりません。ただし、クラスCubeはSpriteを継承し、描画はインスタンス自身に対して行います。したがって、スクリプト第68〜71行目は、自らのSprite.graphicsプロパティを参照して、テクスチャマッピングしています。

  1.     public function rotate(nRotationX, nRotationY):void {
  1.       worldMatrix3D.appendRotation(nRotationY, Vector3D.Y_AXIS);
  2.       worldMatrix3D.appendRotation(nRotationX, Vector3D.X_AXIS);
  1.       graphics.clear();
  2.       graphics.beginBitmapFill(texture);
  3.       graphics.drawTriangles(vertices2D, indices, uvtData, TriangleCulling.NEGATIVE);
  4.       graphics.endFill();
  5.     }

残るメソッドaddRectangleIndices()は、もとのフレームアクション(スクリプト07-009)とまったく同じです(スクリプト第73〜76行目)。さて、以上の修正を加えて定義したクラスCubeが、つぎのスクリプトSPL-011です(Tips SPL-006「クラスCubeのコンストラクタからのrotate()メソッド呼出し」参照)。

スクリプトSPL-011■立方体を座標でつくってテクスチャマッピングするクラスCube
    // ActionScript 3.0クラス定義ファイル: Cube.as
    // 立方体のインスタンスを生成・座標計算してテクスチャマッピングする
  1. package {
  2.   import flash.display.Sprite;
  3.   import flash.display.BitmapData;
  4.   import flash.display.TriangleCulling;
  5.   import flash.geom.PerspectiveProjection;
  6.   import flash.geom.Matrix3D;
  7.   import flash.geom.Vector3D;
  8.   import flash.geom.Utils3D;
  9.   public class Cube extends Sprite {
  10.     private var unit:Number;
  11.     private var texture:BitmapData;
  12.     private var vertices3D:Vector.<Number> = new Vector.<Number>();
  13.     private var indices:Vector.<int> = new Vector.<int>();
  14.     private var uvtData:Vector.<Number> = new Vector.<Number>();
  15.     private var distance:Number;
  16.     private var myPerspective:PerspectiveProjection = new PerspectiveProjection();
  17.     private var worldMatrix3D:Matrix3D = new Matrix3D();
  18.     private var viewMatrix3D:Matrix3D = new Matrix3D();
  19.     public function Cube(myTexture:BitmapData, edge:Number = 100, nDistance:Number = 300) {
  20.       texture = myTexture;
  21.       unit = edge / 2;
  22.       myPerspective.focalLength = distance = nDistance;
  23.       vertices3D.push(-unit, -unit, -unit);
  24.       vertices3D.push(unit, -unit, -unit);
  25.       vertices3D.push(unit, unit, -unit);
  26.       vertices3D.push(-unit, unit, -unit);
  27.       vertices3D.push(-unit, -unit, unit);
  28.       vertices3D.push(unit, -unit, unit);
  29.       vertices3D.push(unit, unit, unit);
  30.       vertices3D.push(-unit, unit, unit);
  31.       vertices3D.push(-unit, -unit, -unit);
  32.       vertices3D.push(-unit, unit, -unit);
  33.       vertices3D.push(-unit, -unit, unit);
  34.       vertices3D.push(unit, -unit, unit);
  35.       vertices3D.push(unit, unit, unit);
  36.       vertices3D.push(-unit, unit, unit);
  37.       addRectangleIndices(0, 1, 2, 3);
  38.       addRectangleIndices(1, 5, 6, 2);
  39.       addRectangleIndices(5, 4, 7, 6);
  40.       addRectangleIndices(4, 8, 9, 7);
  41.       addRectangleIndices(1, 0, 10, 11);
  42.       addRectangleIndices(3, 2, 12, 13);
  43.       uvtData.push(0, 1/3, 0);
  44.       uvtData.push(1/4, 1/3, 0);
  45.       uvtData.push(1/4, 2/3, 0);
  46.       uvtData.push(0, 2/3, 0);
  47.       uvtData.push(3/4, 1/3, 0);
  48.       uvtData.push(2/4, 1/3, 0);
  49.       uvtData.push(2/4, 2/3, 0);
  50.       uvtData.push(3/4, 2/3, 0);
  51.       uvtData.push(1, 1/3, 0);
  52.       uvtData.push(1, 2/3, 0);
  53.       uvtData.push(0, 0, 0);
  54.       uvtData.push(1/4, 0, 0);
  55.       uvtData.push(1/4, 1, 0);
  56.       uvtData.push(0, 1, 0);
  57.       viewMatrix3D.appendTranslation(0, 0, distance);
  58.       viewMatrix3D.append(myPerspective.toMatrix3D());
  59.       rotate(0, 0);
  60.     }
  61.     public function rotate(nRotationX, nRotationY):void {
  62.       var vertices2D:Vector.<Number> = new Vector.<Number>();
  63.       worldMatrix3D.appendRotation(nRotationY, Vector3D.Y_AXIS);
  64.       worldMatrix3D.appendRotation(nRotationX, Vector3D.X_AXIS);
  65.       var myMatrix3D:Matrix3D = worldMatrix3D.clone();
  66.       myMatrix3D.append(viewMatrix3D);
  67.       Utils3D.projectVectors(myMatrix3D, vertices3D, vertices2D, uvtData);
  68.       graphics.clear();
  69.       graphics.beginBitmapFill(texture);
  70.       graphics.drawTriangles(vertices2D, indices, uvtData, TriangleCulling.NEGATIVE);
  71.       graphics.endFill();
  72.     }
  73.     private function addRectangleIndices(n0:uint, n1:uint, n2:uint, n3:uint):void {
  74.       indices.push(n0, n1, n3);
  75.       indices.push(n1, n2, n3);
  76.     }
  77.   }
  78. }

Tips SPL-006■クラスCubeのコンストラクタからのrotate()メソッド呼出し
前掲スクリプトSPL-011のクラスCubeは、コンストラクタメソッドの最後に自らのrotate()メソッドを呼出しています(スクリプト第59行目)。これは、クラスMain(前掲スクリプトSPL-010)で登録したDisplayObject.enterFrameイベント(定数Event.ENTER_FRAME)のリスナーメソッド(rotate())から、初めて呼出されるよりも前にテクスチャマッピングしておくためのステートメントです。もっとも、その間はコンマ1秒足らずですので、省いても問題になることはないでしょう。

これで、立方体の頂点座標をマウスポインタの位置に応じて回し、テクスチャマッピングするサンプルのクラス定義ができました。クラスCubeのコンストラクタにオプションの第2および第3引数を渡せば、立方体の1辺の長さと焦点距離が変えられます。

たとえば、ドキュメントクラスMainのスクリプトSPL-010第10行目をつぎのように書替えれば、デフォルトより立方体のサイズは少し小さく、かつパースペクティブをきつくすることができます(図SPL-012)。

  1.     public function Main() {
  2.       // mySprite = new Cube(new Image(0, 0));
          mySprite = new Cube(new Image(0, 0), 75, 100);   // 第2および第3引数を追加
図SPL-012■コンストラクタの引数で立方体のサイズと焦点距離を変更
デフォルトよりも立方体のサイズは小さく、パースペクティブはきつくなった。

立方体のクラスにテクスチャマッピングの三角形を線描するメソッドの追加
立方体にテクスチャマッピングするクラスCubeにメソッドをひとつ加えます。Graphics.drawTriangles()メソッドで塗る三角形のアウトラインを描画できるようにします。メソッド名はsetLines()とし、ふたつの引数で線の太さとカラーを定めます。太さに0を渡すと、線は表示されません。クラスCubeの定義全体は、スクリプトSPL-012として後に掲げます。メソッドsetLines()の定義は、つぎのとおりとても簡潔です。

  1.     private var thickness:Number;
  2.     private var color:uint;
  1.     public function setLines(nThickness:Number = 0, nColor:uint = 0) {
  2.       thickness = nThickness;
  3.       color = nColor;
  4.     }

テクスチャマッピングするメソッドrotate()にも少し手を加えます。つぎのように、線の太さを納めたプロパティ(thickness)の値が0でなければ、線のスタイルを設定します(スクリプト第71〜73行目)。なお、if条件に数値を渡すと0(あるいはNaN)以外はtrueと評価されます。

  1.     public function rotate(nRotationX, nRotationY):void {
  1.       graphics.clear();
  2.       if (thickness) {
  3.         graphics.lineStyle(thickness, color);
  4.       }
  5.       graphics.beginBitmapFill(texture);
  6.       graphics.drawTriangles(vertices2D, indices, uvtData, TriangleCulling.NEGATIVE);
  7.       graphics.endFill();
  8.     }

これらの追加を行ったクラスCubeの定義は、つぎのスクリプトSPL-012のとおりです。

スクリプトSPL-012■立方体にテクスチャマッピングの三角形を線描するメソッドが加わったクラスCube
    // ActionScript 3.0クラス定義ファイル: Cube.as
    // 立方体のインスタンスを生成・座標計算してテクスチャマッピングする
  1. package {
  2.   import flash.display.Sprite;
  3.   import flash.display.BitmapData;
  4.   import flash.display.TriangleCulling;
  5.   import flash.geom.PerspectiveProjection;
  6.   import flash.geom.Matrix3D;
  7.   import flash.geom.Vector3D;
  8.   import flash.geom.Utils3D;
  9.   public class Cube extends Sprite {
  10.     private var unit:Number;
  11.     private var texture:BitmapData;
  12.     private var vertices3D:Vector.<Number> = new Vector.<Number>();
  13.     private var indices:Vector.<int> = new Vector.<int>();
  14.     private var uvtData:Vector.<Number> = new Vector.<Number>();
  15.     private var distance:Number;
  16.     private var myPerspective:PerspectiveProjection = new PerspectiveProjection();
  17.     private var worldMatrix3D:Matrix3D = new Matrix3D();
  18.     private var viewMatrix3D:Matrix3D = new Matrix3D();
  19.     private var thickness:Number;
  20.     private var color:uint;
  21.     public function Cube(myTexture:BitmapData, edge:Number = 100, nDistance:Number = 300) {
  22.       texture = myTexture;
  23.       unit = edge / 2;
  24.       myPerspective.focalLength = distance = nDistance;
  25.       vertices3D.push(-unit, -unit, -unit);
  26.       vertices3D.push(unit, -unit, -unit);
  27.       vertices3D.push(unit, unit, -unit);
  28.       vertices3D.push(-unit, unit, -unit);
  29.       vertices3D.push(-unit, -unit, unit);
  30.       vertices3D.push(unit, -unit, unit);
  31.       vertices3D.push(unit, unit, unit);
  32.       vertices3D.push(-unit, unit, unit);
  33.       vertices3D.push(-unit, -unit, -unit);
  34.       vertices3D.push(-unit, unit, -unit);
  35.       vertices3D.push(-unit, -unit, unit);
  36.       vertices3D.push(unit, -unit, unit);
  37.       vertices3D.push(unit, unit, unit);
  38.       vertices3D.push(-unit, unit, unit);
  39.       addRectangleIndices(0, 1, 2, 3);
  40.       addRectangleIndices(1, 5, 6, 2);
  41.       addRectangleIndices(5, 4, 7, 6);
  42.       addRectangleIndices(4, 8, 9, 7);
  43.       addRectangleIndices(1, 0, 10, 11);
  44.       addRectangleIndices(3, 2, 12, 13);
  45.       uvtData.push(0, 1/3, 0);
  46.       uvtData.push(1/4, 1/3, 0);
  47.       uvtData.push(1/4, 2/3, 0);
  48.       uvtData.push(0, 2/3, 0);
  49.       uvtData.push(3/4, 1/3, 0);
  50.       uvtData.push(2/4, 1/3, 0);
  51.       uvtData.push(2/4, 2/3, 0);
  52.       uvtData.push(3/4, 2/3, 0);
  53.       uvtData.push(1, 1/3, 0);
  54.       uvtData.push(1, 2/3, 0);
  55.       uvtData.push(0, 0, 0);
  56.       uvtData.push(1/4, 0, 0);
  57.       uvtData.push(1/4, 1, 0);
  58.       uvtData.push(0, 1, 0);
  59.       viewMatrix3D.appendTranslation(0, 0, distance);
  60.       viewMatrix3D.append(myPerspective.toMatrix3D());
  61.       rotate(0, 0);
  62.     }
  63.     public function rotate(nRotationX, nRotationY):void {
  64.       var vertices2D:Vector.<Number> = new Vector.<Number>();
  65.       worldMatrix3D.appendRotation(nRotationY, Vector3D.Y_AXIS);
  66.       worldMatrix3D.appendRotation(nRotationX, Vector3D.X_AXIS);
  67.       var myMatrix3D:Matrix3D = worldMatrix3D.clone();
  68.       myMatrix3D.append(viewMatrix3D);
  69.       Utils3D.projectVectors(myMatrix3D, vertices3D, vertices2D, uvtData);
  70.       graphics.clear();
  71.       if (thickness) {
  72.         graphics.lineStyle(thickness, color);
  73.       }
  74.       graphics.beginBitmapFill(texture);
  75.       graphics.drawTriangles(vertices2D, indices, uvtData, TriangleCulling.NEGATIVE);
  76.       graphics.endFill();
  77.     }
  78.     public function setLines(nThickness:Number = 0, nColor:uint = 0) {
  79.       thickness = nThickness;
  80.       color = nColor;
  81.     }
  82.     private function addRectangleIndices(n0:uint, n1:uint, n2:uint, n3:uint):void {
  83.       indices.push(n0, n1, n3);
  84.       indices.push(n1, n2, n3);
  85.     }
  86.   }
  87. }

それでは、ドキュメントクラスMainにもメソッドを加えて、クラスCubeの新たなメソッドsetLines()を呼出してみます。以下のスクリプトSPL-013のように、ふたつのメソッドaddLines()とremoveLines()を定義しました。

初めはコンストラクタメソッドで、CubeインスタンスのInteractiveObject.clickイベント(定数MouseEvent.CLICK)に、リスナーメソッドとしてaddLines()を登録します(スクリプト第15行目)。Cubeインスタンスをクリックすれば、addLines()メソッド(スクリプト第23〜27行目)が呼び出されて、インスタンスに三角形のアウトラインを描きます(図SPL-013)。すると、このメソッドはリスナーから削除され、替わりにremoveLines()メソッドが登録されます。

つぎにCubeインスタンスをクリックすると、removeLines()メソッド(スクリプト第28〜32行目)が呼出されて、インスタンスに描かれた三角形のアウトラインは消えます。ここで、またリスナーメソッドがaddLines()に切り替わります。つまり、Cubeインスタンスをクリックするたびに、三角形の線描と消去を繰返すことになります。

スクリプトSPL-013■メソッドを追加したドキュメントクラスMain
    // ActionScript 3.0クラス定義ファイル: Main.as
    // ドキュメントクラスに設定
  1. package {
  2.   import flash.display.Sprite;
  3.   import flash.events.Event;
  4.   import flash.events.MouseEvent;
  5.   public class Main extends Sprite {
  6.     private var nX:Number = stage.stageWidth / 2;
  7.     private var nY:Number = stage.stageHeight / 2;
  8.     var nDeceleration:Number = 0.3;
  9.     var mySprite:Cube;
  10.     public function Main() {
  11.       mySprite = new Cube(new Image(0, 0));
  12.       addChild(mySprite);
  13.       mySprite.x = nX;
  14.       mySprite.y = nY;
  15.       mySprite.addEventListener(MouseEvent.CLICK, addLines);
  16.       addEventListener(Event.ENTER_FRAME, rotate);
  17.     }
  18.     private function rotate(eventObject:Event):void {
  19.       var nRotationY:Number = mySprite.mouseX * nDeceleration;
  20.       var nRotationX:Number = mySprite.mouseY * nDeceleration;
  21.       mySprite.rotate(nRotationX, nRotationY);
  22.     }
  23.     private function addLines(eventObject:MouseEvent):void {
  24.       mySprite.setLines(2, 0x00FFFF);
  25.       mySprite.removeEventListener(MouseEvent.CLICK, addLines);
  26.       mySprite.addEventListener(MouseEvent.CLICK, removeLines);
  27.     }
  28.     private function removeLines(eventObject:MouseEvent):void {
  29.       mySprite.setLines();
  30.       mySprite.removeEventListener(MouseEvent.CLICK, removeLines);
  31.       mySprite.addEventListener(MouseEvent.CLICK, addLines);
  32.     }
  33.   }
  34. }

図SPL-013■立方体のインスタンスをクリックするとテクスチャマッピングの三角形が線描される
立方体のインスタンスをクリックするたびに、テクスチャマッピングする三角形の線描と消去が切り替わる。

[Prev]


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


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