众所周知,flash是一个二维的动画软件,是无法完成一些真正的3d效果,但是如果要用它来做一些3d效果也不是不可能,一般来说有以下三种方法.
第一种方法,借助其它的3d软件,象swift 3d,Ulead Cool
3d,TrueSpace等一些相对容易上手的软件,用它们来完成后再导入到flash中,效果是不错,但是导入的大都是位图系列,所以生成的文件都比较
大,再有一个特点就是没有flash所特有的互动性,作为flash 并没有多少工作可做,它只不过是播放的工具而已.
第二种方法,理论上可以,实际上是行不通的,就是利用flash 的画图工具做成逐帧动画,当然简单的还凑合,复杂的就根本不行.
第三种方法,就是我今天所要说的as 3d
,用数学方法算出3d来,也就是利用数学公式和一些透视的知识模仿出3d效果来,也称之为假3d,是一种视觉上的错觉.不过我个人认为在电视上出现的3d
应该都是假3d,只不过那些专业的3d软件做出来的更逼真,更能欺骗你的眼睛罢了.
言归正传,我们首先认识一下坐标问题,因为要模仿三维效果,必须首先了解坐标方面的知识,,因为3d效果都是三维坐标,而flash中是二维坐标,我们应该先把三维坐标转换二维数学坐标,再把二维坐标转换为flash中的坐标,如图:
为此我们先画一个透视图,B点是观察者的眼睛,物体位于三维空间的A点,它的坐标是(x,y,z),将该点的坐标转换为二维坐标,实际上就是点A在平面
(电视屏幕)上的投影点C的坐标,其中CF的长就是该点的X坐标值,CE是该点的Y的坐标值,假设观察点到屏幕的距离BO=d,然后根据三角形相似原理,
对应边成比例可以得到d/(d+z)=x1/x,d/(d+z)=y1/y,其中(x1,y1)就是转换后的坐标,假设d/(d+z)=ratio,所以
x1=x*ratio,y1=y*ratio.
这样如果不好看懂的话,我们再换个角度来看,假设从上往下看和从右往左看,画个顶视图和右视图,这时候顶视图只能看到X轴和Z轴,Y轴只是一个点,右视图只能看到Y轴和Z轴,X而轴只是一个点,如图:
接下来就很简单了,二维数学坐标和flash之间的坐标转换,只需把中心移动到左上角就好了,那么A点最终的坐标:
_x=width/2+x1,
_y=height/2-y1,
其中width和height分别是场景的宽和高.
现在坐标的问题解决了,只是第一步,要想让物体符合空间透视,还需要对其它参数做调整,比如近大远小,景深之类的,但是flash毕竟不是专门的3d软
件,所以我们只能尽可能的模仿了,通过调整物体的比例大小,透明度和物体所处的层次可以达到比较逼真的效果,其中比例问题的原理和坐标相同,所以也可以根
据上面的公式得出,即:_xscale=_yscale=100*ratio(z越大,ratio就越小,离屏幕就越远,物体就越小),透明度也是一样
_alpha=100*ratio;至于层次的问题现在大都用的是经验值swapDepths(10000-z)或者swapDepths(-z).
导入三个小图标,也可以是图片,如果你画的好的话,还可以画三个小人,这样效果更好,做成影片剪辑,拖入主场景中,分别命名为:mcA,mcB,mcC,然后在主场景的第一帧加入以下代码:
[color=Red]mcA.x = -300;
mcA.y = 0;
mcA.z = 0;
mcA.dir = 1;//运动的方向
mcB.x = -50;
mcB.y = 0;
mcB.z = 250;
mcB.dir = 1;
mcC.x = 200;
mcC.y = 0;
mcC.z = 500;
mcC.dir = 1;//以上为三个图标的三维坐标值
d = 300;
speed = 20;
mcMove = function () {
this.z += speed*this.dir;
if (this.z>500) {
this.z = 500;
this.dir = -1;
} else if (this.z<0) {
this.z = 0;
this.dir = 1;
}
var ratio = d/(d+this.z);
this._x = 250+this.x*ratio;
this._y = 150-this.y*ratio;//将三维坐标转化为二维坐标
this._xscale = this._yscale=100*ratio;
this.swapDepths(-this.z);//调整比例大小,透明度和层次让它符合透视原理
};
mcA.onEnterFrame = mcMove;
mcB.onEnterFrame = mcMove;
mcC.onEnterFrame = mcMove; [/color]
这里是swf文件:3d1.swf
附件: 3d1.swf
接下来我们再做一个例子,在这个例子里我们引入一个镜头的概念,通过改变镜头的远近来实现一些更酷的效果,在主场景的第一帧加入以下代码:
[color=Red]this.createEmptyMovieClip("scene", 1);
scene._x = scene._y=200;/
scene.depth = 1;//建立一个空的影片剪辑,用来包含一切的3d元素
cameraView = new Object();
cameraView.x = 0;
cameraView.y = 0;
cameraView.z = 0;//镜头坐标的初始值
cameraView.target = new Object();
cameraView.target.x = 0;
cameraView.target.y = 0;
cameraView.target.z = 0;//目标镜头的坐标值
d = 200;//眼睛和屏幕的距离
selectPic = function () {
cameraView.target.x = this.x;
cameraView.target.y = this.y;
cameraView.target.z = this.z;
};//这个函数就是每选择一个图时,把这个图的坐标作为目标镜头的坐标
displayPic = function (cameraView, d) {
var x = this.x-cameraView.x;
var y = this.y-cameraView.y;
var z = this.z-cameraView.z;
if (z<0) {
this.z += 5000;
this.x = 1000-Math.random()*1000;
this.y = 1000-Math.random()*1000;
x = this.x-cameraView.x;
y = this.y-cameraView.y;
z = this.z-cameraView.z;
}
var ratio = d/(d+z);
this._x = x*ratio;
this._y = y*ratio;
this._xscale = this._yscale=100*ratio;
this.swapDepths(1000-z);
};
Pic = new Array();
for (var i = 1; i<=10; i++) {
mc = scene.attachMovie("pic"+i, "pic"+i, scene.depth++);//导入10个图标,做成影片剪辑
mc.x = 1000-Math.random()*1000;
mc.y = 1000-Math.random()*1000;
mc.z = i*500;
mc.onRelease = selectPic;
mc.display = displayPic;
Pic.push(mc);
}
scene.onEnterFrame = function() {
cameraView.x += (cameraView.target.x-cameraView.x)/5;
cameraView.y += (cameraView.target.y-cameraView.y)/5;
cameraView.z += (cameraView.target.z-cameraView.z)/5;//缓冲公式
for (var i = 0; i<=Pic.length; i++) {
Pic.display(cameraView, d);
}
}; [/color]
这里是swf文件:3d2.swf ,接下来的这俩个例子,都是利用了镜头的概念,你可以利用键盘的方向键来控制看看效果。
以上的例子虽然有了点3d的效果了,但是都是直来直去,并没有涉及到旋转的效果,那么接下来我们再讲一下物体在三维空间里的旋转效果是如何制作的,.我们把旋转分成三种情况来讲,绕X轴,绕Y轴,绕Z轴旋转,首先讲一下绕X轴旋转,如图:
点A绕X轴旋转b到达点B,X坐标的值是不变的,即x1=x,OA=OB=r,再这里首先要说两个数学里三角函数的公式:
sina=对边/斜边,
cosa=邻边/斜边,.
sin(a+b)=sina*cosb+cosa*sinb,
cos(a+b)=cosa*cosb-sina*sinb,
所以点A的坐标:
y=r*sina,
z=r*cosa,
旋转后B点的坐标:
y1=r*sin(a+b),
z1=r*cos(a+b),
最后得到旋转后B点的坐标与旋转前A点坐标的关系:
y1=y*cosb+z*sinb,
z1=z*cosb-y*sinb,
这个公式很重要,在3d效果里涉及到旋转都要用到这个公式,
同样我们还可以计算出饶Y轴旋转的公式:
x1=x*cosb+z*sinb,
z1=z*cosb-x*sinb,
绕Z轴旋转的公式:
x1=x*cosb+y*sinb,
y1=y*cosb-x*sinb,
有了以上的知识,我们就可以做一个空间旋转的例子,在主场景加入以下代码:
[color=Red] this.createEmptyMovieClip("Scene", 1);
Scene._x = 150;
Scene._y = 150;
d = 300;//眼睛和屏幕之间的距离,试着调整看有什么变化
make3DPoint = function(x,y,z){
var point = new Object();
point.x = x;
point.y = y;
point.z = z;
return point;
};
make2DPoint = function(x, y, depth, ratio){
var point = new Object();
point.x = x;
point.y = y;
point.depth = depth;
point.ratio = ratio;
return point;
};
Transform3DPointsTo2DPoints = function(points, rotations){
var myArray = [];
var sx = Math.sin(rotations.x);
var cx = Math.cos(rotations.x);
var sy = Math.sin(rotations.y);
var cy = Math.cos(rotations.y);
var sz = Math.sin(rotations.z);
var cz = Math.cos(rotations.z);
var x,y,z, xy,xz, yx,yz, zx,zy, ratio;
var i = points.length;
while (i--){
x = points.x;
y = points.y;
z = points.z;
xy = cx*y - sx*z;
xz = sx*y + cx*z;
yz = cy*xz - sy*x;
yx = sy*xz + cy*x;
zx = cz*yx - sz*xy;
zy = sz*yx + cz*xy;//旋转后的坐标值
ratio = d/(d + yz);
x = zx*ratio;
y = zy*ratio;//再转化为二维坐标
z = yz;
myArray = make2DPoint(x, y, -z, ratio);
}
return myArray;
};//以上三个函数很重要,因为在大多数3d效果的例子里都能用到,可以说是公式了,尤其是最后一个
pointsArray = [
make3DPoint(-50,-50,-50),
make3DPoint(50,-50,-50),
make3DPoint(50,-50,50),
make3DPoint(-50,-50,50),
make3DPoint(-50,50,-50),
make3DPoint(50,50,-50),
make3DPoint(50,50,50),
make3DPoint(-50,50,50)
];//正方体四个顶点的坐标
for (i=0; i<pointsArray.length; i++){
attachedObj =Scene.attachMovie("ball", "ball"+i, i);
}//画一个小球,转化为影片剪辑
myRotations = make3DPoint(0,0,0);
Scene.onEnterFrame = function(){
myRotations.y -= this._xmouse/2000;
myRotations.x += this._ymouse/2000;//鼠标互动,你也可以调整2000这个值让它转的更快或者更慢,也可以换成固定值
var screenPoints = Transform3DPointsTo2DPoints(pointsArray, myRotations);
for (i=0; i<pointsArray.length; i++){
myball = this["ball"+i];
myball._x = screenPoints.x;
myball._y = screenPoints.y;
myball._xscale = myball._yscale = 100 * screenPoints.ratio;
myball.swapDepths(screenPoints.depth);
}
};[/color]
按Ctrl+Enter效果就出来了,是不是很酷啊?
这是swf文件:3d3.swf
当然我们可以再加点东西,在刚才的onEnterFrame事件后再加以下代码看看又会如何:
[color=Red]this.clear();
this.lineStyle(2, 0xff0000, 100);
// 顶面的四条线
this.moveTo(screenPoints[0].x, screenPoints[0].y);
this.lineTo(screenPoints[1].x, screenPoints[1].y);
this.lineTo(screenPoints[2].x, screenPoints[2].y);
this.lineTo(screenPoints[3].x, screenPoints[3].y);
this.lineTo(screenPoints[0].x, screenPoints[0].y);
// 底面的四条线
this.moveTo(screenPoints[4].x, screenPoints[4].y);
this.lineTo(screenPoints[5].x, screenPoints[5].y);
this.lineTo(screenPoints[6].x, screenPoints[6].y);
this.lineTo(screenPoints[7].x, screenPoints[7].y);
this.lineTo(screenPoints[4].x, screenPoints[4].y);
// 把顶面和第面用四条线连起来
this.moveTo(screenPoints[0].x, screenPoints[0].y);
this.lineTo(screenPoints[4].x, screenPoints[4].y);
this.moveTo(screenPoints[1].x, screenPoints[1].y);
this.lineTo(screenPoints[5].x, screenPoints[5].y);
this.moveTo(screenPoints[2].x, screenPoints[2].y);
this.lineTo(screenPoints[6].x, screenPoints[6].y);
this.moveTo(screenPoints[3].x, screenPoints[3].y);
this.lineTo(screenPoints[7].x, screenPoints[7].y);
//以上代码是一个简单的画图程序 [/color]
再加点东西,在上面的画图程序里加上以下俩句看有什么:
[color=Red]this.beginFill(0x00ff00,80);
this.endFill(); [/color]
这是swf文件:3d3B.swf "3d3C.swf
从上面的例子里我们把as画图和3d效果结合起来,那么我们接下来就专门讲一下这方面的例子,用纯粹的as 做一些3d效果,先画一个旋转的正方形,在第一帧加入以下的代码:
[code][/code]
this.createEmptyMovieClip("theScene", 1);
theScene._x = 150;
theScene._y = 150;
d = 300;
make3DPoint = function(x,y,z){
var point = new Object();
point.x = x;
point.y = y;
point.z = z;
return point;
};
make2DPoint = function(x, y){
var point = new Object();
point.x = x;
point.y = y;
return point;
};
Transform3DPointsTo2DPoints = function(points,rotations){
var myArray = [];
var sx = Math.sin( rotations.x);
var cx = Math.cos( rotations.x);
var sy = Math.sin( rotations.y);
var cy = Math.cos( rotations.y);
var sz = Math.sin( rotations.z);
var cz = Math.cos( rotations.z);
var x,y,z, xy,xz, yx,yz, zx,zy, ratio;
var i = points.length;
while (i--){
x = points.x;
y = points.y;
z = points.z;
xy = cx*y - sx*z;
xz = sx*y + cx*z;
yz = cy*xz - sy*x;
yx = sy*xz + cy*x;
zx = cz*yx - sz*xy;
zy = sz*yx + cz*xy;
ratio = d/(d + yz);
x = zx*ratio;
y = zy*ratio;
myArray = make2DPoint(x, y);
}
return myArray;
};
pointsArray = [
make3DPoint(-50,-50,-50),
make3DPoint(50,-50,-50),
make3DPoint(50,-50,50),
make3DPoint(-50,-50,50),
make3DPoint(-50,50,-50),
make3DPoint(50,50,-50),
make3DPoint(50,50,50),
make3DPoint(-50,50,50)
];
myRotations = make3DPoint(0,0,0);
theScene.onEnterFrame = function(){
myRotations.y -= this._xmouse/3000;
myRotations.x += this._ymouse/3000;
var screenPoints = Transform3DPointsTo2DPoints(pointsArray, myRotations);
this.clear();
this.lineStyle(2,0xff0000,100);
// top
this.moveTo(screenPoints[0].x, screenPoints[0].y);
this.lineTo(screenPoints[1].x, screenPoints[1].y);
this.lineTo(screenPoints[2].x, screenPoints[2].y);
this.lineTo(screenPoints[3].x, screenPoints[3].y);
this.lineTo(screenPoints[0].x, screenPoints[0].y);
// bottom
this.moveTo(screenPoints[4].x, screenPoints[4].y);
this.lineTo(screenPoints[5].x, screenPoints[5].y);
this.lineTo(screenPoints[6].x, screenPoints[6].y);
this.lineTo(screenPoints[7].x, screenPoints[7].y);
this.lineTo(screenPoints[4].x, screenPoints[4].y);
// connecting bottom and top
this.moveTo(screenPoints[0].x, screenPoints[0].y);
this.lineTo(screenPoints[4].x, screenPoints[4].y);
this.moveTo(screenPoints[1].x, screenPoints[1].y);
this.lineTo(screenPoints[5].x, screenPoints[5].y);
this.moveTo(screenPoints[2].x, screenPoints[2].y);
this.lineTo(screenPoints[6].x, screenPoints[6].y);
this.moveTo(screenPoints[3].x, screenPoints[3].y);
this.lineTo(screenPoints[7].x, screenPoints[7].y);
};
这是swf文件:3d4.swf
看看这段程序和上面的程序是不是很相似,其实只要你掌握了其中的原理,只需要把里面的东西作一些相应的变换就可以做成你自己想的很酷的效果,现在我再把上
面的程序稍做调整,原码我就不写了,你可以自己考虑一下.上面做的都是正方形,如果把坐标做相应的调整,还可以做成其它立体图形,比如三角形,五角形等,
关于单纯的由线条组成的立体图形现在看来很简单,只要你知道各个点的坐标就可以用以上的方法做出来,当然你还可以把它填充起来,比如上面的那个正方体.现
在我就利用beginFill()和endFill()这俩个方法在上面的基础把这个立方体填充起来.把上面的立方体去掉线条,只留下三个面,又是另一个
效果:这是swf文件:3d4B.swf " 3d4C.swf
当然你也可以利用以上的知识自己做一些很酷的效果,只需要将上面的坐标坐标变换一下就能做出很多图形,下面是我做的一个平面五角星和一个只有线条的立体五角星,因为坐标是我自己用苯办法算出里的,所以有点不大像.
下面这个立体图形的坐标我给出来看大家能不能自己做出来.
v1(15.9689,-22.1275,-0.135825),
v2(-15.3241,-22.1275,-0.135825),
v3(15.9689,1.32056e-006,21.9917),
v4(-15.3241,1.32255e-006,21.9917),
v5(-15.3241,-1.31526e-006,-22.2633),
v6(15.9686,-1.31725e-006,-22.2633),
v7(15.9686,22.1275,-0.135827),
v8(-15.3241,22.1275,-0.135827),
v9(0.322368,-84.0845,83.9487),
v10(0.322369,-84.0845,-84.2204),
v11(119.236,0.0,-0.135826),
v12(0.322368,84.0845,-84.2204),
v13(0.322386,84.0845,83.9487),
v14(-118.591,0,-0.135826)
一共有十四个点,组成二十四个面,下面是每个面对应的那几个点:
fa1=9,fb1=1,fc1=3,fa2=9,fb2=3,fc2=4,fa3=9,fb3=4,fc3=2,fa4=9,fb4=2,fc4=1,fa5=10,fb5=2,fc5=5,
fa6=10,fb6=5,fc6=6,fa7=10,fb7=6,fc7=1,fa8=10,fb8=1,fc8=2,fa9=11,fb9=6,fc9=7,fa10=11,fb10=7,fc10=3,
fa11=11,fb11=3,fc11=1,fa12=11,fb12=1,fc12=6,fa13=12,fb13=6,fc13=5,fa14=12,fb14=5,fc14=8,
fa15=12,fb15=8,fc15=7,fa16=12,fb16=7,fc16=6,fa17=13,fb17=8,fc17=4,fa18=13,fb18=4,fc18=3,
fa19=13,fb19=3,fc19=7,fa20=13,fb20=7,fc20=8,fa21=14,fb21=8,fc21=5,fa22=14,fb22=5,fc22=2,
fa23=14,fb23=2,fc23=4,fa24=14,fb24=4,fc24=8
这是我做的几个例子:swf文件:3d10.swf "3d10B.swf "3d10C.swf
做旋转的六面体还有另一种做法,前面的那中方法,做出来的实际并不逼真,下面先看看我用另一个方法做出来的例子看与这个有什么区别呢,代码如下:
[color=Red] this.createEmptyMovieClip("theScene", 1);
theScene._x = 150;
theScene._y = 150;
d = 300;
make3DPoint = function(x,y,z){
var point = new Object();
point.x = x;
point.y = y;
point.z = z;
return point;
};
make2DPoint = function(x,y){
var point = new Object();
point.x = x;
point.y = y;
return point;
};
isVisibleBetween = function(a,b,c){
if (((b.y-a.y)/(b.x-a.x)-(c.y-a.y)/(c.x-a.x)<0)^(a.x<=b.x == a.x>c.x)){
return true;
}else{
return false;
}
};[/color]
//
这个函数很重要,你不必知道它的原理,只要知道怎么用就可以了,其实我也不知道怎么解释它,但是这个函数可以根据一个平面上任意三个点的坐标而判断该平面
是否看的见,因为一个立体图形在平面上最多可以显示三个面,所以就必须判断你可以看的那三个面,这个函数不仅可以用在四面体上,不管有多少面的立体图形都
可以用这个公式:
[color=Red] drawFilledSquare = function(a,b,c,d){
this.clear();
this.lineStyle(2,0,100);
if (isVisibleBetween(a,b,c)){
this.moveTo(a.x, a.y);
this.beginFill(this.col, 100);
this.lineTo(b.x, b.y);
this.lineTo(c.x, c.y);
this.lineTo(d.x, d.y);
this.lineTo(a.x, a.y);
this.endFill();
}
};[/color]
//这里就是上面函数的应用,如果这个面看的见就画出它,
//下面的程序基本上就成为了一种固定的形式了:
[color=Red]Transform3DPointsTo2DPoints = function(points, axisRotations){
var myArray = [];
var sx = Math.sin(axisRotations.x);
var cx = Math.cos(axisRotations.x);
var sy = Math.sin(axisRotations.y);
var cy = Math.cos(axisRotations.y);
var sz = Math.sin(axisRotations.z);
var cz = Math.cos(axisRotations.z);
var x,y,z, xy,xz, yx,yz, zx,zy, scaleRatio;
var i = points.length;
while (i--){
x = points.x;
y = points.y;
z = points.z;
xy = cx*y - sx*z;
xz = sx*y + cx*z;
yz = cy*xz - sy*x;
yx = sy*xz + cy*x;
zx = cz*yx - sz*xy;
zy = sz*yx + cz*xy;
scaleRatio = d/(d + yz);
x = zx*scaleRatio;
y = zy*scaleRatio;
myArray = make2DPoint(x, y);
}
return myArray;
};
pointsArray = [
make3DPoint(-50,-50,-50),
make3DPoint(50,-50,-50),
make3DPoint(50,-50,50),
make3DPoint(-50,-50,50),
make3DPoint(-50,50,-50),
make3DPoint(50,50,-50),
make3DPoint(50,50,50),
make3DPoint(-50,50,50)
];
rollOverFace = function(){
this.col = 0xff0000;
};
rollOutFace = function(){
this.col = 0xdddddd;
};
for (i=0; i<6; i++){
emptyFace = theScene.createEmptyMovieClip("face"+i,i);
emptyFace.col = 0xdddddd;
emptyFace.onRollOver = emptyFace.onDragOver = rollOverFace;
emptyFace.onRollOut = emptyFace.onDragOut = rollOutFace;
emptyFace.onPress = pressFace;
emptyFace.draw = drawFilledSquare;
}
cubeAxisRotations = make3DPoint(0,0,0);
theScene.onEnterFrame = function(){
cubeAxisRotations.y -= this._xmouse/3000;
cubeAxisRotations.x += this._ymouse/3000;
var pts2D = Transform3DPointsTo2DPoints(pointsArray, cubeAxisRotations);
this.face0.draw(pts2D[0], pts2D[3], pts2D[2], pts2D[1]);
this.face1.draw(pts2D[4], pts2D[5], pts2D[6], pts2D[7]);
this.face2.draw(pts2D[0], pts2D[4], pts2D[7], pts2D[3]);
this.face3.draw(pts2D[3], pts2D[7], pts2D[6], pts2D[2]);
this.face4.draw(pts2D[2], pts2D[6], pts2D[5], pts2D[1]);
this.face5.draw(pts2D[1], pts2D[5], pts2D[4], pts2D[0]);
}; [/color]
这是swf文件:3d5.swf
现在我们再把上面的坐标换成三角形的坐标,再把最后画图的部分也做一下调整,那么就很容易做成一个旋转的的四面体了,具体调整如下:
[color=Red] pointsArray = [
make3DPoint(-50,87,29),
make3DPoint(0,87,-58),
make3DPoint(50,87,29),
make3DPoint(0,0,0)
];//这是坐标部分
this.face0.draw(pts2D[0], pts2D[1], pts2D[2]);
this.face1.draw(pts2D[0], pts2D[3], pts2D[1]);
this.face2.draw(pts2D[1], pts2D[3], pts2D[2]);
this.face3.draw(pts2D[2], pts2D[3], pts2D[0]);
//这是画图部分[/color]
这是swf文件:3d6.swf .下面的几个例子也是在此基础上扩展的:3d6C.swf ;最后来个超酷的飞机,这可是纯AS做的哦:3d7B.swf .
上面的例子比较常见,当然还有一些其它的效果,我现在再给大家连出一些这方面的例子:
有投影的三维效果:3DB2.swf " 3DF.swf
有弹性的立体小球:3DD.swf
可以拖动的小球:3DA.swf
最后是我做的两个,一个是可以用键盘方向键控制旋转的立体DNA,另一个是我用BitmapData类做的空间旋转的立方体. DNA_exam2.swf "3d9.swf