Flash/Flex学习笔记(44):万有引力与粒子系统
万有引用公式:
其中G为万有引力常数
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); }
代码虽然很长,但是其中有很多都是上一篇里封装好的方法直接复制过来的,应该不难理解
再来模拟一下地球绕着太阳转:
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一书的作者是这么叫的。
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):弹性运动续--弹簧
可以稍加改进,加入质量因素:
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网站上都有的,效果还不错,而且原理也很简单:
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; } }
下面这个是它的变种:
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
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。