Flash/Flex学习笔记(40):弹性运动续弹簧

上一篇里演示的弹性运动加上摩擦力因素后,物体最终基本上都会比较准确的停在目标位置。但是我们回想一下现实世界中的弹簧,如果把弹簧的一头固定起来(即相当于目标点),而另一端栓一个球,把球拉开或压缩一定距离然后松手,事实上小球永远也不可能到达弹簧固定的那一端(因为弹簧即使压缩到最紧,也总有一定的长度)

所以如果要在Flash里模拟现实中的弹簧,真正的目标点绝不是弹簧的端点,而是目标点再偏移一段距离(即弹簧自然伸展时的长度)

show sourceview source

print?

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});

如果考虑到二维坐标的弹簧运动,要稍微复杂一点:

show sourceview source

print?

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});

上面的例子中,移动的方向(即夹角)与目标点都是固定的,如果改成动态的(比如鼠标当前所在位置),效果可能更逼真

view source

print?

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模拟出一个极逼真的弹簧模型:

show sourceview source

print?

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
}

如果玩得再疯狂一点,多放一些小球,让第二个以第一个为目标,第三个以第二个为目标...最后一个再以第一个为目标,这样构成一个环,大概就是下面这个样子:

show sourceview source

print?

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
}

思考一下:这样为啥不会造成死循环?

原文地址:https://www.cnblogs.com/happysky97/p/1884575.html