万有引用公式:
其中G为万有引力常数
001
var
numParticles:
uint
=
50
;
//粒子总数
002
var
G:
Number
=
0.03
;
//万有引力常数
003
var
particles:
Array
=
new
Array
(numParticles);
004
var
bounce:
Number
=-
0.4
;
//边界反弹系统
005
006
//初始化
007
function
init():
void
{
008
particles =
new
Array
();
009
for
(
var
i:
uint
=
0
; i < numParticles; i++) {
010
var
size:
Number
=Math.random()*
12
+
3
;
011
var
particle:Ball=
new
Ball(size,Math.random()*
0xffffff
);
012
particle.x=Math.random()*stage.stageWidth;
013
particle.y=Math.random()*stage.stageHeight;
014
particle.mass=Math.PI * size * size;
//质量与球截面积关联,即从视觉效果上看,个头越大,越重
015
addChild(particle);
016
particles.push(particle);
017
}
018
addEventListener(Event.ENTER_FRAME, EnterFrameHandler);
019
}
020
021
022
function
EnterFrameHandler(event:Event):
void
{
023
for
(
var
i:
uint
=
0
; i < numParticles; i++) {
024
var
particle:Ball=particles[i];
025
particle.x+=particle.vx;
026
particle.y+=particle.vy;
027
}
028
for
(i=
0
; i < numParticles -
1
; i++) {
029
var
partA:Ball=particles[i];
030
for
(
var
j:
uint
= i +
1
; j < numParticles; j++) {
031
var
partB:Ball=particles[j];
032
checkCollision(partA,partB);
//检测碰撞
033
gravitate(partA, partB);
//万有引力处理
034
}
035
checkWalls(partA);
//边界检测
036
}
037
}
038
039
//万有引力处理
040
function
gravitate(partA:Ball, partB:Ball):
void
{
041
var
dx:
Number
=partB.x-partA.x;
042
var
dy:
Number
=partB.y-partA.y;
043
var
distSQ:
Number
=dx*dx+dy*dy;
044
var
dist:
Number
=Math.sqrt(distSQ);
045
var
force:
Number
=G*partA.mass*partB.mass/distSQ;
//计算partA与partB的万有引力
046
var
forceX:
Number
=force*dx/dist;
//即:force * cos(a) --万有引力在x方向上的分量
047
var
forceY:
Number
=force*dy/dist;
//即:force * sin(a) --万有引力在y方向上的分量
048
partA.vx+=forceX/partA.mass;
//牛顿定律a = F/m 在这里得到体现
049
partA.vy+=forceY/partA.mass;
050
partB.vx-=forceX/partB.mass;
051
partB.vy-=forceY/partB.mass;
052
}
053
054
//动量守恒的碰撞检测
055
function
checkCollision(ball0:Ball, ball1:Ball):
void
{
056
var
dx:
Number
=ball1.x-ball0.x;
057
var
dy:
Number
=ball1.y-ball0.y;
058
var
dist:
Number
=Math.sqrt(dx*dx+dy*dy);
059
if
(dist<ball0.radius+ball1.radius) {
060
var
angle:
Number
=Math.atan2(dy,dx);
061
var
sin:
Number
=Math.sin(angle);
062
var
cos:
Number
=Math.cos(angle);
063
var
pos0:Point=
new
Point(
0
,
0
);
064
var
pos1:Point=rotate(dx,dy,sin,cos,
true
);
065
var
vel0:Point=rotate(ball0.vx,ball0.vy,sin,cos,
true
);
066
var
vel1:Point=rotate(ball1.vx,ball1.vy,sin,cos,
true
);
067
var
vxTotal:
Number
=vel0.x-vel1.x;
068
vel0.x = ((ball0.mass - ball1.mass) * vel0.x +
2
* ball1.mass * vel1.x) / (ball0.mass + ball1.mass);
069
vel1.x=vxTotal+vel0.x;
070
var
sumRadius:
Number
=ball0.radius+ball1.radius;
071
var
overlap:
Number
=sumRadius-Math.abs(pos0.x-pos1.x);
072
var
aRadio:
Number
=ball0.radius/sumRadius;
073
var
bRadio:
Number
=ball1.radius/sumRadius;
074
if
(overlap>
0
) {
075
if
(pos0.x>pos1.x) {
076
pos0.x+=overlap*aRadio;
077
pos1.x-=overlap*bRadio;
078
}
else
{
079
pos0.x-=overlap*aRadio;
080
pos1.x+=overlap*bRadio;
081
}
082
}
083
var
pos0F:
Object
=rotate(pos0.x,pos0.y,sin,cos,
false
);
084
var
pos1F:
Object
=rotate(pos1.x,pos1.y,sin,cos,
false
);
085
ball1.x=ball0.x+pos1F.x;
086
ball1.y=ball0.y+pos1F.y;
087
ball0.x=ball0.x+pos0F.x;
088
ball0.y=ball0.y+pos0F.y;
089
var
vel0F:
Object
=rotate(vel0.x,vel0.y,sin,cos,
false
);
090
var
vel1F:
Object
=rotate(vel1.x,vel1.y,sin,cos,
false
);
091
ball0.vx=vel0F.x;
092
ball0.vy=vel0F.y;
093
ball1.vx=vel1F.x;
094
ball1.vy=vel1F.y;
095
}
096
}
097
098
//坐标旋转辅助方法
099
function
rotate(x:
Number
, y:
Number
, sin:
Number
, cos:
Number
, reverse:
Boolean
):Point {
100
var
result:Point =
new
Point();
101
if
(reverse) {
102
result.x=x*cos+y*sin;
103
result.y=y*cos-x*sin;
104
}
else
{
105
result.x=x*cos-y*sin;
106
result.y=y*cos+x*sin;
107
}
108
return
result;
109
}
110
111
112
//舞台边界检测
113
function
checkWalls(b:Ball) {
114
if
(b.x<b.radius) {
115
b.x=b.radius;
116
b.vx*=bounce;
117
}
else
if
(b.x>stage.stageWidth-b.radius) {
118
b.x=stage.stageWidth-b.radius;
119
b.vx*=bounce;
120
}
121
if
(b.y<b.radius) {
122
b.y=b.radius;
123
b.vy*=bounce;
124
}
else
if
(b.y>stage.stageHeight-b.radius) {
125
b.y=stage.stageHeight-b.radius;
126
b.vy*=bounce;
127
}
128
}
129
130
131
init();
132
133
btnReset.addEventListener(MouseEvent.MOUSE_DOWN,MouseDownHandler);
134
135
function
MouseDownHandler(e:MouseEvent):
void
{
136
removeEventListener(Event.ENTER_FRAME, EnterFrameHandler);
137
for
(
var
i:
uint
=
0
; i < numParticles; i++) {
138
var
particle:Ball=particles[i];
139
particle.x=Math.random()*stage.stageWidth;
140
particle.y=Math.random()*stage.stageHeight;
141
particle.vx=
0
;
142
particle.vy=
0
;
143
}
144
addEventListener(Event.ENTER_FRAME, EnterFrameHandler);
145
}
代码虽然很长,但是其中有很多都是上一篇里封装好的方法直接复制过来的,应该不难理解
再来模拟一下地球绕着太阳转:
01
var
numParticles:
uint
=
2
;
//粒子总数
02
var
G:
Number
=
0.03
;
//万有引力常数
03
var
particles:
Array
=
new
Array
(numParticles);
04
var
i:
Number
=
0
;
05
06
07
//初始化
08
function
init():
void
{
09
particles =
new
Array
();
10
var
sun:Ball =
new
Ball(
30
,
0xff0000
);
11
sun.x = stage.stageWidth /
2
;
12
sun.y = stage.stageHeight /
2
;
13
sun.mass =
900000
;
14
addChild(sun);
15
particles.push(sun);
16
var
planet:Ball =
new
Ball(
10
,
0x0000ff
);
17
planet.x = stage.stageWidth /
2
+
200
;
18
planet.y = stage.stageHeight /
2
;
19
planet.vy =
8
;
20
planet.mass =
1
;
21
addChild(planet);
22
particles.push(planet);
23
addEventListener(Event.ENTER_FRAME, EnterFrameHandler);
24
graphics.lineStyle(
1
,
0xdddddd
);
25
graphics.moveTo(planet.x,planet.y);
26
}
27
28
29
function
EnterFrameHandler(event:Event):
void
{
30
for
(
var
i:
uint
=
0
; i < numParticles; i++) {
31
var
particle:Ball=particles[i];
32
particle.x+=particle.vx;
33
particle.y+=particle.vy;
34
}
35
for
(i=
0
; i < numParticles -
1
; i++) {
36
var
partA:Ball=particles[i];
37
for
(
var
j:
uint
= i +
1
; j < numParticles; j++) {
38
var
partB:Ball=particles[j];
39
gravitate(partA, partB);
//万有引力处理
40
}
41
}
42
}
43
44
//万有引力处理
45
function
gravitate(partA:Ball, partB:Ball):
void
{
46
47
var
dx:
Number
=partB.x-partA.x;
48
var
dy:
Number
=partB.y-partA.y;
49
var
distSQ:
Number
=dx*dx+dy*dy;
50
var
dist:
Number
=Math.sqrt(distSQ);
51
var
force:
Number
=G*partA.mass*partB.mass/distSQ;
//计算partA与partB的万有引力
52
var
forceX:
Number
=force*dx/dist;
//即:force * cos(a) --万有引力在x方向上的分量
53
var
forceY:
Number
=force*dy/dist;
//即:force * sin(a) --万有引力在y方向上的分量
54
/*
55
partA.vx+=forceX/partA.mass;//牛顿定律a = F/m 在这里得到体现
56
partA.vy+=forceY/partA.mass;
57
*/
58
partB.vx-=forceX/partB.mass;
59
partB.vy-=forceY/partB.mass;
60
trace
(i);
61
if
(i<=
1000
){
62
graphics.lineTo(partB.x,partB.y);
63
i++;
64
}
65
else
{
66
graphics.clear();
67
graphics.lineStyle(
1
,
0xdddddd
);
68
graphics.moveTo(partB.x,partB.y);
69
i=
0
;
70
}
71
72
}
73
74
init();
代码就是在第一段的基础上修改的,可以看到在"远日点"速度较慢(因为距离越远,万有引力越小,对应的加速度也较小),在"近日点"速度较快(距离越近,万有引力越大,对应的加速度也较大)
节点花园NodeGarden:
为啥叫这个名字,我也说不上来,反正ActionScript3.0 in Animation一书的作者是这么叫的。
01
var
particles:
Array
;
02
var
numParticles:
uint
=
60
;
03
var
minDist:
Number
=
100
;
04
var
springAmount:
Number
=
0.0004
;
05
var
friction:
Number
=
0.9995
;
06
07
function
init():
void
{
08
stage.scaleMode=StageScaleMode.NO_SCALE;
09
stage.align=StageAlign.TOP_LEFT;
10
particles =
new
Array
();
11
for
(
var
i:
uint
=
0
; i < numParticles; i++) {
12
var
particle:Ball=
new
Ball(Math.random()*
3
+
2
,
0xffffff
);
13
particle.x=Math.random()*stage.stageWidth;
14
particle.y=Math.random()*stage.stageHeight;
15
particle.vx=Math.random()*
6
-
3
;
16
particle.vy=Math.random()*
6
-
3
;
17
addChild(particle);
18
particles.push(particle);
19
}
20
addEventListener(Event.ENTER_FRAME, EnterFrameHandler);
21
}
22
23
function
EnterFrameHandler(event:Event):
void
{
24
graphics.clear();
25
for
(
var
i:
uint
=
0
; i < numParticles; i++) {
26
var
particle:Ball=particles[i];
27
particle.x+=particle.vx;
28
particle.y+=particle.vy;
29
30
//屏幕环绕处理
31
if
(particle.x>stage.stageWidth) {
32
particle.x=
0
;
33
}
else
if
(particle.x <
0
) {
34
particle.x=stage.stageWidth;
35
}
36
if
(particle.y>stage.stageHeight) {
37
particle.y=
0
;
38
}
else
if
(particle.y <
0
) {
39
particle.y=stage.stageHeight;
40
}
41
}
42
for
(i=
0
; i < numParticles -
1
; i++) {
43
var
partA:Ball=particles[i];
44
for
(
var
j:
uint
= i +
1
; j < numParticles; j++) {
45
var
partB:Ball=particles[j];
46
spring(partA, partB);
//每个粒子均与其它粒子进行弹性运动处理
47
}
48
49
partA.vx *= friction;
50
partA.vy *= friction;
51
}
52
}
53
54
function
spring(partA:Ball, partB:Ball):
void
{
55
var
dx:
Number
=partB.x-partA.x;
56
var
dy:
Number
=partB.y-partA.y;
57
var
dist:
Number
=Math.sqrt(dx*dx+dy*dy);
58
if
(dist<minDist) {
59
graphics.lineStyle(
1
,
0x00ff00
,
1
- dist / minDist);
//注意这里的透明度设置:二球越来越近时,线条越来越明显,距离越来越远时,线条越来越淡
60
graphics.moveTo(partA.x, partA.y);
61
graphics.lineTo(partB.x, partB.y);
62
//类似弹性运动处理
63
var
ax:
Number
=dx*springAmount;
64
var
ay:
Number
=dy*springAmount;
65
//A球加速
66
partA.vx+=ax;
67
partA.vy+=ay;
68
//B球减速
69
partB.vx-=ax;
70
partB.vy-=ay;
71
//一个球越来越快,一个球越来越慢,所以会不断拉近(当然:前提是在有效距离内)
72
73
74
}
75
76
}
77
78
init();
关于这个效果,建议初次接触的同学们,先回顾一下弹性运动:Flash/Flex学习笔记(40):弹性运动续--弹簧
可以稍加改进,加入质量因素:
01
var
particles:
Array
;
02
var
numParticles:
uint
=
30
;
03
var
minDist:
Number
=
120
;
04
var
springAmount:
Number
=
0.03
;
05
var
friction:
Number
=
0.998
;
06
var
stageHeight:
Number
= stage.stageHeight;
07
var
stageWidth:
Number
= stage.stageWidth;
08
09
function
init():
void
{
10
stage.scaleMode=StageScaleMode.NO_SCALE;
11
stage.align=StageAlign.TOP_LEFT;
12
particles =
new
Array
();
13
for
(
var
i:
uint
=
0
; i < numParticles; i++) {
14
var
particle:Ball=
new
Ball(Math.random()*
5
+
2
,
0xffffff
);
15
particle.x=Math.random()*stageWidth;
16
particle.y=Math.random()*stageHeight;
17
particle.vx=Math.random()*
6
-
3
;
18
particle.vy=Math.random()*
6
-
3
;
19
particle.mass = Math.PI*particle.radius*particle.radius;
//加入质量
20
addChild(particle);
21
particles.push(particle);
22
}
23
addEventListener(Event.ENTER_FRAME, EnterFrameHandler);
24
stage.addEventListener(MouseEvent.MOUSE_MOVE,MouseMoveHandler);
25
}
26
27
//鼠标互动
28
function
MouseMoveHandler(e:MouseEvent):
void
{
29
var
dx:
Number
= mouseX - stageWidth/
2
;
30
var
dy:
Number
= mouseY - stageHeight/
2
;
31
for
(
var
i:
uint
=
0
; i < numParticles; i++) {
32
var
b:Ball=particles[i];
33
b.x -= dx/b.mass;
34
b.y -= dy/b.mass;
35
}
36
}
37
38
function
EnterFrameHandler(e:Event):
void
{
39
graphics.clear();
40
for
(
var
i:
uint
=
0
; i < numParticles; i++) {
41
var
particle:Ball=particles[i];
42
particle.x+=particle.vx;
43
particle.y+=particle.vy;
44
//屏幕环绕处理
45
if
(particle.x>stageWidth) {
46
particle.x=
0
;
47
}
else
if
(particle.x <
0
) {
48
particle.x=stageWidth;
49
}
50
if
(particle.y>stageHeight) {
51
particle.y=
0
;
52
}
else
if
(particle.y <
0
) {
53
particle.y=stageHeight;
54
}
55
}
56
for
(i=
0
; i < numParticles -
1
; i++) {
57
var
partA:Ball=particles[i];
58
for
(
var
j:
uint
= i +
1
; j < numParticles; j++) {
59
var
partB:Ball=particles[j];
60
spring(partA, partB);
//每个粒子均与其它粒子进行弹性运动处理
61
}
62
63
partA.vx *= friction;
64
partA.vy *= friction;
65
}
66
}
67
68
function
spring(partA:Ball, partB:Ball):
void
{
69
var
dx:
Number
=partB.x-partA.x;
70
var
dy:
Number
=partB.y-partA.y;
71
var
dist:
Number
=Math.sqrt(dx*dx+dy*dy);
72
if
(dist<minDist) {
73
graphics.lineStyle(
1
,
0x00ff00
,
1
- dist / minDist);
//注意这里的透明度设置:二球越来越近时,线条越来越明显,距离越来越远时,线条越来越淡
74
graphics.moveTo(partA.x, partA.y);
75
graphics.lineTo(partB.x, partB.y);
76
//类似弹性运动处理
77
var
ax:
Number
=dx*springAmount;
78
var
ay:
Number
=dy*springAmount;
79
//A球加速
80
partA.vx+=ax/partA.mass;
81
partA.vy+=ay/partA.mass;
82
//B球减速
83
partB.vx-=ax/partB.mass;
84
partB.vy-=ay/partB.mass;
85
//一个球越来越快,一个球越来越慢,所以会不断拉近(当然:前提是在有效距离内)
86
}
87
}
88
89
init();
下面这种效果也是很多Flash网站上都有的,效果还不错,而且原理也很简单:
01
var
ballCount:
uint
=
100
;
02
var
friction:
Number
=
0.95
;
03
var
massRadio =
0.005
;
04
var
arrBall:
Array
=
new
Array
(ballCount);
05
var
stageWidth:
Number
= stage.stageWidth;
06
var
stageHeight:
Number
= stage.stageHeight;
07
08
for
(
var
i:
uint
=
0
;i<ballCount;i++){
09
arrBall[i] =
new
Ball(Math.random()*
10
+
3
,Math.random()*
0xffffff
);
10
arrBall[i].x = Math.random()*stageWidth;
11
arrBall[i].y = Math.random()*stageHeight;
12
arrBall[i].mass = massRadio * arrBall[i].radius;
13
addChild(arrBall[i]);
14
}
15
16
17
stage.addEventListener(Event.ENTER_FRAME,EnterFrameHandler);
18
stage.addEventListener(MouseEvent.MOUSE_MOVE,MouseMoveHandler);
19
20
function
EnterFrameHandler(e:Event):
void
{
21
for
(
var
i:
uint
=
0
;i<ballCount;i++){
22
var
b:Ball = arrBall[i];
23
b.vx *=friction;
24
b.vy *=friction;
25
b.x += b.vx;
26
b.y += b.vy;
27
28
//屏幕环绕处理
29
if
(b.x>stageWidth+b.radius){
30
b.x=-b.radius;
31
}
32
else
if
(b.x<-b.radius){
33
b.x = stageWidth+b.radius;
34
}
35
if
(b.y>stageHeight+b.radius){
36
b.y=-b.radius;
37
}
38
else
if
(b.y<-b.radius){
39
b.y = stageHeight+b.radius;
40
}
41
}
42
}
43
44
function
MouseMoveHandler(e:MouseEvent):
void
{
45
var
CenterX:
Number
=
0.5
*stageWidth;
46
var
CenterY:
Number
=
0.5
*stageHeight;
47
var
dx:
Number
= mouseX - CenterX;
48
var
dy:
Number
= mouseY - CenterY;
49
for
(
var
i:
uint
=
0
;i<ballCount;i++){
50
var
b:Ball = arrBall[i];
51
//设置速度
52
b.vx = -dx*b.mass;
53
b.vy = -dy*b.mass;
54
}
55
}
下面这个是它的变种: