Flash/Flex学习笔记(44):万有引力与粒子系统

简介: 万有引用公式: 其中G为万有引力常数   var numParticles:uint=50;//粒子总数 var G:Number=0.03;//万有引力常数 var particles:Array=new Array(numParticles); var bounce:Number=-0.

万有引用公式:

gif.latex?F=G%5Cfrac%7Bm_%7B1%7Dm_%7B2%7

其中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/Flex学习笔记(56):矩阵变换
先回顾一下Silvelright中的矩阵变换[转]WPF中的MatrixTransform,简单点讲:矩阵变换能改变对象的x,y坐标,x或y方向上的缩放,以及对象在x,y轴上的旋转(扭曲变形) 上面这个是WPF/Silverlight中的3*3变换矩阵,其中X,Y用于改变对象的坐标;M11,M22用于对象在x,y轴上的缩放;而M12,M21用于y轴,x轴上的扭曲。
846 0
|
内存技术
Flash/Flex学习笔记(57):实用技巧
布朗运动: varnumDots:uint=50; varfriction:Number=0.9; vardots:Array; varlife:uint=0; functioninit(){ graphics.
752 0
|
图形学 内存技术
Flash/Flex学习笔记(55):背面剔除与 3D 灯光
Animation in ActionScript3.0 这本书总算快学完了,今天继续:上一回Flash/Flex学习笔记(50):3D线条与填充 里,我们知道任何一个3D多面体上的某一个面,都可以分解为多个三角形的组合。
916 0
Flash/Flex学习笔记(54):迷你滚动条ScrollBar
先看最终效果: 整个swf最终不到4k,如果用系统的组件List来做的话,最终尺寸会接近30k ! (当然,核心代码是从网上收集到的:))   大致原理: 把要显示的对象上面加一层遮罩,然后根据滚动条的位置,上下移动显示对象。
931 0
|
Java 索引 内存技术
Flash/Flex学习笔记(49):3D基础
之前我们所做的动画都是基于x,y二维坐标轴的,在三维动画中我们还需要增加一个垂直于屏幕“向里”或“向外”的Z轴,那么z轴到底是应该向外,还是向里呢?这个其实无所谓,不过为了统一,习惯上通常把z轴约定为向里,即所谓的“右手坐标系” 右手坐标系的得名:伸出右手,让食指、中指、大拇指相互垂直;然后 食指指向x轴正向,中指指向y轴正向,则大拇指所指方向即为z轴正向。
830 0
|
索引 容器 内存技术
Flash/Flex学习笔记(52):使用TweenLite
TweenLite是第三方出品的专用于各种缓动动画的类库,其性能据说已经超过了Adobe官方的Tween. 从网上找到了一篇中文的说明文档:http://files.cnblogs.com/yjmyzz/tweenLite%e4%b8%ad%e6%96%87%e6%89%8b%e5%86%8c%e4%b8%8e%e5%8f%82%e6%95%b0%e8%af%b4%e6%98%8e.
901 0
|
Java Spring 内存技术
Flash/Flex学习笔记(41):碰撞检测
碰撞检测基本上可能分为二类:对象与对象的碰撞检测、对象与点的碰撞检测 为了方便测试,先写一个box类(生成一个小矩形) package { import flash.display.Sprite; public class Box extends Sprite { ...
848 0
|
内存技术 C# 开发工具
Flash/Flex学习笔记(38):缓动动画
缓动 与 匀变速 看上去很类似,但其实有区别: 匀变速的公式为 V = V0 + at --速度v与时间t是线性(正比)关系,而且这种运动不需要确定目标点,速度可以按照这种规律一直变下去 而缓动指的是物体越接近目标时速度越慢,速度跟距离成反比关系,用公式描述为 V = k S  (0
813 0
|
内存技术 数据格式 XML
Flash/Flex学习笔记(37):不用系统组件(纯AS3)的视频播放器--只有8.82K
以前为了赶项目,利用系统组件制作过一款视频播放器(见Flash/Flex学习笔记(6):制作基于xml数据源的flv视频播放器),但是系统组件实在是太大了,最终生成的swf居然有103K,随着AS3的深入学习,昨天又弄了一个只用AS3的播放器,最终只有8.82K,呵呵,这肥减得那是相当厉害。
1211 0