Flash/Flex学习笔记(44):万有引力与粒子系统
万有引用公式:
其中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 }
下面这个是它的变种:
浙公网安备 33010602011771号