ActionScript 3.0で始めるオブジェクト指向スクリプティング

第50回 立方体の4面にテクスチャを貼って上下左右に回す

この記事を読むのに必要な時間:およそ 11 分

立方体を上下左右に回す

ここまでくれば,垂直に回すのもすでに学んだことの復習に過ぎない。手を加えるのはふたつの関数だ。できあがったフレームアクション全体をスクリプト3に掲げた。

第1は,DisplayObject.enterFrameイベントのリスナー関数(xRotate())だ。マウスポインタのy座標値から,x軸で回す垂直方向の角度を求める。そして,y軸とx軸の回転角を座標変換の関数(xTransform())に引数として渡す。すると第2に,座標変換の関数に垂直回転の角度を新たな引数として加えなければならない。そして,その引数値でMatrix3Dオブジェクト(worldMatrix3D)にx軸の垂直回転を加える。また,直前の頂点座標をふたつの軸で回転するには,後から加える変換のメソッドMatrix3D.appendRotation()を使わなければならない※2⁠。

スクリプト3 3次元空間で上下左右に回す立方体の4面にテクスチャマッピング

// フレームアクション
var nUnit:Number = 100 / 2;
var mySprite:Sprite = new Sprite();
var myTexture:BitmapData = new Image();
var vertices:Vector.<Number> = new Vector.<Number>();
var indices:Vector.<int> = new Vector.<int>();
var uvtData:Vector.<Number> = new Vector.<Number>();
var nDeceleration:Number = 0.3;
var myGraphics:Graphics = mySprite.graphics;
var myPerspective:PerspectiveProjection = transform.perspectiveProjection;
var worldMatrix3D:Matrix3D = new Matrix3D();
var viewMatrix3D:Matrix3D = myPerspective.toMatrix3D();
var nFaces:uint = 4;
var faces:Vector.<Vector.<uint>> = new Vector.<Vector.<uint>>();
var centers:Vector.<Vector3D> = new Vector.<Vector3D>();
faces.push(new <uint>[0, 1, 2, 3]);
faces.push(new <uint>[1, 5, 6, 2]);
faces.push(new <uint>[5, 4, 7, 6]);
faces.push(new <uint>[4, 8, 9, 7]);
centers.push(new Vector3D(0, 0, -nUnit));
centers.push(new Vector3D(nUnit, 0, 0));
centers.push(new Vector3D(0, 0, nUnit));
centers.push(new Vector3D(-nUnit, 0, 0));
viewMatrix3D.prependTranslation(0, 0, myPerspective.focalLength);
mySprite.x = stage.stageWidth / 2;
mySprite.y = stage.stageHeight / 2;
vertices.push(-nUnit, -nUnit, -nUnit);
vertices.push(nUnit, -nUnit, -nUnit);
vertices.push(nUnit, nUnit, -nUnit);
vertices.push(-nUnit, nUnit, -nUnit);
vertices.push(-nUnit, -nUnit, nUnit);
vertices.push(nUnit, -nUnit, nUnit);
vertices.push(nUnit, nUnit, nUnit);
vertices.push(-nUnit, nUnit, nUnit);
vertices.push(-nUnit, -nUnit, -nUnit);
vertices.push(-nUnit, nUnit, -nUnit);
for (var i:uint = 0; i < nFaces; i++) {
  addRectangleIndices(faces[i]);  
}
uvtData.push(0, 0, 0);
uvtData.push(1/4, 0, 0);
uvtData.push(1/4, 1, 0);
uvtData.push(0, 1, 0);
uvtData.push(3/4, 0, 0);
uvtData.push(2/4, 0, 0);
uvtData.push(2/4, 1, 0);
uvtData.push(3/4, 1, 0);
uvtData.push(1, 0, 0);
uvtData.push(1, 1, 0);
addChild(mySprite);
addEventListener(Event.ENTER_FRAME, xRotate);
function xRotate(eventObject:Event):void {
  var nRotationY:Number = mySprite.mouseX * nDeceleration;
  var nRotationX:Number = mySprite.mouseY * nDeceleration;   // 追加
  var vertices2D:Vector.<Number> = new Vector.<Number>();
  // xTransform(vertices2D, nRotationY);
  xTransform(vertices2D, nRotationY, nRotationX);
  xSetOrder();
  xDraw(vertices2D);
}
// function xTransform(vertices2D:Vector.<Number>, myRotation:Number):void {
function xTransform(vertices2D:Vector.<Number>, myRotationY:Number, myRotationX:Number):void {
  // worldMatrix3D.prependRotation(myRotation, Vector3D.Y_AXIS);
  worldMatrix3D.appendRotation(myRotationY, Vector3D.Y_AXIS);
  worldMatrix3D.appendRotation(myRotationX, Vector3D.X_AXIS);   // 追加
  var myMatrix3D:Matrix3D = worldMatrix3D.clone();
  myMatrix3D.append(viewMatrix3D);
  Utils3D.projectVectors(myMatrix3D, vertices, vertices2D, uvtData);
}
function xDraw(vertices2D:Vector.<Number>):void {
  myGraphics.clear();
  myGraphics.beginBitmapFill(myTexture);
  myGraphics.drawTriangles(vertices2D, indices, uvtData);
  myGraphics.endFill();
}
function addRectangleIndices(face:Vector.<uint>):void {
  indices.push(face[0], face[1], face[3]);
  indices.push(face[1], face[2], face[3]);
}
function xSetOrder():void {
  var transformedFaces:Vector.<Array> = new Vector.<Array>();
  for (var i:uint = 0; i < nFaces; i++) {
    var transformedVector3D:Vector3D = worldMatrix3D.transformVector(centers[i]);
    transformedFaces[i] = [transformedVector3D, faces[i]];
  }
  transformedFaces.sort(compare);
  indices.length = 0;
  for (var j:uint = 0; j < nFaces; j++) {
    addRectangleIndices(transformedFaces[j][1]);
  }
}
function compare(a:Array, b:Array):Number {
  var nA:Number = a[0].z;
  var nB:Number = b[0].z;
  if (nA < nB) {
    return 1;
  } else if (nA > nB) {
    return -1;
  } else {
    return 0;
  }
}

[ムービープレビュー]を確かめると,立方体の4面がマウスポインタの位置に応じて上下左右に回る図1⁠。この動きの見た目は,第38回「z座標値に応じて重ね順を変える」でつくったムービーと同じだ。しかし,テクスチャマッピングの方が優れている点は大きくふたつある。

第1に,球やドーナッツ型など,複雑なかたちが自在につくれる。DisplayObject(のサブクラスの)インスンスを組合わせた形状にはかぎりがある。第2は,処理の軽さだ。テクスチャマッピングの直前まで,演算はすべて座標に対して行うだけで,描画に関わらない。そして,描画するインスタンスはひとつで済む。

図1 上下左右に回した立方体に面の前後を整えて塗る

図1 上下左右に回した立方体に面の前後を整えて塗る 図1 上下左右に回した立方体に面の前後を整えて塗る

テクスチャマッピングは今回で終える。次回は3次元空間座標を回転するMatrix3Dクラスのメソッドについて,少し深堀りしてみよう。

※2
変換を加えるのが後か先かの違いについては,第36回Matrix3Dクラスの後から加える変換を参照してほしい。前回のスクリプトではy軸だけで水平に回したので,後でも先でも変わりがなかった。

著者プロフィール

野中文雄(のなかふみお)

ソフトウェアトレーナー,テクニカルライター,オーサリングエンジニア。上智大学法学部卒,慶応義塾大学大学院経営管理研究科修士課程修了(MBA)。独立系パソコン販売会社で,総務・人事,企画,外資系企業担当営業などに携わる。その後,マルチメディアコンテンツ制作会社に転職。ソフトウェアトレーニング,コンテンツ制作などの業務を担当する。2001年11月に独立。Web制作者に向けた情報発信プロジェクトF-siteにも参加する。株式会社ロクナナ取締役(非常勤)。

URLhttp://www.FumioNonaka.com/

著書