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 }
思考一下:这样为啥不会造成死循环?
浙公网安备 33010602011771号