roadProgram的专栏

一个程序员的路

转 第十五章 3D 基础 (3)(as3.0)

坐标旋转
     接下来是 3D  坐标旋转。这里要比第十和十一章中的 2D  坐标旋转要稍微复杂一些。
我们不仅可以在三个不同的轴上旋转,还可以同时在两个以上的轴上进行旋转。
     在 2D 坐标旋转中物体是绕着 z  轴旋转的,如图 15-9  所示。想一想命运之轮(Wheel of
Fortune)这类游戏---轮轴从轮盘的中心穿过。轮轴就是 z 轴。只改变 x  和 y  坐标。

转 <wbr>第十五章 <wbr>3D <wbr>基础 <wbr>(3)(as3.0)

图 15-9  z 轴旋转
     在 3D 中,我们也可以在 y  或 z  轴上旋转。x  轴的旋转如同车轮向前滚动,如图 15-10
所示。这时轮轴就是 x 轴。只改变 y  和 z  上的点。

转 <wbr>第十五章 <wbr>3D <wbr>基础 <wbr>(3)(as3.0)

图 15-10 x  轴旋转
 y  轴上的旋转,请想象一下老的唱片机,如图 15-11  所示。轴心是 y  轴。只改变 x 和 z
上的点。

转 <wbr>第十五章 <wbr>3D <wbr>基础 <wbr>(3)(as3.0)

 

图 15-11 y  轴旋转
     因此,对于 3D  而言,当我们在某个轴上旋转一个物体时,将改变两个轴上的位置。
回顾一下第十章,我们找到 2D  旋转的公式如下:
      x1 = cos(angle) * x - sin(angle) * y;
       y1 = cos(angle) * y + sin(angle) * x;
在 3D  中,方法几乎相同,但是需要指定操作的是哪个轴:x, y, z。因此得到下面三个公式:
      x1 = cos(angleZ) * x - sin(angleZ) * y;
       y1 = cos(angleZ) * y + sin(angleZ) * x;
      x1 = cos(angleY) * x - sin(angleY) * z;
       z1 = cos(angleY) * z + sin(angleY) * x;
       y1 = cos(angleX) * y - sin(angleX) * z;
       z1 = cos(angleX) * z + sin(angleX) * y;


下面,试一下 y  轴的旋转。以下代码可在 RotateY.as  中找到。创建 50  个 Ball3D  的实例,
随机放置舞台上。根据鼠标 x  轴上的位置计算出 y  轴角度。鼠标越向右,角度值越高。物
体就像跟着鼠标旋转一样。
package {
 import flash.display.Sprite;
 import flash.events.Event;
  public class RotateY extends Sprite {
   private var balls:Array;
   private var numBalls:uint = 50;

   private var fl:Number = 250;
   private var vpX:Number = stage.stageWidth / 2;
   private var vpY:Number = stage.stageHeight / 2;
   public function RotateY() {
   init();
  }
   private function init():void {
   balls = new Array();
     for (var i:uint = 0; i < numBalls; i++) {
      var ball:Ball3D = new Ball3D(15);
    balls.push(ball);
      ball.xpos = Math.random() * 200 - 100;
      ball.ypos = Math.random() * 200 - 100;
      ball.zpos = Math.random() * 200 - 100;
    addChild(ball);
   }
   addEventListener(Event.ENTER_FRAME, onEnterFrame);
  }
   private function onEnterFrame(event:Event):void {
     var angleY:Number = (mouseX - vpX) * .001;
     for (var i:uint = 0; i < numBalls; i++) {

   var ball:Ball3D = balls[i];
    rotateY(ball, angleY);
   }
   sortZ();
  }
   private function rotateY(ball:Ball3D, angleY:Number):void {
   var cosY:Number = Math.cos(angleY);
     var sinY:Number = Math.sin(angleY);
     var x1:Number = ball.xpos * cosY - ball.zpos * sinY;
     var z1:Number = ball.zpos * cosY + ball.xpos * sinY;
   ball.xpos = x1;
   ball.zpos = z1;
     if (ball.zpos > -fl) {
    var scale:Number = fl / (fl + ball.zpos);
      ball.scaleX = ball.scaleY = scale;
      ball.x = vpX + ball.xpos * scale;
      ball.y = vpY + ball.ypos * scale;
    ball.visible = true;
   } else {

    ball.visible = false;
   }
  }
   private function sortZ():void {
   balls.sortOn("zpos", Array.DESCENDING | Array.NUMERIC);
     for (var i:uint = 0; i < numBalls; i++) {
    var ball:Ball3D = balls[i];
    setChildIndex(ball, i);
   }
   }
 }
}
     重要的部分加粗表示。获得一个角度,然后使用该角度调用 rotateY 的方法。该方法中,
获得角度的正弦和余弦值,执行旋转,将 x1 和 z1  再赋值给 ball.xpos  和 ball.zpos。接下
来,就是标准透视以及 z  排序。运行结果如图 15-12  所示。
转 <wbr>第十五章 <wbr>3D <wbr>基础 <wbr>(3)(as3.0)

图 15-12  y 轴旋转
     如果这个程序没问题,大家一定也可以将其转换为 x  轴的旋转。只需要改变
onEnterFrame 方法并加入 rotateX  方法:
private function onEnterFrame(event:Event):void {
  var angleX:Number = (mouseY - vpY) * .001;
  for (var i:uint = 0; i < numBalls; i++) {
   var ball:Ball3D = balls[i];
  rotateX(ball, angleX);
 }
 sortZ();
}
private function rotateX(ball:Ball3D, angleX:Number):void {
  var cosX:Number = Math.cos(angleX);
  var sinX:Number = Math.sin(angleX);
  var y1:Number = ball.ypos * cosX - ball.zpos * sinX;
  var z1:Number = ball.zpos * cosX + ball.ypos * sinX;
  ball.ypos = y1;
 ball.zpos = z1;
  if (ball.zpos > -fl) {

   var scale:Number = fl / (fl + ball.zpos);
   ball.scaleX = ball.scaleY = scale;
   ball.x = vpX + ball.xpos * scale;
   ball.y = vpY + ball.ypos * scale;
  ball.visible = true;
  } else {
  ball.visible = false;
 }
}
     现在,angleX  是根据鼠标的 y  坐标确定的。然后计算出正余弦的值,并使用它们算出

y1  和 z1,再赋值给 ypos  和 zpos 属性。
     接下来,我们将两种旋转组合起来。以下是代码(可以在 RotateXY.as  中找到):
package {
 import flash.display.Sprite;
 import flash.events.Event;
  public class RotateXY extends Sprite {
   private var balls:Array;
   private var numBalls:uint = 50;
   private var fl:Number = 250;
   private var vpX:Number = stage.stageWidth / 2;
   private var vpY:Number = stage.stageHeight / 2;
   public function RotateXY() {
   init();
  }
   private function init():void {
   balls = new Array();
     for (var i:uint = 0; i < numBalls; i++) {
      var ball:Ball3D = new Ball3D(15);
    balls.push(ball);
      ball.xpos = Math.random() * 200 - 100;

      ball.ypos = Math.random() * 200 - 100;
      ball.zpos = Math.random() * 200 - 100;
    addChild(ball);
   }
   addEventListener(Event.ENTER_FRAME, onEnterFrame);
  }
   private function onEnterFrame(event:Event):void {
     var angleX:Number = (mouseY - vpY) * .001;
     var angleY:Number = (mouseX - vpX) * .001;
     for (var i:uint = 0; i < numBalls; i++) {
    var ball:Ball3D = balls[i];
    rotateX(ball, angleX);
    rotateY(ball, angleY);
    doPerspective(ball);
   }
   sortZ();
  }
   private function rotateX(ball:Ball3D, angleX:Number):void {
   var cosX:Number = Math.cos(angleX);
     var sinX:Number = Math.sin(angleX);
     var y1:Number = ball.ypos * cosX - ball.zpos * sinX;
     var z1:Number = ball.zpos * cosX + ball.ypos * sinX;
   ball.ypos = y1;

   ball.zpos = z1;
  }
   private function rotateY(ball:Ball3D, angleY:Number):void {
   var cosY:Number = Math.cos(angleY);
     var sinY:Number = Math.sin(angleY);
     var x1:Number = ball.xpos * cosY - ball.zpos * sinY;
      var z1:Number = ball.zpos * cosY + ball.xpos * sinY;
   ball.xpos = x1;
   ball.zpos = z1;
  }
   private function doPerspective(ball:Ball3D):void {
     if (ball.zpos > -fl) {
    var scale:Number = fl / (fl + ball.zpos);
      ball.scaleX = ball.scaleY = scale;
      ball.x = vpX + ball.xpos * scale;
      ball.y = vpY + ball.ypos * scale;
    ball.visible = true;
   } else {
    ball.visible = false;
   }
  }
   private function sortZ():void {
   balls.sortOn("zpos", Array.DESCENDING | Array.NUMERIC);
     for (var i:uint = 0; i < numBalls; i++) {
    var ball:Ball3D = balls[i];
    setChildIndex(ball, i);
   }

  }
 }
}
     与前一个例子相比主要的变化用粗体表示。现在,我们计算出 angleY, angleX  ,并调
用 rotateX, rotateY。注意,我将透视的代码从 rotate  方法中分离到一个单独的方法中,因
为它不需要被调用两次。我相信根据前面的公式,您一定可以自行加入 rotateZ 方法。
     测试一下这个影片。通过将 3D  坐标旋转与前一节赛车游戏的“屏幕环绕”的概念相
结合,大家一定还可以创建出丰富的,交互性的 3D  环境。
 

 
碰撞检测
     本章的最后我要给大家介绍一下 3D  的碰撞检测。在 Flash  的三维空间中唯一可行的
方法就是距离碰撞检测。找出两个物体的距离(使用 3D 距离公式),如果小于两个物体的
半径之和,就产生了碰撞。
     我将前面 3D  反弹的例子改成了 3D  碰撞检测的例子,加入了少量的物体并扩大了一
些空间。代码中首先执行普通的 3D  运动及透视,然后做一个双重循环比较所有小球的位
置。如果有两个物体的距离小于它们的半径之和,就使用颜色转换代码将它们都设置为蓝色。
非常简单。以下是代码(Collision3D.as):
package {
 import flash.display.Sprite;
 import flash.events.Event;
 import flash.geom.ColorTransform;
  public class Collision3D extends Sprite {
   private var balls:Array;
   private var numBalls:uint = 20;
   private var fl:Number = 250;
   private var vpX:Number = stage.stageWidth / 2;

   private var vpY:Number = stage.stageHeight / 2;
   private var top:Number = -200;
   private var bottom:Number = 200;
   private var left:Number = -200;
   private var right:Number = 200;
   private var front:Number = 200;
   private var back:Number = -200;
   public function Collision3D() {
   init();
  }
   private function init():void {
   balls = new Array();
     for (var i:uint = 0; i < numBalls; i++) {
      var ball:Ball3D = new Ball3D(15);
    balls.push(ball);
      ball.xpos = Math.random() * 400 - 200;
      ball.ypos = Math.random() * 400 - 200;
      ball.zpos = Math.random() * 400 - 200;
      ball.vx = Math.random() * 10 - 5;
      ball.vy = Math.random() * 10 - 5;
      ball.vz = Math.random() * 10 - 5;
    addChild(ball);
   }

   addEventListener(Event.ENTER_FRAME, onEnterFrame);
  }
   private function onEnterFrame(event:Event):void {
     for (var i:uint = 0; i < numBalls; i++) {
    var ball:Ball3D = balls[i];
    move(ball);
   }
     for (i = 0; i < numBalls - 1; i++) {
      var ballA:Ball3D = balls[i];
       for (var j:uint = i + 1; j < numBalls; j++) {
     var ballB:Ball3D = balls[j];
        var dx:Number = ballA.xpos - ballB.xpos;
        var dy:Number = ballA.ypos - ballB.ypos;
     var dz:Number = ballA.zpos - ballB.zpos;
        var dist:Number = Math.sqrt(dx*dx + dy*dy + dz*dz);
        if (dist < ballA.radius + ballB.radius) {
      var blueTransform:ColorTransform =
         new ColorTransform(0, 1, 1, 1, 0, 0, 255, 0);
      ballA.transform.colorTransform = blueTransform;
      ballB.transform.colorTransform = blueTransform;
     }
    }
   }

 sortZ();
  }
   private function move(ball:Ball3D):void {
      var radius:Number = ball.radius;
   ball.xpos += ball.vx;
   ball.ypos += ball.vy;
   ball.zpos += ball.vz;
     if (ball.xpos + radius > right) {
      ball.xpos = right - radius;
    ball.vx *= -1;
     } else if (ball.xpos - radius < left) {
      ball.xpos = left + radius;
    ball.vx *= -1;
   }
     if (ball.ypos + radius > bottom) {
      ball.ypos = bottom - radius;
    ball.vy *= -1;
     } else if (ball.ypos - radius < top) {
      ball.ypos = top + radius;
    ball.vy *= -1;
   }
     if (ball.zpos + radius > front) {
      ball.zpos = front - radius;

    ball.vz *= -1;
     } else if (ball.zpos - radius < back) {
    ball.zpos = back + radius;
    ball.vz *= -1;
   }
     if (ball.zpos > -fl) {
    var scale:Number = fl / (fl + ball.zpos);
      ball.scaleX = ball.scaleY = scale;
      ball.x = vpX + ball.xpos * scale;
      ball.y = vpY + ball.ypos * scale;
    ball.visible = true;
   } else {
    ball.visible = false;
   }
  }
   private function sortZ():void {
   balls.sortOn("zpos", Array.DESCENDING | Array.NUMERIC);
     for (var i:uint = 0; i < numBalls; i++) {
    var ball:Ball3D = balls[i];
    setChildIndex(ball, i);
   }
  }
 }

}
     重要的部分用粗体表示。小球起初都是红色的,当它们碰撞后,颜色发生改变。最后,
全部变为蓝色。
 

本章重要公式
     本章的重要公式是3D透视,坐标旋转以及距离的计算。
 
 
 
基本 
scale = fl / (fl + zpos);
sprite.scaleX = sprite.scaleY = scale;
sprite.alpha = scale; //  可选
sprite.x = vanishingPointX + xpos * scale;
sprite.y = vanishingPointY + ypos * scale;

 

 
Z  排序:
//  假设有一个带有 zpos 属性的 3D 物体的数组
objectArray.sortOn("zpos", Array.DESCENDING | Array.NUMERIC);
for(var i:uint = 0; i < numObjects; i++) {
    setChildIndex(objectArray[i], i);
}
 

 
坐标旋转:
x1 = cos(angleZ) * xpos - sin(angleZ) * ypos;
y1 = cos(angleZ) * ypos + sin(angleZ) * xpos;
x1 = cos(angleY) * xpos - sin(angleY) * zpos;
z1 = cos(angleY) * zpos + sin(angleY) * xpos;
y1 = cos(angleX) * ypos - sin(angleX) * zpos;
z1 = cos(angleX) * zpos + sin(angleX) * ypos;
 
 
 
3D  距离:
dist = Math.sqrt(dx * dx + dy * dy + dz * dz); 
 

 

其实现在CS4 CS5都已经出来了,用过的朋友都知道从CS4开始已经引入了3D这个概念,直接增加Z这个坐标属性以及旋转,不过作为知识是不会过时的哈哈,大家可以结合CS4的特性来修改下上面所描述的Z得坐标。
 

(如果要转载请注明出处http://blog.sina.com.cn/jooi,谢谢)

阅读更多
个人分类: Flash Flex
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭