zoukankan      html  css  js  c++  java
  • 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
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    encodeURI 编码、decodeURI 解码
    JavaScript toLocaleString() 方法
    element-ui el-upload限制图片格式尺寸及图片
    vue计算属性的使用
    element级联选择器表单回显
    关于css浮动需要注意的一点
    el-tree树形结构实现自定义关联
    element UI 表格中暂无数据自定义
    vue登录流程
    vue 中表单置空简单操作
  • 原文地址:https://www.cnblogs.com/yjmyzz/p/1723003.html
Copyright © 2011-2022 走看看