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