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 ++;

	}

	

}

你可能感兴趣的:(Flash)