209/092
AS3-多物体碰撞万有引力+连线+拖拽版
Demo如下,本文内容部分来自《Foundation Actionscript 3.0 Animation: Making Things Move!》:
基本上这次的内容就是前边几次的综合,不过加上了连线的部分。
直接上代码:
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Point;
public class NodesMass extends Sprite {
private var particles:Array;
//粒子数量
private var numParticles:uint = 15;
//两球间的画线距离设置,这里是整个Flash的宽度都画线
private var minDist:Number = stage.stageWidth;
//碰到墙壁后的反弹参数,负数是表示反向,正常情况下应该是小于0但是大于-1的值,设置成正数会有很有趣的效果出现
private var bounce:Number = -0.7;
//模拟中立的一个值,值越大吸引力越大
private var springAmount:Number = 0.001;
//画线用的容器
private var spLine:Sprite;
//当前拖动的Ball实例
private var currentParticles:Ball;
//当前拖动的Ball实例x位置
private var oldX:Number;
//当前拖动的Ball实例y位置
private var oldY:Number;
public function NodesMass () {
init ();
}
private function init ():void {
spLine=new Sprite();
addChild(spLine);
particles = new Array ;
for (var i:uint = 0; i < numParticles; i++) {
var size:Number = Math.random() * 5 + 10;
var color:uint= Math.random() * 0xffffff;
var particle:Ball = new Ball(size,color);
particle.x = Math.random() * stage.stageWidth;
particle.y = Math.random() * stage.stageHeight;
particle.vx = Math.random() * 2 - 1;
particle.vy = Math.random() * 2 - 1;
particle.mass = size;
addChild (particle);
particle.addEventListener (MouseEvent.MOUSE_DOWN,onMouseDownHandler);
particles.push (particle);
}
addEventListener (Event.ENTER_FRAME,onEnterFrame);
}
private function onMouseDownHandler (_e:MouseEvent):void {
currentParticles = _e.currentTarget as Ball;
currentParticles.vx = 0;
currentParticles.vy = 0;
currentParticles.alpha = 0.5;
stage.addEventListener (MouseEvent.MOUSE_UP,onMouseUpHandler);
currentParticles.startDrag ();
removeEventListener (Event.ENTER_FRAME, onEnterFrame);
addEventListener (Event.ENTER_FRAME, trackVelocity);
}
//计算抛掷速度向量
private function trackVelocity (_e:Event):void {
currentParticles.vx=currentParticles.x-oldX;
currentParticles.vy=currentParticles.y-oldY;
oldX=currentParticles.x;
oldY=currentParticles.y;
}
private function onMouseUpHandler (_e:MouseEvent):void {
stage.removeEventListener (MouseEvent.MOUSE_UP,onMouseUpHandler);
currentParticles.stopDrag ();
currentParticles.alpha=1;
removeEventListener (Event.ENTER_FRAME, trackVelocity);
addEventListener (Event.ENTER_FRAME, onEnterFrame);
}
private function onEnterFrame (event:Event):void {
for (var i:uint = 0; i < numParticles; i++) {
var particle:Ball = particles[i];
particle.x += particle.vx;
particle.y+=particle.vy;
checkWalls (particle);
}
//每次更新线条之前先clear掉原来的线条
spLine.graphics.clear();
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);
spring (partA,partB);
}
}
}
private function checkWalls (ball:Ball):void {
if (ball.x+ball.radius>stage.stageWidth) {
ball.x=stage.stageWidth-ball.radius;
ball.vx*=bounce;
} else if (ball.x - ball.radius < 0) {
ball.x=ball.radius;
ball.vx*=bounce;
}
if (ball.y+ball.radius>stage.stageHeight) {
ball.y=stage.stageHeight-ball.radius;
ball.vy*=bounce;
} else if (ball.y - ball.radius < 0) {
ball.y=ball.radius;
ball.vy*=bounce;
}
}
private function spring (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 = partA.mass * partB.mass / distSQ;
if (dist<minDist) {
spLine.graphics.lineStyle (1,0xffffff,1-dist/minDist);
spLine.graphics.moveTo (partA.x,partA.y);
spLine.graphics.lineTo (partB.x,partB.y);
var ax:Number=dx*springAmount;
var ay:Number=dy*springAmount;
partA.vx+=ax/partA.mass;
partA.vy+=ay/partA.mass;
partB.vx-=ax/partB.mass;
partB.vy-=ay/partB.mass;
}
}
private 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);
// 旋转 ball0 的位置
var pos0:Point=new Point(0,0);
// 旋转 ball1 的速度
var pos1:Point=rotate(dx,dy,sin,cos,true);
// 旋转 ball0 的速度
var vel0:Point=rotate(ball0.vx,ball0.vy,sin,cos,true);
// 旋转 ball1 的速度
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 absV:Number=Math.abs(vel0.x)+Math.abs(vel1.x);
var overlap:Number = (ball0.radius + ball1.radius) - Math.abs(pos0.x - pos1.x);
pos0.x+=vel0.x/absV*overlap;
pos1.x+=vel1.x/absV*overlap;
// 将位置旋转回来
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;
}
}
private 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;
}
}
}
spring函数里边加入了万有引力,恩,顺便讲下万有引力。
实际上万有引力的物理公式是:
force = G * m1 * m2 / (distance*distance)
用AS3来实现的话,可以用下边这个函数:
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 = partA.mass * partB.mass / distSQ;
var ax:Number = force * dx / dist;
var ay:Number = force * dy / dist;
partA.vx += ax / partA.mass;
partA.vy+=ay/partA.mass;
partB.vx-=ax/partB.mass;
partB.vy-=ay/partB.mass;
}
还有个简单版的:
function gravitate (partA:Ball,partB:Ball):void {
var ax:Number = dx * springAmount;
var ay:Number = dy * springAmount;
partA.vx += ax / partA.mass;
partA.vy+=ay/partA.mass;
partB.vx-=ax/partB.mass;
partB.vy-=ay/partB.mass;
}
去掉了烦琐的force计算,直接给定了一个值springAmount来代替引力。虽然不科学,不过还好,在实际应用中其实差别不大~
September 2nd, 2009 - 09:41
尝试过将球的数量设置成200,这样总的线条数应该是1+2+3+…+199,差不多是20000,FP会崩掉哈哈~效果不行。。
[Reply]
September 2nd, 2009 - 14:08
说的很好 很经典我顶你
[Reply]