Flash/Flex学习笔记(44):万有引力与粒子系统
万有引用公式:
其中G为万有引力常数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | var numParticles: uint = 50 ; //粒子总数 var G: Number = 0.03 ; //万有引力常数 var particles: Array = new Array (numParticles); var bounce: Number =- 0.4 ; //边界反弹系统 //初始化 function init(): void { particles = new Array (); for ( var i: uint = 0 ; i < numParticles; i++) { var size: Number =Math.random()* 12 + 3 ; var particle:Ball= new Ball(size,Math.random()* 0xffffff ); particle.x=Math.random()*stage.stageWidth; particle.y=Math.random()*stage.stageHeight; particle.mass=Math.PI * size * size; //质量与球截面积关联,即从视觉效果上看,个头越大,越重 addChild(particle); particles.push(particle); } addEventListener(Event.ENTER_FRAME, EnterFrameHandler); } function EnterFrameHandler(event:Event): void { for ( var i: uint = 0 ; i < numParticles; i++) { var particle:Ball=particles[i]; particle.x+=particle.vx; particle.y+=particle.vy; } for (i= 0 ; i < numParticles - 1 ; i++) { var partA:Ball=particles[i]; for ( var j: uint = i + 1 ; j < numParticles; j++) { var partB:Ball=particles[j]; checkCollision(partA,partB); //检测碰撞 gravitate(partA, partB); //万有引力处理 } checkWalls(partA); //边界检测 } } //万有引力处理 function gravitate(partA:Ball, partB:Ball): void { var dx: Number =partB.x-partA.x; var dy: Number =partB.y-partA.y; var distSQ: Number =dx*dx+dy*dy; var dist: Number =Math.sqrt(distSQ); var force: Number =G*partA.mass*partB.mass/distSQ; //计算partA与partB的万有引力 var forceX: Number =force*dx/dist; //即:force * cos(a) --万有引力在x方向上的分量 var forceY: Number =force*dy/dist; //即:force * sin(a) --万有引力在y方向上的分量 partA.vx+=forceX/partA.mass; //牛顿定律a = F/m 在这里得到体现 partA.vy+=forceY/partA.mass; partB.vx-=forceX/partB.mass; partB.vy-=forceY/partB.mass; } //动量守恒的碰撞检测 function checkCollision(ball0:Ball, ball1:Ball): void { var dx: Number =ball1.x-ball0.x; var dy: Number =ball1.y-ball0.y; var dist: Number =Math.sqrt(dx*dx+dy*dy); if (dist<ball0.radius+ball1.radius) { var angle: Number =Math.atan2(dy,dx); var sin: Number =Math.sin(angle); var cos: Number =Math.cos(angle); var pos0:Point= new Point( 0 , 0 ); var pos1:Point=rotate(dx,dy,sin,cos, true ); var vel0:Point=rotate(ball0.vx,ball0.vy,sin,cos, true ); var vel1:Point=rotate(ball1.vx,ball1.vy,sin,cos, true ); var vxTotal: Number =vel0.x-vel1.x; vel0.x = ((ball0.mass - ball1.mass) * vel0.x + 2 * ball1.mass * vel1.x) / (ball0.mass + ball1.mass); vel1.x=vxTotal+vel0.x; var sumRadius: Number =ball0.radius+ball1.radius; var overlap: Number =sumRadius-Math.abs(pos0.x-pos1.x); var aRadio: Number =ball0.radius/sumRadius; var bRadio: Number =ball1.radius/sumRadius; if (overlap> 0 ) { if (pos0.x>pos1.x) { pos0.x+=overlap*aRadio; pos1.x-=overlap*bRadio; } else { pos0.x-=overlap*aRadio; pos1.x+=overlap*bRadio; } } var pos0F: Object =rotate(pos0.x,pos0.y,sin,cos, false ); var pos1F: Object =rotate(pos1.x,pos1.y,sin,cos, false ); ball1.x=ball0.x+pos1F.x; ball1.y=ball0.y+pos1F.y; ball0.x=ball0.x+pos0F.x; ball0.y=ball0.y+pos0F.y; var vel0F: Object =rotate(vel0.x,vel0.y,sin,cos, false ); var vel1F: Object =rotate(vel1.x,vel1.y,sin,cos, false ); ball0.vx=vel0F.x; ball0.vy=vel0F.y; ball1.vx=vel1F.x; ball1.vy=vel1F.y; } } //坐标旋转辅助方法 function rotate(x: Number , y: Number , sin: Number , cos: Number , reverse: Boolean ):Point { var result:Point = new Point(); if (reverse) { result.x=x*cos+y*sin; result.y=y*cos-x*sin; } else { result.x=x*cos-y*sin; result.y=y*cos+x*sin; } return result; } //舞台边界检测 function checkWalls(b:Ball) { if (b.x<b.radius) { b.x=b.radius; b.vx*=bounce; } else if (b.x>stage.stageWidth-b.radius) { b.x=stage.stageWidth-b.radius; b.vx*=bounce; } if (b.y<b.radius) { b.y=b.radius; b.vy*=bounce; } else if (b.y>stage.stageHeight-b.radius) { b.y=stage.stageHeight-b.radius; b.vy*=bounce; } } init(); btnReset.addEventListener(MouseEvent.MOUSE_DOWN,MouseDownHandler); function MouseDownHandler(e:MouseEvent): void { removeEventListener(Event.ENTER_FRAME, EnterFrameHandler); for ( var i: uint = 0 ; i < numParticles; i++) { var particle:Ball=particles[i]; particle.x=Math.random()*stage.stageWidth; particle.y=Math.random()*stage.stageHeight; particle.vx= 0 ; particle.vy= 0 ; } addEventListener(Event.ENTER_FRAME, EnterFrameHandler); } |
代码虽然很长,但是其中有很多都是上一篇里封装好的方法直接复制过来的,应该不难理解
再来模拟一下地球绕着太阳转:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | var numParticles: uint = 2 ; //粒子总数 var G: Number = 0.03 ; //万有引力常数 var particles: Array = new Array (numParticles); var i: Number = 0 ; //初始化 function init(): void { particles = new Array (); var sun:Ball = new Ball( 30 , 0xff0000 ); sun.x = stage.stageWidth / 2 ; sun.y = stage.stageHeight / 2 ; sun.mass = 900000 ; addChild(sun); particles.push(sun); var planet:Ball = new Ball( 10 , 0x0000ff ); planet.x = stage.stageWidth / 2 + 200 ; planet.y = stage.stageHeight / 2 ; planet.vy = 8 ; planet.mass = 1 ; addChild(planet); particles.push(planet); addEventListener(Event.ENTER_FRAME, EnterFrameHandler); graphics.lineStyle( 1 , 0xdddddd ); graphics.moveTo(planet.x,planet.y); } function EnterFrameHandler(event:Event): void { for ( var i: uint = 0 ; i < numParticles; i++) { var particle:Ball=particles[i]; particle.x+=particle.vx; particle.y+=particle.vy; } for (i= 0 ; i < numParticles - 1 ; i++) { var partA:Ball=particles[i]; for ( var j: uint = i + 1 ; j < numParticles; j++) { var partB:Ball=particles[j]; gravitate(partA, partB); //万有引力处理 } } } //万有引力处理 function gravitate(partA:Ball, partB:Ball): void { var dx: Number =partB.x-partA.x; var dy: Number =partB.y-partA.y; var distSQ: Number =dx*dx+dy*dy; var dist: Number =Math.sqrt(distSQ); var force: Number =G*partA.mass*partB.mass/distSQ; //计算partA与partB的万有引力 var forceX: Number =force*dx/dist; //即:force * cos(a) --万有引力在x方向上的分量 var forceY: Number =force*dy/dist; //即:force * sin(a) --万有引力在y方向上的分量 /* partA.vx+=forceX/partA.mass;//牛顿定律a = F/m 在这里得到体现 partA.vy+=forceY/partA.mass; */ partB.vx-=forceX/partB.mass; partB.vy-=forceY/partB.mass; trace (i); if (i<= 1000 ){ graphics.lineTo(partB.x,partB.y); i++; } else { graphics.clear(); graphics.lineStyle( 1 , 0xdddddd ); graphics.moveTo(partB.x,partB.y); i= 0 ; } } init(); |
代码就是在第一段的基础上修改的,可以看到在"远日点"速度较慢(因为距离越远,万有引力越小,对应的加速度也较小),在"近日点"速度较快(距离越近,万有引力越大,对应的加速度也较大)
节点花园NodeGarden:
为啥叫这个名字,我也说不上来,反正ActionScript3.0 in Animation一书的作者是这么叫的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | var particles: Array ; var numParticles: uint = 60 ; var minDist: Number = 100 ; var springAmount: Number = 0.0004 ; var friction: Number = 0.9995 ; function init(): void { stage.scaleMode=StageScaleMode.NO_SCALE; stage.align=StageAlign.TOP_LEFT; particles = new Array (); for ( var i: uint = 0 ; i < numParticles; i++) { var particle:Ball= new Ball(Math.random()* 3 + 2 , 0xffffff ); particle.x=Math.random()*stage.stageWidth; particle.y=Math.random()*stage.stageHeight; particle.vx=Math.random()* 6 - 3 ; particle.vy=Math.random()* 6 - 3 ; addChild(particle); particles.push(particle); } addEventListener(Event.ENTER_FRAME, EnterFrameHandler); } function EnterFrameHandler(event:Event): void { graphics.clear(); for ( var i: uint = 0 ; i < numParticles; i++) { var particle:Ball=particles[i]; particle.x+=particle.vx; particle.y+=particle.vy; //屏幕环绕处理 if (particle.x>stage.stageWidth) { particle.x= 0 ; } else if (particle.x < 0 ) { particle.x=stage.stageWidth; } if (particle.y>stage.stageHeight) { particle.y= 0 ; } else if (particle.y < 0 ) { particle.y=stage.stageHeight; } } for (i= 0 ; i < numParticles - 1 ; i++) { var partA:Ball=particles[i]; for ( var j: uint = i + 1 ; j < numParticles; j++) { var partB:Ball=particles[j]; spring(partA, partB); //每个粒子均与其它粒子进行弹性运动处理 } partA.vx *= friction; partA.vy *= friction; } } function spring(partA:Ball, partB:Ball): void { var dx: Number =partB.x-partA.x; var dy: Number =partB.y-partA.y; var dist: Number =Math.sqrt(dx*dx+dy*dy); if (dist<minDist) { graphics.lineStyle( 1 , 0x00ff00 , 1 - dist / minDist); //注意这里的透明度设置:二球越来越近时,线条越来越明显,距离越来越远时,线条越来越淡 graphics.moveTo(partA.x, partA.y); graphics.lineTo(partB.x, partB.y); //类似弹性运动处理 var ax: Number =dx*springAmount; var ay: Number =dy*springAmount; //A球加速 partA.vx+=ax; partA.vy+=ay; //B球减速 partB.vx-=ax; partB.vy-=ay; //一个球越来越快,一个球越来越慢,所以会不断拉近(当然:前提是在有效距离内) } } init(); |
关于这个效果,建议初次接触的同学们,先回顾一下弹性运动:Flash/Flex学习笔记(40):弹性运动续--弹簧
可以稍加改进,加入质量因素:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | var particles: Array ; var numParticles: uint = 30 ; var minDist: Number = 120 ; var springAmount: Number = 0.03 ; var friction: Number = 0.998 ; var stageHeight: Number = stage.stageHeight; var stageWidth: Number = stage.stageWidth; function init(): void { stage.scaleMode=StageScaleMode.NO_SCALE; stage.align=StageAlign.TOP_LEFT; particles = new Array (); for ( var i: uint = 0 ; i < numParticles; i++) { var particle:Ball= new Ball(Math.random()* 5 + 2 , 0xffffff ); particle.x=Math.random()*stageWidth; particle.y=Math.random()*stageHeight; particle.vx=Math.random()* 6 - 3 ; particle.vy=Math.random()* 6 - 3 ; particle.mass = Math.PI*particle.radius*particle.radius; //加入质量 addChild(particle); particles.push(particle); } addEventListener(Event.ENTER_FRAME, EnterFrameHandler); stage.addEventListener(MouseEvent.MOUSE_MOVE,MouseMoveHandler); } //鼠标互动 function MouseMoveHandler(e:MouseEvent): void { var dx: Number = mouseX - stageWidth/ 2 ; var dy: Number = mouseY - stageHeight/ 2 ; for ( var i: uint = 0 ; i < numParticles; i++) { var b:Ball=particles[i]; b.x -= dx/b.mass; b.y -= dy/b.mass; } } function EnterFrameHandler(e:Event): void { graphics.clear(); for ( var i: uint = 0 ; i < numParticles; i++) { var particle:Ball=particles[i]; particle.x+=particle.vx; particle.y+=particle.vy; //屏幕环绕处理 if (particle.x>stageWidth) { particle.x= 0 ; } else if (particle.x < 0 ) { particle.x=stageWidth; } if (particle.y>stageHeight) { particle.y= 0 ; } else if (particle.y < 0 ) { particle.y=stageHeight; } } for (i= 0 ; i < numParticles - 1 ; i++) { var partA:Ball=particles[i]; for ( var j: uint = i + 1 ; j < numParticles; j++) { var partB:Ball=particles[j]; spring(partA, partB); //每个粒子均与其它粒子进行弹性运动处理 } partA.vx *= friction; partA.vy *= friction; } } function spring(partA:Ball, partB:Ball): void { var dx: Number =partB.x-partA.x; var dy: Number =partB.y-partA.y; var dist: Number =Math.sqrt(dx*dx+dy*dy); if (dist<minDist) { graphics.lineStyle( 1 , 0x00ff00 , 1 - dist / minDist); //注意这里的透明度设置:二球越来越近时,线条越来越明显,距离越来越远时,线条越来越淡 graphics.moveTo(partA.x, partA.y); graphics.lineTo(partB.x, partB.y); //类似弹性运动处理 var ax: Number =dx*springAmount; var ay: Number =dy*springAmount; //A球加速 partA.vx+=ax/partA.mass; partA.vy+=ay/partA.mass; //B球减速 partB.vx-=ax/partB.mass; partB.vy-=ay/partB.mass; //一个球越来越快,一个球越来越慢,所以会不断拉近(当然:前提是在有效距离内) } } init(); |
下面这种效果也是很多Flash网站上都有的,效果还不错,而且原理也很简单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | var ballCount: uint = 100 ; var friction: Number = 0.95 ; var massRadio = 0.005 ; var arrBall: Array = new Array (ballCount); var stageWidth: Number = stage.stageWidth; var stageHeight: Number = stage.stageHeight; for ( var i: uint = 0 ;i<ballCount;i++){ arrBall[i] = new Ball(Math.random()* 10 + 3 ,Math.random()* 0xffffff ); arrBall[i].x = Math.random()*stageWidth; arrBall[i].y = Math.random()*stageHeight; arrBall[i].mass = massRadio * arrBall[i].radius; addChild(arrBall[i]); } stage.addEventListener(Event.ENTER_FRAME,EnterFrameHandler); stage.addEventListener(MouseEvent.MOUSE_MOVE,MouseMoveHandler); function EnterFrameHandler(e:Event): void { for ( var i: uint = 0 ;i<ballCount;i++){ var b:Ball = arrBall[i]; b.vx *=friction; b.vy *=friction; b.x += b.vx; b.y += b.vy; //屏幕环绕处理 if (b.x>stageWidth+b.radius){ b.x=-b.radius; } else if (b.x<-b.radius){ b.x = stageWidth+b.radius; } if (b.y>stageHeight+b.radius){ b.y=-b.radius; } else if (b.y<-b.radius){ b.y = stageHeight+b.radius; } } } function MouseMoveHandler(e:MouseEvent): void { var CenterX: Number = 0.5 *stageWidth; var CenterY: Number = 0.5 *stageHeight; var dx: Number = mouseX - CenterX; var dy: Number = mouseY - CenterY; for ( var i: uint = 0 ;i<ballCount;i++){ var b:Ball = arrBall[i]; //设置速度 b.vx = -dx*b.mass; b.vy = -dy*b.mass; } } |
下面这个是它的变种:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | var ballCount: uint = 200 ; var friction: Number = 0.95 ; var massRadio= 0.1 ; var arrBall: Array = new Array (ballCount); var stageWidth: Number =stage.stageWidth; var stageHeight: Number =stage.stageHeight; var _oldX: Number ,_oldY: Number ; var _frameCount = 0 ; for ( var i: uint = 0 ; i<ballCount; i++) { arrBall[i]= new Ball(Math.random()* 10 + 2 ,Math.random()* 0xffffff ); arrBall[i].x=Math.random()*stageWidth; arrBall[i].y=Math.random()*stageHeight; arrBall[i].mass=massRadio*arrBall[i].radius; addChild(arrBall[i]); } stage.addEventListener(Event.ENTER_FRAME,EnterFrameHandler); function EnterFrameHandler(e:Event): void { var dx: Number =mouseX-_oldX; var dy: Number =mouseY-_oldY; for (i= 0 ; i<ballCount; i++) { var b:Ball=arrBall[i]; if (Math.abs(dx)> 0 ) { b.vx=- dx*b.mass; } if (Math.abs(dy)> 0 ) { b.vy=- dy*b.mass; } b.vx*=friction; b.vy*=friction; b.x+=b.vx; b.y+=b.vy; //屏幕环绕处理 if (b.x>stageWidth+b.radius) { b.x=- b.radius; } else if (b.x < -b.radius) { b.x=stageWidth+b.radius; } if (b.y>stageHeight+b.radius) { b.y=- b.radius; } else if (b.y < -b.radius) { b.y=stageHeight+b.radius; } } trace (_frameCount); //每2帧记录一下"速度" if (_frameCount>= 2 ){ _frameCount = 0 ; _oldX=mouseX; _oldY=mouseY; } else { _frameCount ++; } } |
作者:菩提树下的杨过
出处:http://yjmyzz.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
出处:http://yjmyzz.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步