之前我们所做的动画都是基于x,y二维坐标轴的,在三维动画中我们还需要增加一个垂直于屏幕“向里”或“向外”的Z轴,那么z轴到底是应该向外,还是向里呢?这个其实无所谓,不过为了统一,习惯上通常把z轴约定为向里,即所谓的“右手坐标系”
右手坐标系的得名:伸出右手,让食指、中指、大拇指相互垂直;然后 食指指向x轴正向,中指指向y轴正向,则大拇指所指方向即为z轴正向。(事实上这个姿势酷似周杰伦周董的招牌动作)
三维透视的基本规则:
物体在Z轴上的坐标越大(越远),则看起来越小(将上图坐标系旋转,把z轴转到x轴方向后,得到下图),如果距离足够远,则物体将消失于屏幕上的某个特定点(通常称为“消失点”)
技术上的主要处理:动态调整物体的scaleX与scaleY(同时因为物体的大小改变后,相应的x,y坐标值通常也会改变,所以x,y坐标也要做相应调整以符合透视规则),基本公式如下:
scale = fl/(fl+z)
01
var
ball:Ball =
new
Ball();
02
addChild(ball);
03
04
//观察点 相对于 消失点的坐标
05
var
xPos:
Number
=
0
;
06
var
yPos:
Number
=
0
;
07
var
zPos:
Number
=
0
;
08
var
fl:
Number
=
250
;
//焦距
09
//消失点
10
var
vpX:
Number
=stage.stageWidth/
2
;
11
var
vpY:
Number
=stage.stageHeight/
2
;
12
13
addEventListener(Event.ENTER_FRAME, EnterFrameHandler);
14
stage.addEventListener(KeyboardEvent.KEY_DOWN, KeyDownHandler);
15
stage.addEventListener(MouseEvent.MOUSE_WHEEL,MouseWheelHandler);
16
17
//鼠标滚轮事件(注:必须让stage获取焦点时-即用鼠标在动画上点击一下,该事件才会触发,另外还要注意:嵌入网页时,浏览器也会响应鼠标滚轮)
18
function
MouseWheelHandler(e:MouseEvent):
void
{
19
zPos += (e.delta*
5
);
20
}
21
22
function
EnterFrameHandler(event:Event):
void
{
23
if
(zPos> -fl) {
24
ball.visible=
true
;
25
xPos=mouseX-vpX;
26
yPos=mouseY-vpY;
27
var
scale:
Number
= fl / (fl + zPos);
28
ball.scaleX=ball.scaleY=scale;
29
ball.x=vpX+xPos*scale;
30
ball.y=vpY+yPos*scale;
31
}
else
{
32
ball.visible=
false
;
33
}
34
35
//辅助线
36
graphics.clear();
37
graphics.lineStyle(
1
,
0xcccccc
);
38
graphics.moveTo(vpX,vpY);
39
graphics.lineTo(vpX,ball.y);
40
graphics.moveTo(vpX,vpY);
41
graphics.lineTo(ball.x,vpY);
42
43
44
graphics.lineStyle(
1
,
0x0000ff
,
0.5
);
45
graphics.moveTo(vpX,vpY);
46
graphics.lineTo(ball.x,ball.y);
47
48
graphics.lineStyle(
1
,
0xff0000
,
0.5
);
49
graphics.moveTo(ball.x,ball.y);
50
graphics.lineTo(mouseX,mouseY);
51
}
52
53
//键盘事件
54
function
KeyDownHandler(e:KeyboardEvent):
void
{
55
if
(e.keyCode==Keyboard.UP) {
56
zPos+=
5
;
57
}
else
if
(e.keyCode == Keyboard.DOWN) {
58
zPos-=
5
;
59
}
60
}
这个示例中,"鼠标所在位置"充当了"观察点"(即前面示意图中的"人眼"位置),电脑屏幕所在平面即物体的成像面,用键盘上下键可调整小球在Z轴上的位置,然后移动鼠标位置,通过辅助线观察变化。
基本的3D运动:
001
package
{
002
import
flash.display.Sprite;
003
import
flash.events.Event;
004
import
flash.events.KeyboardEvent;
005
import
flash.ui.Keyboard;
006
import
flash.display.StageAlign;
007
import
flash.display.StageScaleMode;
008
009
public
class
Velocity3D
extends
Sprite {
010
private
var
ball:Ball;
011
012
//相当于消失点的坐标
013
private
var
xpos:
Number
=
0
;
014
private
var
ypos:
Number
=
0
;
015
private
var
zpos:
Number
=
0
;
016
017
//x,y,z三轴上的速度分量
018
private
var
vx:
Number
=
0
;
019
private
var
vy:
Number
=
0
;
020
private
var
vz:
Number
=
0
;
021
022
private
var
friction:
Number
=
0.98
;
023
private
var
fl:
Number
=
250
;
024
025
//消失点
026
private
var
vpX:
Number
=stage.stageWidth/
2
;
027
private
var
vpY:
Number
=stage.stageHeight/
2
;
028
029
public
function
Velocity3D() {
030
init();
031
}
032
private
function
init():
void
{
033
stage.align = StageAlign.TOP_LEFT;
034
stage.scaleMode = StageScaleMode.NO_SCALE;
035
ball =
new
Ball(
20
);
036
addChild(ball);
037
addEventListener(Event.ENTER_FRAME, EnterFrameHandler);
038
stage.addEventListener(KeyboardEvent.KEY_DOWN, KeyDownHandler);
039
}
040
private
function
EnterFrameHandler(event:Event):
void
{
041
vpX =stage.stageWidth/
2
;
042
vpY =stage.stageHeight/
2
;
043
044
xpos+=vx;
045
ypos+=vy;
046
zpos+=vz;
047
vx*=friction;
048
vy*=friction;
049
vz*=friction;
050
051
if
(zpos>-fl) {
052
var
scale:
Number
= fl / (fl + zpos);
053
ball.scaleX=ball.scaleY=scale;
054
ball.x=vpX+xpos*scale;
055
ball.y=vpY+ypos*scale;
056
ball.visible=
true
;
057
}
else
{
058
ball.visible=
false
;
059
}
060
061
//辅助线
062
graphics.clear();
063
graphics.lineStyle(
1
,
0xefefef
);
064
graphics.moveTo(
0
,stage.stageHeight/
2
);
065
graphics.lineTo(stage.stageWidth,stage.stageHeight/
2
);
066
graphics.lineTo(stage.stageWidth-
15
,stage.stageHeight/
2
-
8
);
067
graphics.moveTo(stage.stageWidth,stage.stageHeight/
2
);
068
graphics.lineTo(stage.stageWidth-
15
,stage.stageHeight/
2
+
8
);
069
070
graphics.moveTo(stage.stageWidth/
2
,
0
);
071
graphics.lineTo(stage.stageWidth/
2
,stage.stageHeight);
072
graphics.lineTo(stage.stageWidth/
2
-
8
,stage.stageHeight-
15
);
073
graphics.moveTo(stage.stageWidth/
2
,stage.stageHeight);
074
graphics.lineTo(stage.stageWidth/
2
+
8
,stage.stageHeight-
15
);
075
graphics.lineStyle(
1
,
0xdadada
);
076
graphics.moveTo(vpX,vpY);
077
graphics.lineTo(ball.x,ball.y);
078
079
080
}
081
private
function
KeyDownHandler(e:KeyboardEvent):
void
{
082
switch
(e.keyCode) {
083
case
Keyboard.UP :
084
vy-=
1
;
085
break
;
086
case
Keyboard.DOWN :
087
vy+=
1
;
088
break
;
089
case
Keyboard.LEFT :
090
vx-=
1
;
091
break
;
092
case
Keyboard.RIGHT :
093
vx+=
1
;
094
break
;
095
case
Keyboard.SHIFT :
096
vz+=
0.5
;
097
break
;
098
case
Keyboard.CONTROL :
099
vz-=
0.5
;
100
break
;
101
default
:
102
break
;
103
}
104
}
105
}
106
}
上下左右键控制x,y轴方向速度,shift/ctrl键控制z轴速度(当然这个示例还没考虑到3D环境中的边界反弹,下面就要谈到这个问题)
3维运动反弹:
这个需要一点想象力,二维情况下,通常只要把舞台的四个边当作边界就足够了,但是试想一下:在一个立体的空间里,要限制一个物体的活动范围,得要有6个边界面(上,下,左,右,前,后)
001
package
{
002
import
flash.display.Sprite;
003
import
flash.events.Event;
004
import
flash.display.StageAlign;
005
import
flash.display.StageScaleMode;
006
007
public
class
Bounce3D
extends
Sprite {
008
private
var
ball:Ball;
009
private
var
xpos:
Number
=
0
;
010
private
var
ypos:
Number
=
0
;
011
private
var
zpos:
Number
=
0
;
012
private
var
vx:
Number
=Math.random()*
12
-
6
;
013
private
var
vy:
Number
=Math.random()*
12
-
6
;
014
private
var
vz:
Number
=Math.random()*
12
-
6
;
015
private
var
fl:
Number
=
250
;
016
017
//消失点
018
private
var
vpX:
Number
=stage.stageWidth/
2
;
019
private
var
vpY:
Number
=stage.stageHeight/
2
;
020
021
//相对于消失点的六个边界面(上,下,左,右,前,后)
022
private
var
top:
Number
=-
100
;
023
private
var
bottom:
Number
=
100
;
024
private
var
left:
Number
=-
100
;
025
private
var
right:
Number
=
100
;
026
private
var
front:
Number
=
100
;
027
private
var
back:
Number
=-
100
;
028
029
public
function
Bounce3D() {
030
init();
031
}
032
private
function
init():
void
{
033
stage.align = StageAlign.TOP_LEFT;
034
stage.scaleMode = StageScaleMode.NO_SCALE;
035
ball=
new
Ball(
15
);
036
addChild(ball);
037
addEventListener(Event.ENTER_FRAME, EnterFrameHandler);
038
}
039
private
function
EnterFrameHandler(event:Event):
void
{
040
vpX =stage.stageWidth/
2
;
041
vpY =stage.stageHeight/
2
;
042
043
xpos+=vx;
044
ypos+=vy;
045
zpos+=vz;
046
var
radius:
Number
=ball.radius;
047
//左右边界
048
if
(xpos+radius>right) {
049
xpos=right-radius;
050
vx*=-
1
;
051
}
else
if
(xpos - radius < left) {
052
xpos=left+radius;
053
vx*=-
1
;
054
}
055
//上下边界
056
if
(ypos+radius>bottom) {
057
ypos=bottom-radius;
058
vy*=-
1
;
059
}
else
if
(ypos - radius < top) {
060
ypos=top+radius;
061
vy*=-
1
;
062
}
063
//前后边界
064
if
(zpos+radius>front) {
065
zpos=front-radius;
066
vz*=-
1
;
067
}
else
if
(zpos - radius < back) {
068
zpos=back+radius;
069
vz*=-
1
;
070
}
071
072
//换算成平面二维坐标及缩放比率
073
if
(zpos>- fl) {
074
var
scale:
Number
= fl / (fl + zpos);
075
ball.scaleX=ball.scaleY=scale;
076
ball.x=vpX+xpos*scale;
077
ball.y=vpY+ypos*scale;
078
ball.visible=
true
;
079
}
else
{
080
ball.visible=
false
;
081
}
082
083
//辅助线
084
graphics.clear();
085
graphics.lineStyle(
1
,
0xccccff
);
086
graphics.moveTo(
0
,stage.stageHeight/
2
);
087
graphics.lineTo(stage.stageWidth,stage.stageHeight/
2
);
088
graphics.lineTo(stage.stageWidth-
15
,stage.stageHeight/
2
-
8
);
089
graphics.moveTo(stage.stageWidth,stage.stageHeight/
2
);
090
graphics.lineTo(stage.stageWidth-
15
,stage.stageHeight/
2
+
8
);
091
092
graphics.moveTo(
0
,stage.stageHeight);
093
graphics.lineTo(stage.stageWidth,
0
);
094
graphics.lineTo(stage.stageWidth-
15
,
2
);
095
graphics.moveTo(stage.stageWidth,
0
);
096
graphics.lineTo(stage.stageWidth-
6
,
13
);
097
098
graphics.moveTo(stage.stageWidth/
2
,
0
);
099
graphics.lineTo(stage.stageWidth/
2
,stage.stageHeight);
100
graphics.lineTo(stage.stageWidth/
2
-
8
,stage.stageHeight-
15
);
101
graphics.moveTo(stage.stageWidth/
2
,stage.stageHeight);
102
graphics.lineTo(stage.stageWidth/
2
+
8
,stage.stageHeight-
15
);
103
graphics.lineStyle(
1
,
0xffccff
);
104
graphics.moveTo(vpX,vpY);
105
graphics.lineTo(ball.x,ball.y);
106
}
107
}
108
}
也许这样看得并不清楚,再加入更多的小球反弹,可能更容易理解一些,不过为了方便代码处理,先定义一个新的小球类:Ball3D
01
package
{
02
import
flash.display.Sprite;
03
public
class
Ball3D
extends
Sprite {
04
public
var
radius:
Number
;
05
private
var
color:
uint
;
06
public
var
xpos:
Number
=
0
;
07
public
var
ypos:
Number
=
0
;
08
public
var
zpos:
Number
=
0
;
09
public
var
vx:
Number
=
0
;
10
public
var
vy:
Number
=
0
;
11
public
var
vz:
Number
=
0
;
12
public
var
mass:
Number
=
1
;
13
public
function
Ball3D(radius:
Number
=
40
, color:
uint
=
0xff0000
) {
14
this
.radius=radius;
15
this
.color=color;
16
init();
17
}
18
public
function
init():
void
{
19
graphics.lineStyle(
0
);
20
graphics.beginFill(color);
21
graphics.drawCircle(
0
,
0
, radius);
22
graphics.endFill();
23
}
24
}
25
}
多个小球的3D反弹:
001
package
{
002
import
flash.display.Sprite;
003
import
flash.events.Event;
004
import
flash.display.StageAlign;
005
import
flash.display.StageScaleMode;
006
007
public
class
MultiBounce3D
extends
Sprite {
008
private
var
balls:
Array
;
009
private
var
numBalls:
uint
=
20
;
010
private
var
fl:
Number
=
250
;
011
private
var
vpX:
Number
=stage.stageWidth/
2
;
012
private
var
vpY:
Number
=stage.stageHeight/
2
;
013
private
var
top:
Number
=-
120
;
014
private
var
bottom:
Number
=
120
;
015
private
var
left:
Number
=-
120
;
016
private
var
right:
Number
=
120
;
017
private
var
front:
Number
=
120
;
018
private
var
back:
Number
=-
120
;
019
public
function
MultiBounce3D() {
020
init();
021
}
022
private
function
init():
void
{
023
stage.align = StageAlign.TOP_LEFT;
024
stage.scaleMode = StageScaleMode.NO_SCALE;
025
balls =
new
Array
();
026
for
(
var
i:
uint
=
0
; i < numBalls; i++) {
027
var
ball:Ball3D=
new
Ball3D(
15
,Math.random()*
0xffffff
);
028
balls.push(ball);
029
ball.vx=Math.random()*
10
-
5
;
030
ball.vy=Math.random()*
10
-
5
;
031
ball.vz=Math.random()*
10
-
5
;
032
addChild(ball);
033
}
034
addEventListener(Event.ENTER_FRAME, onEnterFrame);
035
}
036
private
function
onEnterFrame(event:Event):
void
{
037
vpX =stage.stageWidth/
2
;
038
vpY =stage.stageHeight/
2
;
039
graphics.clear();
040
for
(
var
i:
uint
=
0
; i < numBalls; i++) {
041
var
ball:Ball3D=balls[i];
042
move(ball);
043
}
044
}
045
private
function
move(ball:Ball3D):
void
{
046
var
radius:
Number
=ball.radius;
047
ball.xpos+=ball.vx;
048
ball.ypos+=ball.vy;
049
ball.zpos+=ball.vz;
050
051
//6边界检测
052
if
(ball.xpos+radius>right) {
053
ball.xpos=right-radius;
054
ball.vx*=-
1
;
055
}
else
if
(ball.xpos - radius < left) {
056
ball.xpos=left+radius;
057
ball.vx*=-
1
;
058
}
059
if
(ball.ypos+radius>bottom) {
060
ball.ypos=bottom-radius;
061
ball.vy*=-
1
;
062
}
else
if
(ball.ypos - radius < top) {
063
ball.ypos=top+radius;
064
ball.vy*=-
1
;
065
}
066
if
(ball.zpos+radius>front) {
067
ball.zpos=front-radius;
068
ball.vz*=-
1
;
069
}
else
if
(ball.zpos - radius < back) {
070
ball.zpos=back+radius;
071
ball.vz*=-
1
;
072
}
073
074
//转换化2D坐标
075
if
(ball.zpos>- fl) {
076
var
scale:
Number
= fl / (fl + ball.zpos);
077
ball.scaleX=ball.scaleY=scale;
078
ball.x=vpX+ball.xpos*scale;
079
ball.y=vpY+ball.ypos*scale;
080
ball.visible=
true
;
081
}
else
{
082
ball.visible=
false
;
083
}
084
085
//辅助线
086
graphics.lineStyle(
1
,
0xccccff
);
087
graphics.moveTo(
0
,stage.stageHeight/
2
);
088
graphics.lineTo(stage.stageWidth,stage.stageHeight/
2
);
089
graphics.lineTo(stage.stageWidth-
15
,stage.stageHeight/
2
-
8
);
090
graphics.moveTo(stage.stageWidth,stage.stageHeight/
2
);
091
graphics.lineTo(stage.stageWidth-
15
,stage.stageHeight/
2
+
8
);
092
093
graphics.moveTo(
0
,stage.stageHeight);
094
graphics.lineTo(stage.stageWidth,
0
);
095
graphics.lineTo(stage.stageWidth-
15
,
2
);
096
graphics.moveTo(stage.stageWidth,
0
);
097
graphics.lineTo(stage.stageWidth-
6
,
13
);
098
099
graphics.moveTo(stage.stageWidth/
2
,
0
);
100
graphics.lineTo(stage.stageWidth/
2
,stage.stageHeight);
101
graphics.lineTo(stage.stageWidth/
2
-
8
,stage.stageHeight-
15
);
102
graphics.moveTo(stage.stageWidth/
2
,stage.stageHeight);
103
graphics.lineTo(stage.stageWidth/
2
+
8
,stage.stageHeight-
15
);
104
graphics.lineStyle(
1
,
0xff99ff
);
105
graphics.moveTo(vpX,vpY);
106
graphics.lineTo(ball.x,ball.y);
107
}
108
}
109
}
仔细观察一下,相信不少人会发现问题:物体的前后顺序不对,远处的物体居然挡住了近处的物体。(css中可以通过z-Index来调整,silverlight的canvas中也有类似的zIndex,但在As3中如何做呢?)
先跑一下题,来看一个小技巧:Object 数组的排序
1
var
arrTest = [{age:
20
,name:
"a"
},{age:
50
,name:
"b"
},{age:
30
,name:
"c"
}]
2
arrTest.sortOn(
"age"
,
Array
.DESCENDING);
//按age值倒排
3
for
(
var
i:
int
=
0
,j=arrTest.length;i<j;i++){
4
trace
(arrTest[i].age ,arrTest[i].name);
5
}
是不是很好用!
ok,问题解决了:Flash的显示列表中,最后被addChild的物体总是显示在上面,在Flash内部"舞台上的每个物体"都对应一个索引值,随着物体不断被添加到舞台上,其对应的索引值也不断增加,我们可以通过调整索引值来改变物体的显示顺序.
基本测试:
01
var
ballA =
new
Ball(
50
);
02
ballA.x = stage.stageWidth/
2
;
03
ballA.y = stage.stageHeight/
2
;
04
addChild(ballA);
05
06
var
ballB =
new
Ball(
45
,
0x0000ff
);
07
ballB.x = ballA.x;
08
ballB.y = ballA.y +
20
;
09
addChild(ballB);
10
11
btn1.addEventListener(MouseEvent.MOUSE_DOWN,btn1Click);
12
13
function
btn1Click(e:MouseEvent):
void
{
14
setChildIndex(ballB,
0
);
15
setChildIndex(ballA,
1
);
16
}
17
18
btn2.addEventListener(MouseEvent.MOUSE_DOWN,btn2Click);
19
20
function
btn2Click(e:MouseEvent):
void
{
21
setChildIndex(ballB,
1
);
22
setChildIndex(ballA,
0
);
23
}
调整后的3D反弹
001
package
{
002
import
flash.display.Sprite;
003
import
flash.events.Event;
004
import
flash.display.StageAlign;
005
import
flash.display.StageScaleMode;
006
007
public
class
MultiBounce3D
extends
Sprite {
008
private
var
balls:
Array
;
009
private
var
numBalls:
uint
=
20
;
010
private
var
fl:
Number
=
250
;
011
private
var
vpX:
Number
=stage.stageWidth/
2
;
012
private
var
vpY:
Number
=stage.stageHeight/
2
;
013
private
var
top:
Number
=-
120
;
014
private
var
bottom:
Number
=
120
;
015
private
var
left:
Number
=-
120
;
016
private
var
right:
Number
=
120
;
017
private
var
front:
Number
=
120
;
018
private
var
back:
Number
=-
120
;
019
public
function
MultiBounce3D() {
020
init();
021
}
022
private
function
init():
void
{
023
stage.align=StageAlign.TOP_LEFT;
024
stage.scaleMode=StageScaleMode.NO_SCALE;
025
balls =
new
Array
();
026
for
(
var
i:
uint
=
0
; i < numBalls; i++) {
027
var
ball:Ball3D=
new
Ball3D(
15
,Math.random()*
0xffffff
);
028
balls.push(ball);
029
ball.vx=Math.random()*
10
-
5
;
030
ball.vy=Math.random()*
10
-
5
;
031
ball.vz=Math.random()*
10
-
5
;
032
addChild(ball);
033
}
034
addEventListener(Event.ENTER_FRAME, onEnterFrame);
035
}
036
private
function
onEnterFrame(event:Event):
void
{
037
vpX=stage.stageWidth/
2
;
038
vpY=stage.stageHeight/
2
;
039
graphics.clear();
040
for
(
var
i:
uint
=
0
; i < numBalls; i++) {
041
var
ball:Ball3D=balls[i];
042
move(ball);
043
}
044
sortZ();
045
}
046
047
function
sortZ():
void
{
048
balls.sortOn(
"zpos"
,
Array
.DESCENDING |
Array
.NUMERIC);
049
for
(
var
i:
uint
=
0
; i < numBalls; i++) {
050
var
ball:Ball3D=balls[i];
051
setChildIndex(ball, i);
052
}
053
}
054
055
private
function
move(ball:Ball3D):
void
{
056
var
radius:
Number
=ball.radius;
057
ball.xpos+=ball.vx;
058
ball.ypos+=ball.vy;
059
ball.zpos+=ball.vz;
060
061
//6边界检测
062
if
(ball.xpos+radius>right) {
063
ball.xpos=right-radius;
064
ball.vx*=-
1
;
065
}
else
if
(ball.xpos - radius < left) {
066
ball.xpos=left+radius;
067
ball.vx*=-
1
;
068
}
069
if
(ball.ypos+radius>bottom) {
070
ball.ypos=bottom-radius;
071
ball.vy*=-
1
;
072
}
else
if
(ball.ypos - radius < top) {
073
ball.ypos=top+radius;
074
ball.vy*=-
1
;
075
}
076
if
(ball.zpos+radius>front) {
077
ball.zpos=front-radius;
078
ball.vz*=-
1
;
079
}
else
if
(ball.zpos - radius < back) {
080
ball.zpos=back+radius;
081
ball.vz*=-
1
;
082
}
083
084
//转换化2D坐标
085
if
(ball.zpos>- fl) {
086
var
scale:
Number
= fl / (fl + ball.zpos);
087
ball.scaleX=ball.scaleY=scale;
088
ball.x=vpX+ball.xpos*scale;
089
ball.y=vpY+ball.ypos*scale;
090
ball.visible=
true
;
091
}
else
{
092
ball.visible=
false
;
093
}
094
095
//辅助线
096
graphics.lineStyle(
1
,
0xccccff
);
097
graphics.moveTo(
0
,stage.stageHeight/
2
);
098
graphics.lineTo(stage.stageWidth,stage.stageHeight/
2
);
099
graphics.lineTo(stage.stageWidth-
15
,stage.stageHeight/
2
-
8
);
100
graphics.moveTo(stage.stageWidth,stage.stageHeight/
2
);
101
graphics.lineTo(stage.stageWidth-
15
,stage.stageHeight/
2
+
8
);
102
103
graphics.moveTo(
0
,stage.stageHeight);
104
graphics.lineTo(stage.stageWidth,
0
);
105
graphics.lineTo(stage.stageWidth-
15
,
2
);
106
graphics.moveTo(stage.stageWidth,
0
);
107
graphics.lineTo(stage.stageWidth-
6
,
13
);
108
109
graphics.moveTo(stage.stageWidth/
2
,
0
);
110
graphics.lineTo(stage.stageWidth/
2
,stage.stageHeight);
111
graphics.lineTo(stage.stageWidth/
2
-
8
,stage.stageHeight-
15
);
112
graphics.moveTo(stage.stageWidth/
2
,stage.stageHeight);
113
graphics.lineTo(stage.stageWidth/
2
+
8
,stage.stageHeight-
15
);
114
graphics.lineStyle(
1
,
0xff99ff
);
115
graphics.moveTo(vpX,vpY);
116
graphics.lineTo(ball.x,ball.y);
117
}
118
}
119
}
3D粒子喷射:
01
package
{
02
import
flash.display.Sprite;
03
import
flash.events.Event;
04
import
flash.display.StageAlign;
05
import
flash.display.StageScaleMode;
06
07
//设置动画背景为黑色
08
[SWF(backgroundColor=
0x000000
)]
//c#中的特性? 哈
09
public
class
Fireworks
extends
Sprite {
10
private
var
balls:
Array
;
11
private
var
numBalls:
uint
=
100
;
12
private
var
fl:
Number
=
250
;
13
//消失点
14
private
var
vpX:
Number
=stage.stageWidth/
2
;
15
private
var
vpY:
Number
=stage.stageHeight/
2
;
16
private
var
gravity:
Number
=
0.2
;
17
private
var
floor:
Number
=
50
;
//y轴反弹的边界(相对消失点而言)
18
private
var
bounce:
Number
=-
0.6
;
19
public
function
Fireworks() {
20
init();
21
}
22
23
private
function
init():
void
{
24
stage.scaleMode = StageScaleMode.NO_SCALE;
25
stage.align = StageAlign.TOP_LEFT;
26
balls =
new
Array
();
27
for
(
var
i:
uint
=
0
; i < numBalls; i++) {
28
var
ball:Ball3D=
new
Ball3D(
5
,Math.random()*
0xffffff
);
29
balls.push(ball);
30
addChild(ball);
31
}
32
initVelocity();
33
addEventListener(Event.ENTER_FRAME, onEnterFrame);
34
35
}
36
37
private
function
initVelocity():
void
{
38
for
(
var
i:
uint
=
0
; i < numBalls; i++) {
39
var
ball:Ball3D=balls[i];
40
reset(ball);
41
}
42
}
43
44
private
function
reset(b:Ball3D):
void
{
45
b.ypos=-
250
;
46
b.zpos=
200
;
47
b.xpos=
0
;
48
b.vx=(Math.random()*
2
-
1
)*
3
//x轴方向速度为-3到+3的随机值(即:看起来有的球向左,有的球向右,在横向扩散)
49
b.vy=(Math.random()-
1
)*
4
;
//y轴方向为-4到0之间的随机值(即向下掉)
50
b.vz=(Math.random()-
1
)*
3
;
//z轴方向速度为-3到0的随机值(即:所有球从远处向近处喷)
51
52
}
53
54
private
function
onEnterFrame(event:Event):
void
{
55
for
(
var
i:
uint
=
0
; i < numBalls; i++) {
56
var
ball:Ball3D=balls[i];
57
move(ball);
58
}
59
sortZ();
60
}
61
private
function
move(ball:Ball3D):
void
{
62
ball.vy+=gravity;
63
ball.xpos+=ball.vx;
64
ball.ypos+=ball.vy;
65
ball.zpos+=ball.vz;
66
if
(ball.ypos>floor) {
67
ball.ypos=floor;
68
ball.vy*=bounce;
69
}
70
if
(ball.zpos>-fl) {
71
var
scale:
Number
= fl / (fl + ball.zpos);
72
ball.scaleX=ball.scaleY=scale;
73
ball.x=vpX+ball.xpos*scale;
74
ball.y=vpY+ball.ypos*scale;
75
ball.alpha = scale;
//越远的物体,越淡
76
if
(ball.x<
0
|| ball.x>stage.stageWidth || ball.y>stage.stageHeight || ball.alpha<
0.05
){
77
reset(ball);
78
}
79
ball.visible=
true
;
80
}
else
{
81
ball.visible=
false
;
82
reset(ball);
83
}
84
}
85
private
function
sortZ():
void
{
86
balls.sortOn(
"zpos"
,
Array
.DESCENDING |
Array
.NUMERIC);
87
for
(
var
i:
uint
=
0
; i < numBalls; i++) {
88
var
ball:Ball3D=balls[i];
89
setChildIndex(ball, i);
90
}
91
}
92
}
93
}
Z轴上的屏幕环绕:
类似2d中的处理,当物体在z轴的坐标达到某一限定值时,重新将其设置为相反值,通俗点讲:物体太远(接近看不见时),重新将其放在无限近的地方(通常是观察者背后),物体太近(甚至跑到观察者背后时),重新将其放在无限远处.
01
var
trees:
Array
;
02
var
numTrees:
uint
=
100
;
03
var
fl:
Number
=
250
;
04
var
vpX:
Number
=stage.stageWidth/
2
;
05
var
vpY:
Number
=stage.stageHeight/
2
;
06
var
floor:
Number
=
50
;
07
var
vz:
Number
=
0
;
08
var
friction:
Number
=
0.98
;
09
var
temp:
Number
=
0
;
10
11
12
function
init():
void
{
13
trees =
new
Array
();
14
for
(
var
i:
uint
=
0
; i < numTrees; i++) {
15
var
tree:Tree =
new
Tree();
16
trees.push(tree);
17
tree.xpos=Math.random()*
2000
-
1000
;
18
tree.ypos=floor;
19
tree.zpos=Math.random()*
10000
;
20
addChild(tree);
21
}
22
addEventListener(Event.ENTER_FRAME, EnterFrameHandler);
23
stage.addEventListener(KeyboardEvent.KEY_DOWN, KeyDownHandler);
24
}
25
26
function
EnterFrameHandler(event:Event):
void
{
27
for
(
var
i:
uint
=
0
; i < numTrees; i++) {
28
var
tree:Tree=trees[i];
29
move(tree);
30
}
31
vz*=friction;
32
sortZ();
33
34
//自动播放的处理
35
if
(temp>
500
) {
36
vz-=
0.5
;
37
}
else
{
38
vz+=
0.5
;
39
}
40
temp++;
41
if
(temp==
1000
){
42
temp=
0
;
43
}
44
trace
(temp);
45
}
46
47
//按上下键时的速度处理
48
function
KeyDownHandler(event:KeyboardEvent):
void
{
49
if
(event.keyCode==Keyboard.UP) {
50
vz-=
1
;
51
}
else
if
(event.keyCode == Keyboard.DOWN) {
52
vz+=
1
;
53
}
54
}
55
56
57
function
move(tree:Tree):
void
{
58
tree.zpos+=vz;
59
60
//如果物体跑到观察点后面了,则重新将其放在无限远处
61
if
(tree.zpos<fl*-
1
) {
62
tree.zpos+=
10000
;
63
}
64
65
//如果物体跑得太远了,重新将其放在观察点身后
66
if
(tree.zpos>
10000
-fl) {
67
tree.zpos-=
10000
;
68
}
69
70
var
scale:
Number
= fl / (fl + tree.zpos);
71
tree.scaleX=tree.scaleY=scale;
72
tree.x=vpX+tree.xpos*scale;
73
tree.y=vpY+tree.ypos*scale;
74
tree.alpha=scale*
0.8
+
0.2
;
75
}
76
77
//z轴排序
78
function
sortZ():
void
{
79
trees.sortOn(
"zpos"
,
Array
.DESCENDING |
Array
.NUMERIC);
80
for
(
var
i:
uint
=
0
; i < numTrees; i++) {
81
var
tree:Tree=trees[i];
82
setChildIndex(tree, i);
83
}
84
}
85
86
init();
这个示例中用到了一个新的类Tree,代码如下:
01
package
{
02
import
flash.display.Sprite;
03
public
class
Tree
extends
Sprite {
04
public
var
xpos:
Number
=
0
;
05
public
var
ypos:
Number
=
0
;
06
public
var
zpos:
Number
=
0
;
07
public
function
Tree() {
08
init();
09
}
10
public
function
init():
void
{
11
graphics.lineStyle(
0
,
0xffffff
);
12
graphics.lineTo(
0
,-
140
-Math.random()*
20
);
13
graphics.moveTo(
0
,-
30
-Math.random()*
30
);
14
graphics.lineTo(Math.random()*
80
-
40
,-
100
-Math.random()*
40
);
15
graphics.moveTo(
0
,-
60
-Math.random()*
40
);
16
graphics.lineTo(Math.random()*
60
-
30
,-
110
-Math.random()*
20
);
17
}
18
}
19
}
上面这个示例的加强版:(左右键控制x轴加速度,上下键控制z轴加速度,空格键整体下落)
001
package
{
002
import
flash.display.Sprite;
003
import
flash.events.Event;
004
import
flash.events.KeyboardEvent;
005
import
flash.ui.Keyboard;
006
007
[SWF(backgroundColor=
0x000000
)]
008
public
class
Trees2
extends
Sprite {
009
private
var
trees:
Array
;
010
private
var
numTrees:
uint
=
100
;
011
012
private
var
fl:
Number
=
250
;
013
014
//消失点
015
private
var
vpX:
Number
=stage.stageWidth/
2
;
016
private
var
vpY:
Number
=stage.stageHeight/
2
;
017
018
private
var
floor:
Number
=
50
;
019
020
021
//加速度
022
private
var
ax:
Number
=
0
;
023
private
var
ay:
Number
=
0
;
024
private
var
az:
Number
=
0
;
025
026
//速度
027
private
var
vx:
Number
=
0
;
028
private
var
vy:
Number
=
0
;
029
private
var
vz:
Number
=
0
;
030
031
//重力与摩擦力
032
private
var
gravity:
Number
=
0.3
;
033
private
var
friction:
Number
=
0.98
;
034
035
public
function
Trees2() {
036
init();
037
}
038
039
private
function
init():
void
{
040
trees =
new
Array
();
041
for
(
var
i:
uint
=
0
; i < numTrees; i++) {
042
var
tree:Tree =
new
Tree();
043
trees.push(tree);
044
tree.xpos=Math.random()*
2000
-
1000
;
045
tree.ypos=floor;
046
tree.zpos=Math.random()*
10000
;
047
addChild(tree);
048
}
049
addEventListener(Event.ENTER_FRAME, onEnterFrame);
050
stage.addEventListener(KeyboardEvent.KEY_DOWN, KeyDownHandler);
051
stage.addEventListener(KeyboardEvent.KEY_UP, KeyUpHandler);
052
}
053
054
private
function
onEnterFrame(event:Event):
void
{
055
vx+=ax;
056
vy+=ay;
057
vz+=az;
058
vy-=gravity;
//负重力?哈,好好理解一下,这一行结合后面的y轴坐标检测,才保证了所有树在空格键松开时,能在y轴方向上恢复原状
059
for
(
var
i:
uint
=
0
; i < numTrees; i++) {
060
var
tree:Tree=trees[i];
061
move(tree);
062
}
063
vx*=friction;
064
vy*=friction;
065
vz*=friction;
066
sortZ();
067
}
068
069
//键盘按下时,处理加速度
070
private
function
KeyDownHandler(event:KeyboardEvent):
void
{
071
switch
(event.keyCode) {
072
case
Keyboard.UP :
073
az=-
1
;
074
break
;
075
case
Keyboard.DOWN :
076
az=
1
;
077
break
;
078
case
Keyboard.LEFT :
079
ax=
1
;
080
break
;
081
case
Keyboard.RIGHT :
082
ax=-
1
;
083
break
;
084
case
Keyboard.SPACE :
085
ay=
1
;
086
break
;
087
default
:
088
break
;
089
}
090
}
091
092
//按键抬起时,加速度置0
093
private
function
KeyUpHandler(event:KeyboardEvent):
void
{
094
switch
(event.keyCode) {
095
case
Keyboard.UP :
096
case
Keyboard.DOWN :
097
az=
0
;
098
break
;
099
case
Keyboard.LEFT :
100
case
Keyboard.RIGHT :
101
ax=
0
;
102
break
;
103
case
Keyboard.SPACE :
104
ay=
0
;
105
break
;
106
default
:
107
break
;
108
}
109
}
110
111
112
private
function
move(tree:Tree):
void
{
113
tree.xpos+=vx;
114
tree.ypos+=vy;
115
tree.zpos+=vz;
116
117
118
//y轴上的坐标判断,否则所有树将一直向上跑
119
if
(tree.ypos<floor) {
120
tree.ypos=floor;
121
}
122
123
//z轴屏幕环绕
124
if
(tree.zpos<fl*-
1
) {
125
tree.zpos+=
10000
;
126
}
127
if
(tree.zpos>
10000
-fl) {
128
tree.zpos-=
10000
;
129
}
130
131
var
scale:
Number
= fl / (fl + tree.zpos);
132
tree.scaleX=tree.scaleY=scale;
133
tree.x=vpX+tree.xpos*scale;
134
tree.y=vpY+tree.ypos*scale;
135
tree.alpha=scale*
0.8
+
0.2
;
136
}
137
138
//z轴排序
139
private
function
sortZ():
void
{
140
trees.sortOn(
"zpos"
,
Array
.DESCENDING |
Array
.NUMERIC);
141
for
(
var
i:
uint
=
0
; i < numTrees; i++) {
142
var
tree:Tree=trees[i];
143
setChildIndex(tree, i);
144
}
145
}
146
}
147
}
3D缓动:
01
package
{
02
import
flash.display.Sprite;
03
import
flash.events.Event;
04
public
class
Easing3D
extends
Sprite {
05
private
var
balls:
Array
;
06
private
var
ballNum:
Number
=
20
;
07
private
var
easing:
Number
=.
1
;
08
private
var
fl:
Number
=
250
;
09
private
var
vpX:
Number
=stage.stageWidth/
2
;
10
private
var
vpY:
Number
=stage.stageHeight/
2
;
11
12
public
function
Easing3D() {
13
init();
14
}
15
private
function
init():
void
{
16
balls=
new
Array
(ballNum);
17
for
(
var
i:
int
=
0
; i<ballNum; i++) {
18
balls[i]=
new
Ball3D(
20
,Math.random()*
0xffffff
);
19
balls[i].tx=(Math.random()*
2
-
1
)*
200
;
20
balls[i].ty=(Math.random()*
2
-
1
)*
200
;
21
balls[i].tz=(Math.random()*
2
-
1
)*
500
;
22
addChild(balls[i]);
23
}
24
addEventListener(Event.ENTER_FRAME, onEnterFrame);
25
}
26
27
28
private
function
onEnterFrame(event:Event):
void
{
29
for
(
var
i:
int
=
0
; i<ballNum; i++) {
30
move(balls[i]);
31
}
32
sortZ();
33
}
34
35
function
move(b:Ball3D):
void
{
36
var
dx:
Number
=b.tx-b.xpos;
37
var
dy:
Number
=b.ty-b.ypos;
38
var
dz:
Number
=b.tz-b.zpos;
39
//缓动公式
40
b.xpos+=dx*easing;
41
b.ypos+=dy*easing;
42
b.zpos+=dz*easing;
43
var
dist:
Number
=Math.sqrt(dx*dx+dy*dy+dz*dz);
44
if
(dist<
1
) {
45
b.tx=(Math.random()*
2
-
1
)*
200
;
46
b.ty=(Math.random()*
2
-
1
)*
200
;
47
b.tz=(Math.random()*
2
-
1
)*
500
;
48
}
49
if
(b.zpos > -fl) {
50
var
scale:
Number
= fl / (fl + b.zpos);
51
b.scaleX=b.scaleY=scale;
52
b.x=vpX+b.xpos*scale;
53
b.y=vpY+b.ypos*scale;
54
b.alpha=scale*
0.7
+
0.3
;
55
b.visible=
true
;
56
}
else
{
57
b.visible=
false
;
58
}
59
}
60
61
//z轴排序
62
function
sortZ():
void
{
63
balls.sortOn(
"zpos"
,
Array
.DESCENDING |
Array
.NUMERIC);
64
for
(
var
i:
uint
=
0
; i < ballNum; i++) {
65
var
b:Ball3D=balls[i];
66
setChildIndex(b, i);
67
}
68
69
}
70
71
72
}
73
}
当然这个示例中,要对Ball3D做一些改造:
01
package
{
02
import
flash.display.Sprite;
03
public
class
Ball3D
extends
Sprite {
04
public
var
radius:
Number
;
05
private
var
color:
uint
;
06
07
public
var
xpos:
Number
=
0
;
08
public
var
ypos:
Number
=
0
;
09
public
var
zpos:
Number
=
0
;
10
11
public
var
vx:
Number
=
0
;
12
public
var
vy:
Number
=
0
;
13
public
var
vz:
Number
=
0
;
14
15
public
var
tx:
Number
=
0
;
16
public
var
ty:
Number
=
0
;
17
public
var
tz:
Number
=
0
;
18
19
public
var
mass:
Number
=
1
;
20
21
public
function
Ball3D(radius:
Number
=
40
, color:
uint
=
0xff0000
) {
22
this
.radius=radius;
23
this
.color=color;
24
init();
25
}
26
public
function
init():
void
{
27
graphics.lineStyle(
0
);
28
graphics.beginFill(color);
29
graphics.drawCircle(
0
,
0
, radius);
30
graphics.endFill();
31
}
32
}
33
}
3D弹性运动:
01
package
{
02
import
flash.display.Sprite;
03
import
flash.events.Event;
04
import
flash.events.MouseEvent;
05
public
class
Spring3D
extends
Sprite {
06
07
private
var
balls:
Array
;
08
private
var
ballNum:
Number
=
20
;
09
10
private
var
spring:
Number
=.
1
;
11
private
var
friction:
Number
=.
94
;
12
private
var
fl:
Number
=
250
;
13
private
var
vpX:
Number
=stage.stageWidth/
2
;
14
private
var
vpY:
Number
=stage.stageHeight/
2
;
15
16
var
temp:
Number
=
0
;
17
18
public
function
Spring3D() {
19
init();
20
}
21
private
function
init():
void
{
22
balls=
new
Array
(ballNum);
23
for
(
var
i:
int
=
0
; i<ballNum; i++) {
24
balls[i]=
new
Ball3D(
20
,Math.random()*
0xffffff
);
25
balls[i].tx=(Math.random()*
2
-
1
)*
200
;
26
balls[i].ty=(Math.random()*
2
-
1
)*
200
;
27
balls[i].tz=(Math.random()*
2
-
1
)*
300
;
28
addChild(balls[i]);
29
}
30
addEventListener(Event.ENTER_FRAME, onEnterFrame);
31
32
}
33
private
function
onEnterFrame(event:Event):
void
{
34
for
(
var
i:
int
=
0
; i<ballNum; i++) {
35
move(balls[i]);
36
}
37
sortZ();
38
39
temp++;
40
if
(temp>=
250
){
41
reset();
42
temp=
0
;
43
}
44
//trace(temp);
45
}
46
47
function
move(b:Ball3D):
void
{
48
var
dx:
Number
=b.tx-b.xpos;
49
var
dy:
Number
=b.ty-b.ypos;
50
var
dz:
Number
=b.tz-b.zpos;
51
b.vx+=dx*spring;
52
b.vy+=dy*spring;
53
b.vz+=dz*spring;
54
b.xpos+=b.vx;
55
b.ypos+=b.vy;
56
b.zpos+=b.vz;
57
b.vx*=friction;
58
b.vy*=friction;
59
b.vz*=friction;
60
if
(b.zpos>- fl) {
61
var
scale:
Number
= fl / (fl + b.zpos);
62
b.scaleX=b.scaleY=scale;
63
b.x=vpX+b.xpos*scale;
64
b.y=vpY+b.ypos*scale;
65
b.alpha=scale*
0.8
+
0.2
;
66
b.visible=
true
;
67
}
else
{
68
b.visible=
false
;
69
}
70
}
71
72
private
function
reset():
void
{
73
for
(
var
i:
int
=
0
; i<ballNum; i++) {
74
balls[i].tx=(Math.random()*
2
-
1
)*
200
;
75
balls[i].ty=(Math.random()*
2
-
1
)*
200
;
76
balls[i].tz=(Math.random()*
2
-
1
)*
300
;
77
}
78
}
79
80
//z轴排序
81
function
sortZ():
void
{
82
balls.sortOn(
"zpos"
,
Array
.DESCENDING |
Array
.NUMERIC);
83
for
(
var
i:
uint
=
0
; i < ballNum; i++) {
84
var
b:Ball3D=balls[i];
85
setChildIndex(b, i);
86
}
87
88
}
89
}
90
}
3D坐标旋转:
其实跟2D坐标旋转几乎完全相同,只不过要指定绕哪个轴旋转
绕z轴旋转
x1 = cos(angleZ) * x - sin(angleZ) * y;
y1 = cos(angleZ) * y + sin(angleZ) * x;
绕y轴旋转
x1 = cos(angleY) * x - sin(angleY) * z;
z1 = cos(angleY) * z + sin(angleY) * x;
绕x轴旋转
y1 = cos(angleX) * y - sin(angleX) * z;
z1 = cos(angleX) * z + sin(angleX) * y;
01
package
{
02
import
flash.display.Sprite;
03
import
flash.events.Event;
04
public
class
RotateY
extends
Sprite {
05
private
var
balls:
Array
;
06
private
var
numBalls:
uint
=
50
;
07
08
private
var
fl:
Number
=
250
;
09
private
var
vpX:
Number
=stage.stageWidth/
2
;
10
private
var
vpY:
Number
=stage.stageHeight/
2
;
11
public
function
RotateY() {
12
init();
13
}
14
private
function
init():
void
{
15
balls =
new
Array
();
16
for
(
var
i:
uint
=
0
; i < numBalls; i++) {
17
var
ball:Ball3D=
new
Ball3D(
10
,Math.random()*
0xffffff
);
18
balls.push(ball);
19
ball.xpos=Math.random()*
200
-
100
;
20
ball.ypos=Math.random()*
200
-
100
;
21
ball.zpos=(Math.random()*
2
-
1
)*
100
;
22
addChild(ball);
23
}
24
addEventListener(Event.ENTER_FRAME, onEnterFrame);
25
}
26
private
function
onEnterFrame(event:Event):
void
{
27
var
angleY:
Number
= (mouseX - vpX) * .
0004
;
//旋转的角度与鼠标的水平位置关联
28
for
(
var
i:
uint
=
0
; i < numBalls; i++) {
29
var
ball:Ball3D=balls[i];
30
rotateY(ball, angleY);
31
}
32
sortZ();
33
}
34
35
//绕y轴旋转
36
private
function
rotateY(ball:Ball3D, angleY:
Number
):
void
{
37
var
cosY:
Number
=Math.cos(angleY);
38
var
sinY:
Number
=Math.sin(angleY);
39
var
x1:
Number
=ball.xpos*cosY-ball.zpos*sinY;
40
var
z1:
Number
=ball.zpos*cosY+ball.xpos*sinY;
41
ball.xpos=x1;
42
ball.zpos=z1;
43
if
(ball.zpos>- fl) {
44
var
scale:
Number
= fl / (fl + ball.zpos);
45
ball.scaleX=ball.scaleY=scale;
46
ball.x=vpX+ball.xpos*scale;
47
ball.y=vpY+ball.ypos*scale;
48
ball.alpha = scale*
0.8
;
49
ball.visible=
true
;
50
}
else
{
51
ball.visible=
false
;
52
}
53
}
54
private
function
sortZ():
void
{
55
balls.sortOn(
"zpos"
,
Array
.DESCENDING |
Array
.NUMERIC);
56
for
(
var
i:
uint
=
0
; i < numBalls; i++) {
57
var
ball:Ball3D=balls[i];
58
setChildIndex(ball, i);
59
}
60
}
61
}
62
}
这个示例还可以扩充到3个轴的同时旋转:
01
var
balls:
Array
;
02
var
numBalls:
uint
=
50
;
03
04
var
fl:
Number
=
250
;
05
var
vpx:
Number
=stage.stageWidth/
2
;
06
var
vpy:
Number
=stage.stageHeight/
2
;
07
08
function
init():
void
{
09
balls=
new
Array
(numBalls);
10
for
(
var
i:
uint
=
0
; i<numBalls; i++) {
11
var
ball:Ball3D=
new
Ball3D(
10
,Math.random()*
0xffffff
);
12
balls[i]=ball;
13
ball.xpos = (Math.random()*
2
-
1
)*
100
;
14
ball.ypos = (Math.random()*
2
-
1
)*
100
;
15
ball.zpos = (Math.random()*
2
-
1
)*
100
;
16
addChild(ball);
17
}
18
addEventListener(Event.ENTER_FRAME,EnterFrameHandler);
19
}
20
21
function
EnterFrameHandler(e:Event):
void
{
22
var
dx:
Number
= mouseX - vpx;
23
var
dy:
Number
= mouseY - vpy;
24
var
angleY:
Number
=dx*
0.0005
;
25
var
angleX:
Number
=dy*
0.0005
;
26
var
angleZ:
Number
= Math.sqrt(dx*dx + dy*dy)*
0.0005
;
27
28
if
(dx>
0
){angleZ *=-
1
;}
//以鼠标所在点的x坐标相对于消失点的位置为判断依据,左侧z轴正向旋转,右侧z轴反向旋转
29
30
for
(
var
i:
uint
; i<numBalls; i++) {
31
var
b:Ball3D=balls[i];
32
rotateX(b,angleX);
33
rotateY(b,angleY);
34
rotateZ(b,angleZ);
35
doPerspective(b);
36
}
37
sortZ();
38
}
39
40
//x轴的坐标旋转
41
function
rotateX(ball:Ball3D, angleX:
Number
):
void
{
42
var
cosX:
Number
=Math.cos(angleX);
43
var
sinX:
Number
=Math.sin(angleX);
44
var
y1:
Number
=ball.ypos*cosX-ball.zpos*sinX;
45
var
z1:
Number
=ball.zpos*cosX+ball.ypos*sinX;
46
ball.ypos=y1;
47
ball.zpos=z1;
48
}
49
50
//y轴的坐标旋转
51
function
rotateY(ball:Ball3D, angleY:
Number
):
void
{
52
var
cosY:
Number
=Math.cos(angleY);
53
var
sinY:
Number
=Math.sin(angleY);
54
var
x1:
Number
=ball.xpos*cosY-ball.zpos*sinY;
55
var
z1:
Number
=ball.zpos*cosY+ball.xpos*sinY;
56
ball.xpos=x1;
57
ball.zpos=z1;
58
}
59
60
//z轴的坐标旋转
61
function
rotateZ(ball:Ball3D, angleZ:
Number
):
void
{
62
var
cosZ:
Number
=Math.cos(angleZ);
63
var
sinZ:
Number
=Math.sin(angleZ);
64
var
x1:
Number
=ball.xpos*cosZ-ball.ypos*sinZ;
65
var
y1:
Number
=ball.ypos*cosZ+ball.xpos*sinZ;
66
ball.xpos=x1;
67
ball.ypos=y1;
68
}
69
70
//3D透视处理
71
function
doPerspective(ball:Ball3D):
void
{
72
if
(ball.zpos>-fl) {
73
var
scale:
Number
= fl / (fl + ball.zpos);
74
ball.scaleX=ball.scaleY=scale;
75
ball.x=vpx+ball.xpos*scale;
76
ball.y=vpy+ball.ypos*scale;
77
//ball.alpha = scale*0.65;
78
ball.visible=
true
;
79
}
else
{
80
ball.visible=
false
;
81
}
82
}
83
84
//z轴排序
85
function
sortZ():
void
{
86
balls.sortOn(
"zpos"
,
Array
.DESCENDING|
Array
.NUMERIC);
87
for
(
var
i:
uint
=
0
; i<numBalls; i++) {
88
setChildIndex(balls[i],i);
89
}
90
}
91
92
init();
3D碰撞检测:
基本原理仍然可以套用前面讲过的“基于距离的检测”,当二个球之间的距离小于二球的半径合时,即认为发生了碰撞,至于碰撞检测出来以后再如何处理,就要发挥想象了
001
package
{
002
import
flash.display.Sprite;
003
import
flash.events.Event;
004
import
flash.geom.ColorTransform;
005
public
class
Collision3D
extends
Sprite {
006
private
var
balls:
Array
;
007
private
var
numBalls:
uint
=
8
;
008
private
var
fl:
Number
=
250
;
009
private
var
vpX:
Number
=stage.stageWidth/
2
;
010
011
private
var
vpY:
Number
=stage.stageHeight/
2
;
012
private
var
top:
Number
=-
100
;
013
private
var
bottom:
Number
=
100
;
014
private
var
left:
Number
=-
100
;
015
private
var
right:
Number
=
100
;
016
private
var
front:
Number
=
100
;
017
private
var
back:
Number
=-
100
;
018
public
function
Collision3D() {
019
init();
020
}
021
private
function
init():
void
{
022
balls =
new
Array
();
023
for
(
var
i:
uint
=
0
; i < numBalls; i++) {
024
var
ball:Ball3D=
new
Ball3D(
10
);
025
balls.push(ball);
026
ball.xpos=Math.random()*
400
-
200
;
027
ball.ypos=Math.random()*
400
-
200
;
028
ball.zpos=Math.random()*
400
-
200
;
029
ball.vx=Math.random()*
10
-
5
;
030
ball.vy=Math.random()*
10
-
5
;
031
ball.vz=Math.random()*
10
-
5
;
032
addChild(ball);
033
}
034
addEventListener(Event.ENTER_FRAME, onEnterFrame);
035
}
036
private
function
onEnterFrame(event:Event):
void
{
037
for
(
var
i:
uint
=
0
; i < numBalls; i++) {
038
var
ball:Ball3D=balls[i];
039
move(ball);
040
}
041
for
(i =
0
; i < numBalls -
1
; i++) {
042
var
ballA:Ball3D=balls[i];
043
for
(
var
j:
uint
= i +
1
; j < numBalls; j++) {
044
var
ballB:Ball3D=balls[j];
045
var
dx:
Number
=ballA.xpos-ballB.xpos;
046
var
dy:
Number
=ballA.ypos-ballB.ypos;
047
var
dz:
Number
=ballA.zpos-ballB.zpos;
048
var
dist:
Number
=Math.sqrt(dx*dx+dy*dy+dz*dz);
049
if
(dist<ballA.radius+ballB.radius) {
050
var
newTransform:ColorTransform =
051
new
ColorTransform(
0
,
1
,
1
,
1
, Math.random()*
255
, Math.random()*
255
, Math.random()*
255
,
0
);
052
ballA.transform.colorTransform=newTransform;
053
ballB.transform.colorTransform=newTransform;
054
}
055
}
056
}
057
sortZ();
058
}
059
private
function
move(ball:Ball3D):
void
{
060
061
var
radius:
Number
=ball.radius;
062
ball.xpos+=ball.vx;
063
ball.ypos+=ball.vy;
064
ball.zpos+=ball.vz;
065
if
(ball.xpos+radius>right) {
066
ball.xpos=right-radius;
067
ball.vx*=-
1
;
068
}
else
if
(ball.xpos - radius < left) {
069
ball.xpos=left+radius;
070
ball.vx*=-
1
;
071
}
072
if
(ball.ypos+radius>bottom) {
073
ball.ypos=bottom-radius;
074
ball.vy*=-
1
;
075
}
else
if
(ball.ypos - radius < top) {
076
ball.ypos=top+radius;
077
ball.vy*=-
1
;
078
}
079
if
(ball.zpos+radius>front) {
080
ball.zpos=front-radius;
081
ball.vz*=-
1
;
082
}
else
if
(ball.zpos - radius < back) {
083
ball.zpos=back+radius;
084
ball.vz*=-
1
;
085
}
086
if
(ball.zpos>- fl) {
087
var
scale:
Number
= fl / (fl + ball.zpos);
088
ball.scaleX=ball.scaleY=scale;
089
ball.x=vpX+ball.xpos*scale;
090
ball.y=vpY+ball.ypos*scale;
091
ball.visible=
true
;
092
}
else
{
093
ball.visible=
false
;
094
}
095
}
096
private
function
sortZ():
void
{
097
balls.sortOn(
"zpos"
,
Array
.DESCENDING |
Array
.NUMERIC);
098
for
(
var
i:
uint
=
0
; i < numBalls; i++) {
099
var
ball:Ball3D=balls[i];
100
setChildIndex(ball, i);
101
}
102
}
103
}
104
}
上面的示例中,生二球发生碰撞后,颜色变为随机色(当然您可以处理根据前面讲过的动量守恒原理对速度做处理,不过在3D空间中要同时计算x,y,z三个轴的速度大小以及方向,运算量是比较大的,各位有兴趣可自行尝试)