Flash/Flex学习笔记(40):弹性运动续--弹簧
上一篇里演示的弹性运动加上摩擦力因素后,物体最终基本上都会比较准确的停在目标位置。但是我们回想一下现实世界中的弹簧,如果把弹簧的一头固定起来(即相当于目标点),而另一端栓一个球,把球拉开或压缩一定距离然后松手,事实上小球永远也不可能到达弹簧固定的那一端(因为弹簧即使压缩到最紧,也总有一定的长度)
所以如果要在Flash里模拟现实中的弹簧,真正的目标点绝不是弹簧的端点,而是目标点再偏移一段距离(即弹簧自然伸展时的长度)
01
var ball:Ball =
new
Ball(6);
02
addChild(ball);
03
ball.y = 20;
04
ball.x = 20;
05
06
var targetX:Number=stage.stageWidth/2;
07
var targetY:Number=ball.y;
08
09
var springLength = 100;
//弹簧长度
10
var spring = 0.2;
//弹性系数
11
var friction = 0.92;
//摩擦系数
12
13
//画辅助线,以便看得更清楚
14
graphics.lineStyle(0.5,0xaaaaaa);
15
graphics.moveTo(ball.x,ball.y);
16
graphics.lineTo(stage.stageWidth-ball.x,ball.y);
17
graphics.moveTo(targetX,targetY-10);
18
graphics.lineTo(targetX,targetY+10);
19
graphics.moveTo(targetX-springLength,targetY-8);
20
graphics.lineTo(targetX-springLength,targetY+8);
21
22
var rect:Rectangle =
new
Rectangle(ball.x,ball.y,stage.stageWidth-ball.x*2,0);
23
24
addEventListener(Event.ENTER_FRAME,EnterFrameHandler);
25
ball.addEventListener(MouseEvent.MOUSE_DOWN,MouseDownHandler);
26
stage.addEventListener(MouseEvent.MOUSE_UP,MouseUpHandler);
27
28
function EnterFrameHandler(e:Event):
void
{
29
ball.vx += (targetX - springLength - ball.x)*spring;
30
ball.vx *= friction;
31
ball.x += ball.vx;
32
}
33
34
function MouseDownHandler(e:MouseEvent):
void
{
35
(e.target
as
Sprite).startDrag(
true
,rect);
36
removeEventListener(Event.ENTER_FRAME,EnterFrameHandler);
37
}
38
39
function MouseUpHandler(e:MouseEvent):
void
{
40
ball.stopDrag();
41
addEventListener(Event.ENTER_FRAME,EnterFrameHandler);
42
}
43
44
ball.addEventListener(MouseEvent.MOUSE_OUT,function(){Mouse.cursor = MouseCursor.AUTO});
45
ball.addEventListener(MouseEvent.MOUSE_OVER,function(){Mouse.cursor = MouseCursor.HAND});
如果考虑到二维坐标的弹簧运动,要稍微复杂一点:
01
var
ball:Ball =
new
Ball(
10
);
02
addChild(ball);
03
ball.y =
20
;
04
ball.x =
20
;
05
06
var
targetX:
Number
=stage.stageWidth/
2
;
07
var
targetY:
Number
=stage.stageHeight/
2
;
08
09
var
springLength:
uint
=
100
;
//弹簧长度
10
var
spring:
Number
=
0.2
;
//弹性系数
11
var
friction:
Number
=
0.92
;
//摩擦系数
12
var
angle:
Number
=
0
;
13
14
addEventListener(Event.ENTER_FRAME,EnterFrameHandler);
15
ball.addEventListener(MouseEvent.MOUSE_DOWN,MouseDownHandler);
16
stage.addEventListener(MouseEvent.MOUSE_UP,MouseUpHandler);
17
stage.addEventListener(MouseEvent.MOUSE_MOVE,
function
(){DrawLine()});
18
19
angle = Math.atan2(targetY - ball.y,targetX -ball.x);
//确定夹角
20
trace
(angle*
180
/Math.PI);
21
22
function
EnterFrameHandler(e:Event):
void
{
23
ball.vx += (targetX - springLength*Math.cos(angle) - ball.x)*spring;
//调整目标点
24
ball.vy += (targetY - springLength*Math.sin(angle) - ball.y)*spring;
25
ball.vx *= friction;
26
ball.vy *= friction;
27
ball.x += ball.vx;
28
ball.y += ball.vy;
29
DrawLine();
30
}
31
32
function
DrawLine():
void
{
33
graphics.clear();
34
graphics.lineStyle(
1
);
35
graphics.moveTo(targetX,targetY-
10
);
36
graphics.lineTo(targetX,targetY+
10
);
37
graphics.moveTo(targetX-
10
,targetY);
38
graphics.lineTo(targetX+
10
,targetY);
39
graphics.moveTo(targetX,targetY);
40
graphics.lineStyle(
0.5
,
0xaaaaaa
);
41
graphics.lineTo(ball.x,ball.y);
42
}
43
44
function
MouseDownHandler(e:MouseEvent):
void
{
45
(e.target
as
Sprite).startDrag(
true
);
46
removeEventListener(Event.ENTER_FRAME,EnterFrameHandler);
47
}
48
49
function
MouseUpHandler(e:MouseEvent):
void
{
50
ball.stopDrag();
51
addEventListener(Event.ENTER_FRAME,EnterFrameHandler);
52
}
53
54
ball.addEventListener(MouseEvent.MOUSE_OUT,
function
(){Mouse.cursor = MouseCursor.AUTO});
55
ball.addEventListener(MouseEvent.MOUSE_OVER,
function
(){Mouse.cursor = MouseCursor.HAND});
上面的例子中,移动的方向(即夹角)与目标点都是固定的,如果改成动态的(比如鼠标当前所在位置),效果可能更逼真
01
function
EnterFrameHandler(e:Event):
void
{
02
targetX = mouseX;
//改成动态目标
03
targetY = mouseY;
04
angle = Math.atan2(targetY - ball.y,targetX -ball.x);
//动态夹角
05
ball.vx += (targetX - springLength*Math.cos(angle) - ball.x)*spring;
06
ball.vy += (targetY - springLength*Math.sin(angle) - ball.y)*spring;
07
ball.vx *= friction;
08
ball.vy *= friction;
09
ball.x += ball.vx;
10
ball.y += ball.vy;
11
DrawLine();
12
}
如果二个物体相互以对方所在位置为目标做弹性运动,同时再考虑弹簧长度,边界检测等因素,可以用AS3模拟出一个极逼真的弹簧模型:
01
var
ball_1:Ball =
new
Ball(
10
,
0xff0000
);
02
var
ball_2:Ball =
new
Ball(
10
,
0x0000ff
);
03
ball_1.x = stage.stageWidth * Math.random();
04
ball_1.y = stage.stageHeight * Math.random();
05
ball_2.x = stage.stageWidth/
2
;
06
ball_2.y = stage.stageHeight/
2
;
07
08
addChild(ball_1);
09
addChild(ball_2);
10
11
var
spring:
Number
=
0.1
;
12
var
springLength:
uint
=
100
;
13
var
friction:
Number
=
0.9
;
14
var
darggingBall:Ball;
15
16
addEventListener(Event.ENTER_FRAME,EnterFrameHandler);
17
ball_1.addEventListener(MouseEvent.MOUSE_DOWN,MouseDownHandler);
18
ball_2.addEventListener(MouseEvent.MOUSE_DOWN,MouseDownHandler);
19
stage.addEventListener(MouseEvent.MOUSE_UP,MouseUpHandler);
20
stage.addEventListener(MouseEvent.MOUSE_MOVE,
function
(){DrawLine();});
21
ball_1.addEventListener(MouseEvent.MOUSE_OVER,MouseOverHandler);
22
ball_1.addEventListener(MouseEvent.MOUSE_OUT,MouseOutHandler);
23
ball_2.addEventListener(MouseEvent.MOUSE_OVER,MouseOverHandler);
24
ball_2.addEventListener(MouseEvent.MOUSE_OUT,MouseOutHandler);
25
26
function
MouseOutHandler(e:MouseEvent){
27
Mouse.cursor = MouseCursor.AUTO;
28
}
29
30
function
MouseOverHandler(e:MouseEvent){
31
Mouse.cursor = MouseCursor.HAND;
32
}
33
34
function
MouseDownHandler(e:MouseEvent):
void
{
35
(e.target
as
Sprite).startDrag(
true
,
new
Rectangle(
20
,
20
,stage.stageWidth-
40
,stage.stageHeight-
40
));
36
darggingBall = e.target
as
Ball;
37
removeEventListener(Event.ENTER_FRAME,EnterFrameHandler);
38
}
39
40
function
MouseUpHandler(e:MouseEvent):
void
{
41
if
(darggingBall!=
null
){
42
darggingBall.stopDrag();
43
darggingBall =
null
;
44
addEventListener(Event.ENTER_FRAME,EnterFrameHandler);
45
}
46
}
47
48
function
EnterFrameHandler(e:Event):
void
{
49
var
dx1 = ball_2.x -ball_1.x;
50
var
dy1 = ball_2.y -ball_1.y;
51
var
angle1:
Number
= Math.atan2(dy1,dx1);
52
ball_1.vx += (ball_2.x - springLength * Math.cos(angle1) - ball_1.x) * spring;
53
ball_1.vy += (ball_2.y - springLength * Math.sin(angle1) - ball_1.y) * spring;
54
ball_1.vx *= friction;
55
ball_1.vy *= friction;
56
ball_1.x += ball_1.vx;
57
ball_1.y += ball_1.vy;
58
59
60
var
dx2 = ball_1.x -ball_2.x;
61
var
dy2 = ball_1.y -ball_2.y;
62
var
angle2:
Number
= Math.atan2(dy2,dx2);
63
ball_2.vx += (ball_1.x - springLength * Math.cos(angle2) - ball_2.x) * spring;
64
ball_2.vy += (ball_1.y - springLength * Math.sin(angle2) - ball_2.y) * spring;
65
ball_2.vx *= friction;
66
ball_2.vy *= friction;
67
ball_2.x += ball_2.vx;
68
ball_2.y += ball_2.vy;
69
70
DrawLine();
71
72
CheckBoundary(ball_1);
73
CheckBoundary(ball_2);
74
75
76
}
77
78
function
DrawLine():
void
{
79
graphics.clear();
80
graphics.lineStyle(
0.5
,
0x666666
);
81
graphics.moveTo(ball_1.x,ball_1.y);
82
graphics.lineTo(ball_2.x,ball_2.y);
83
}
84
85
function
CheckBoundary(b:Ball){
86
if
(b.x>stage.stageWidth-b.width/
2
|| b.x<=b.width/
2
){
87
b.x -= b.vx;
88
b.vx *= -
1
;
89
}
90
91
if
(b.y>stage.stageHeight-b.height/
2
|| b.y<=b.height/
2
){
92
b.y -= b.vy;
93
b.vy *= -
1
;
94
}
95
}
如果玩得再疯狂一点,多放一些小球,让第二个以第一个为目标,第三个以第二个为目标...最后一个再以第一个为目标,这样构成一个环,大概就是下面这个样子:
001
var
spring:
Number
=
0.1
;
002
var
springLength:
uint
=
150
;
003
var
friction:
Number
=
0.8
;
//摩擦力
004
var
darggingBall:Ball;
005
var
ballNumber:
uint
=
3
;
//小球个数
006
007
var
arrBalls:
Array
=
new
Array
(ballNumber);
008
009
for
(
var
i:
uint
=
0
,j=arrBalls.length;i<j;i++){
010
arrBalls[i] =
new
Ball(
20
,Math.random() *
0xffffff
);
011
var
_ball:Ball = arrBalls[i];
012
_ball.x=stage.stageWidth*Math.random();
013
_ball.y=stage.stageHeight*Math.random();
014
addChild(_ball);
015
_ball.addEventListener(MouseEvent.MOUSE_OVER,MouseOverHandler);
016
_ball.addEventListener(MouseEvent.MOUSE_OUT,MouseOutHandler);
017
_ball.addEventListener(MouseEvent.MOUSE_DOWN,MouseDownHandler);
018
}
019
020
addEventListener(Event.ENTER_FRAME,EnterFrameHandler);
021
022
023
stage.addEventListener(MouseEvent.MOUSE_UP,MouseUpHandler);
024
stage.addEventListener(MouseEvent.MOUSE_MOVE,
function
(){DrawLine();});
025
026
027
//切换光标
028
function
MouseOutHandler(e:MouseEvent) {
029
Mouse.cursor=MouseCursor.AUTO;
030
}
031
032
//切换光标
033
function
MouseOverHandler(e:MouseEvent) {
034
Mouse.cursor=MouseCursor.HAND;
035
}
036
037
//开始拖动
038
function
MouseDownHandler(e:MouseEvent):
void
{
039
(e.target
as
Sprite).startDrag(
true
,
new
Rectangle(
20
,
20
,stage.stageWidth-
40
,stage.stageHeight-
40
));
040
darggingBall=e.target
as
Ball;
041
removeEventListener(Event.ENTER_FRAME,EnterFrameHandler);
042
}
043
044
//结束拖动
045
function
MouseUpHandler(e:MouseEvent):
void
{
046
if
(darggingBall!=
null
) {
047
darggingBall.stopDrag();
048
darggingBall=
null
;
049
addEventListener(Event.ENTER_FRAME,EnterFrameHandler);
050
}
051
}
052
053
function
EnterFrameHandler(e:Event):
void
{
054
055
for
(
var
i:
uint
=
0
,j=arrBalls.length-
1
;i<j;i++){
056
SpringTo(arrBalls[i],arrBalls[i+
1
]);
057
}
058
SpringTo(arrBalls[arrBalls.length-
1
],arrBalls[
0
]);
059
060
DrawLine();
061
062
for
(i=
0
,j=arrBalls.length;i<j;i++){
063
CheckBoundary(arrBalls[i]);
064
}
065
066
067
}
068
069
070
//画连接线
071
function
DrawLine():
void
{
072
graphics.clear();
073
graphics.lineStyle(
0.5
,
0x666666
);
074
//graphics.moveTo(ball_1.x,ball_1.y);
075
//graphics.lineTo(ball_2.x,ball_2.y);
076
077
for
(
var
i:
uint
=
0
,j=arrBalls.length-
1
;i<j;i++){
078
graphics.moveTo(arrBalls[i].x,arrBalls[i].y);
079
graphics.lineTo(arrBalls[i+
1
].x,arrBalls[i+
1
].y);
080
}
081
graphics.lineTo(arrBalls[
0
].x,arrBalls[
0
].y);
082
}
083
084
//弹性运动处理
085
function
SpringTo(targetBall:Ball,moveBall:Ball):
void
{
086
var
dy=targetBall.y-moveBall.y;
087
var
dx=targetBall.x-moveBall.x;
088
var
angle1:
Number
=Math.atan2(dy,dx);
089
moveBall.vx += (targetBall.x - springLength * Math.cos(angle1) - moveBall.x) * spring;
090
moveBall.vy += (targetBall.y - springLength * Math.sin(angle1) - moveBall.y) * spring;
091
moveBall.vx *= friction;
092
moveBall.vy *= friction;
093
moveBall.x += moveBall.vx;
094
moveBall.y += moveBall.vy;
095
}
096
097
//检测边界
098
function
CheckBoundary(b:Ball) {
099
if
(b.x>stage.stageWidth-b.width/
2
||b.x<=b.width/
2
) {
100
b.x-=b.vx;
101
b.vx*=-
1
;
102
}
103
104
if
(b.y>stage.stageHeight-b.height/
2
||b.y<=b.height/
2
) {
105
b.y-=b.vy;
106
b.vy*=-
1
;
107
}
108
}
思考一下:这样为啥不会造成死循环?