Flash/Flex学习笔记(40):弹性运动续--弹簧

简介: 上一篇里演示的弹性运动加上摩擦力因素后,物体最终基本上都会比较准确的停在目标位置。但是我们回想一下现实世界中的弹簧,如果把弹簧的一头固定起来(即相当于目标点),而另一端栓一个球,把球拉开或压缩一定距离然后松手,事实上小球永远也不可能到达弹簧固定的那一端(因为弹簧即使压缩到最紧,也总有一定的长度) ...

上一篇里演示的弹性运动加上摩擦力因素后,物体最终基本上都会比较准确的停在目标位置。但是我们回想一下现实世界中的弹簧,如果把弹簧的一头固定起来(即相当于目标点),而另一端栓一个球,把球拉开或压缩一定距离然后松手,事实上小球永远也不可能到达弹簧固定的那一端(因为弹簧即使压缩到最紧,也总有一定的长度)

所以如果要在Flash里模拟现实中的弹簧,真正的目标点绝不是弹簧的端点,而是目标点再偏移一段距离(即弹簧自然伸展时的长度)

var ball:Ball = new Ball(6);
addChild(ball);
ball.y = 20;
ball.x = 20;

var targetX:Number=stage.stageWidth/2;
var targetY:Number=ball.y;

var springLength = 100;//弹簧长度
var spring = 0.2;//弹性系数
var friction = 0.92;//摩擦系数

//画辅助线,以便看得更清楚
graphics.lineStyle(0.5,0xaaaaaa);
graphics.moveTo(ball.x,ball.y);
graphics.lineTo(stage.stageWidth-ball.x,ball.y);
graphics.moveTo(targetX,targetY-10);
graphics.lineTo(targetX,targetY+10);
graphics.moveTo(targetX-springLength,targetY-8);
graphics.lineTo(targetX-springLength,targetY+8);

var rect:Rectangle = new Rectangle(ball.x,ball.y,stage.stageWidth-ball.x*2,0);

addEventListener(Event.ENTER_FRAME,EnterFrameHandler);
ball.addEventListener(MouseEvent.MOUSE_DOWN,MouseDownHandler);
stage.addEventListener(MouseEvent.MOUSE_UP,MouseUpHandler);

function EnterFrameHandler(e:Event):void{
	ball.vx += (targetX - springLength - ball.x)*spring;
	ball.vx *= friction;
	ball.x += ball.vx;
}

function MouseDownHandler(e:MouseEvent):void{
	(e.target as Sprite).startDrag(true,rect);
	removeEventListener(Event.ENTER_FRAME,EnterFrameHandler);
}

function MouseUpHandler(e:MouseEvent):void{
	ball.stopDrag();
	addEventListener(Event.ENTER_FRAME,EnterFrameHandler);
}

ball.addEventListener(MouseEvent.MOUSE_OUT,function(){Mouse.cursor = MouseCursor.AUTO});
ball.addEventListener(MouseEvent.MOUSE_OVER,function(){Mouse.cursor = MouseCursor.HAND});

如果考虑到二维坐标的弹簧运动,要稍微复杂一点:

img_c59da255ea47552b0f505e2abab8c7ef.jpg

var ball:Ball = new Ball(10);
addChild(ball);
ball.y = 20;
ball.x = 20;

var targetX:Number=stage.stageWidth/2;
var targetY:Number=stage.stageHeight/2;

var springLength:uint = 100;//弹簧长度
var spring:Number = 0.2;//弹性系数
var friction:Number = 0.92;//摩擦系数
var angle:Number = 0;

addEventListener(Event.ENTER_FRAME,EnterFrameHandler);
ball.addEventListener(MouseEvent.MOUSE_DOWN,MouseDownHandler);
stage.addEventListener(MouseEvent.MOUSE_UP,MouseUpHandler);
stage.addEventListener(MouseEvent.MOUSE_MOVE,function(){DrawLine()});

angle = Math.atan2(targetY - ball.y,targetX -ball.x);//确定夹角
trace(angle*180/Math.PI);

function EnterFrameHandler(e:Event):void{
	ball.vx += (targetX - springLength*Math.cos(angle) - ball.x)*spring;//调整目标点
	ball.vy += (targetY - springLength*Math.sin(angle) - ball.y)*spring;
	ball.vx *= friction;
	ball.vy *= friction;
	ball.x += ball.vx;
	ball.y += ball.vy;
	DrawLine();
}

function DrawLine():void{
	graphics.clear();
	graphics.lineStyle(1);
	graphics.moveTo(targetX,targetY-10);
	graphics.lineTo(targetX,targetY+10);
	graphics.moveTo(targetX-10,targetY);
	graphics.lineTo(targetX+10,targetY);
	graphics.moveTo(targetX,targetY);
	graphics.lineStyle(0.5,0xaaaaaa);
	graphics.lineTo(ball.x,ball.y);
}

function MouseDownHandler(e:MouseEvent):void{
	(e.target as Sprite).startDrag(true);
	removeEventListener(Event.ENTER_FRAME,EnterFrameHandler);
}

function MouseUpHandler(e:MouseEvent):void{
	ball.stopDrag();
	addEventListener(Event.ENTER_FRAME,EnterFrameHandler);
}

ball.addEventListener(MouseEvent.MOUSE_OUT,function(){Mouse.cursor = MouseCursor.AUTO});
ball.addEventListener(MouseEvent.MOUSE_OVER,function(){Mouse.cursor = MouseCursor.HAND});

上面的例子中,移动的方向(即夹角)与目标点都是固定的,如果改成动态的(比如鼠标当前所在位置),效果可能更逼真

function EnterFrameHandler(e:Event):void{
	targetX = mouseX;//改成动态目标
	targetY = mouseY;
	angle = Math.atan2(targetY - ball.y,targetX -ball.x);//动态夹角
	ball.vx += (targetX - springLength*Math.cos(angle) - ball.x)*spring;
	ball.vy += (targetY - springLength*Math.sin(angle) - ball.y)*spring;
	ball.vx *= friction;
	ball.vy *= friction;
	ball.x += ball.vx;
	ball.y += ball.vy;
	DrawLine();
}

如果二个物体相互以对方所在位置为目标做弹性运动,同时再考虑弹簧长度,边界检测等因素,可以用AS3模拟出一个极逼真的弹簧模型:

var ball_1:Ball = new Ball(10,0xff0000);
var ball_2:Ball = new Ball(10,0x0000ff);
ball_1.x = stage.stageWidth * Math.random();
ball_1.y = stage.stageHeight * Math.random();
ball_2.x = stage.stageWidth/2;
ball_2.y = stage.stageHeight/2;

addChild(ball_1);
addChild(ball_2);

var spring:Number = 0.1;
var springLength:uint = 100;
var friction:Number = 0.9;
var darggingBall:Ball;

addEventListener(Event.ENTER_FRAME,EnterFrameHandler);
ball_1.addEventListener(MouseEvent.MOUSE_DOWN,MouseDownHandler);
ball_2.addEventListener(MouseEvent.MOUSE_DOWN,MouseDownHandler);
stage.addEventListener(MouseEvent.MOUSE_UP,MouseUpHandler);
stage.addEventListener(MouseEvent.MOUSE_MOVE,function(){DrawLine();});
ball_1.addEventListener(MouseEvent.MOUSE_OVER,MouseOverHandler);
ball_1.addEventListener(MouseEvent.MOUSE_OUT,MouseOutHandler);
ball_2.addEventListener(MouseEvent.MOUSE_OVER,MouseOverHandler);
ball_2.addEventListener(MouseEvent.MOUSE_OUT,MouseOutHandler);

function MouseOutHandler(e:MouseEvent){
	Mouse.cursor = MouseCursor.AUTO;
}

function MouseOverHandler(e:MouseEvent){
	Mouse.cursor = MouseCursor.HAND;
}

function MouseDownHandler(e:MouseEvent):void{
	(e.target as Sprite).startDrag(true,new Rectangle(20,20,stage.stageWidth-40,stage.stageHeight-40));
	darggingBall = e.target as Ball;
	removeEventListener(Event.ENTER_FRAME,EnterFrameHandler);
}

function MouseUpHandler(e:MouseEvent):void{
	if (darggingBall!=null){
		darggingBall.stopDrag();
		darggingBall = null;
		addEventListener(Event.ENTER_FRAME,EnterFrameHandler);
	}	
}

function EnterFrameHandler(e:Event):void{
	var dx1 = ball_2.x -ball_1.x;
	var dy1 = ball_2.y -ball_1.y;	
	var angle1:Number = Math.atan2(dy1,dx1);	
	ball_1.vx += (ball_2.x - springLength * Math.cos(angle1) - ball_1.x) * spring;
	ball_1.vy += (ball_2.y - springLength * Math.sin(angle1) - ball_1.y) * spring;	
	ball_1.vx *= friction;
	ball_1.vy *= friction;	
	ball_1.x += ball_1.vx;
	ball_1.y += ball_1.vy;
	
	
	var dx2 = ball_1.x -ball_2.x;
	var dy2 = ball_1.y -ball_2.y;	
	var angle2:Number = Math.atan2(dy2,dx2);	
	ball_2.vx += (ball_1.x - springLength * Math.cos(angle2) - ball_2.x) * spring;
	ball_2.vy += (ball_1.y - springLength * Math.sin(angle2) - ball_2.y) * spring;	
	ball_2.vx *= friction;
	ball_2.vy *= friction;	
	ball_2.x += ball_2.vx;
	ball_2.y += ball_2.vy;
	
	DrawLine();
	
	CheckBoundary(ball_1);
	CheckBoundary(ball_2);
	
	
}

function DrawLine():void{
	graphics.clear();
	graphics.lineStyle(0.5,0x666666);
	graphics.moveTo(ball_1.x,ball_1.y);
	graphics.lineTo(ball_2.x,ball_2.y);
}

function CheckBoundary(b:Ball){
	if (b.x>stage.stageWidth-b.width/2 || b.x<=b.width/2){
		b.x -= b.vx;		
		b.vx *= -1;
	}
	
		if (b.y>stage.stageHeight-b.height/2 || b.y<=b.height/2){
		b.y -= b.vy;
		b.vy *= -1;
	}
}

如果玩得再疯狂一点,多放一些小球,让第二个以第一个为目标,第三个以第二个为目标...最后一个再以第一个为目标,这样构成一个环,大概就是下面这个样子:

var spring:Number=0.1;
var springLength:uint=150;
var friction:Number=0.8;//摩擦力
var darggingBall:Ball;
var ballNumber:uint = 3;//小球个数

var arrBalls:Array = new Array(ballNumber);

for(var i:uint=0,j=arrBalls.length;i<j;i++){
	arrBalls[i] = new Ball(20,Math.random() * 0xffffff);
	var _ball:Ball = arrBalls[i];
	_ball.x=stage.stageWidth*Math.random();
	_ball.y=stage.stageHeight*Math.random();
	addChild(_ball);
	_ball.addEventListener(MouseEvent.MOUSE_OVER,MouseOverHandler);
	_ball.addEventListener(MouseEvent.MOUSE_OUT,MouseOutHandler);
	_ball.addEventListener(MouseEvent.MOUSE_DOWN,MouseDownHandler);
}

addEventListener(Event.ENTER_FRAME,EnterFrameHandler);


stage.addEventListener(MouseEvent.MOUSE_UP,MouseUpHandler);
stage.addEventListener(MouseEvent.MOUSE_MOVE,function(){DrawLine();});


//切换光标
function MouseOutHandler(e:MouseEvent) {
	Mouse.cursor=MouseCursor.AUTO;
}

//切换光标
function MouseOverHandler(e:MouseEvent) {
	Mouse.cursor=MouseCursor.HAND;
}

//开始拖动
function MouseDownHandler(e:MouseEvent):void {
	(e.target as Sprite).startDrag(true,new Rectangle(20,20,stage.stageWidth-40,stage.stageHeight-40));
	darggingBall=e.target as Ball;
	removeEventListener(Event.ENTER_FRAME,EnterFrameHandler);
}

//结束拖动
function MouseUpHandler(e:MouseEvent):void {
	if (darggingBall!=null) {
		darggingBall.stopDrag();
		darggingBall=null;
		addEventListener(Event.ENTER_FRAME,EnterFrameHandler);
	}
}

function EnterFrameHandler(e:Event):void {
	
	for(var i:uint=0,j=arrBalls.length-1;i<j;i++){
		SpringTo(arrBalls[i],arrBalls[i+1]);		
	}
	SpringTo(arrBalls[arrBalls.length-1],arrBalls[0]);
	
	DrawLine();	
	
	for(i=0,j=arrBalls.length;i<j;i++){
		CheckBoundary(arrBalls[i]);
	}


}


//画连接线
function DrawLine():void {
	graphics.clear();
	graphics.lineStyle(0.5,0x666666);
	//graphics.moveTo(ball_1.x,ball_1.y);
	//graphics.lineTo(ball_2.x,ball_2.y);
	
	for(var i:uint=0,j=arrBalls.length-1;i<j;i++){
		graphics.moveTo(arrBalls[i].x,arrBalls[i].y);
		graphics.lineTo(arrBalls[i+1].x,arrBalls[i+1].y);
	}
	graphics.lineTo(arrBalls[0].x,arrBalls[0].y);
}

//弹性运动处理
function SpringTo(targetBall:Ball,moveBall:Ball):void{
	var dy=targetBall.y-moveBall.y;
	var dx=targetBall.x-moveBall.x;	
	var angle1:Number=Math.atan2(dy,dx);
	moveBall.vx += (targetBall.x - springLength * Math.cos(angle1) - moveBall.x) * spring;
	moveBall.vy += (targetBall.y - springLength * Math.sin(angle1) - moveBall.y) * spring;
	moveBall.vx *= friction;
	moveBall.vy *= friction;
	moveBall.x += moveBall.vx;
	moveBall.y += moveBall.vy;
}

//检测边界
function CheckBoundary(b:Ball) {
	if (b.x>stage.stageWidth-b.width/2||b.x<=b.width/2) {
		b.x-=b.vx;
		b.vx*=-1;
	}

	if (b.y>stage.stageHeight-b.height/2||b.y<=b.height/2) {
		b.y-=b.vy;
		b.vy*=-1;
	}
}

思考一下:这样为啥不会造成死循环?

目录
相关文章
Core Animation - 发光的太阳(附高效设置图片圆角和变圆的方法)
Core Animation - 发光的太阳(附高效设置图片圆角和变圆的方法)
56 0
Core Animation - 发光的太阳(附高效设置图片圆角和变圆的方法)
|
图形学
Unity 3D光源-Spot Light聚光灯用法详解、模拟手电筒、台灯等线性教程
Unity4大光源之聚光灯 本文提供全流程,中文翻译。 Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 —— 高分辨率用户请根据需求调整网页缩放比例) Chinar —— 心...
3533 0
|
传感器
camera摄像原理之三:色温和自动白平衡【转】
转自:http://blog.csdn.net/ghostyu/article/details/7912863 色温的定义:将黑体从绝对零度开始加温,温度每升高一度称为1开氏度(用字母K表示),当温度升高到一定程度时候,黑体便辐射出可见光,其光谱成份以及给人的感觉也会着温度的不断升高发生相应变化。
1088 0
|
定位技术
Flex:地图缩放平移效果(简易版)
先看效果:(这个只是原型,简陋了点) 功能: 双击图片或拖动右侧的滑块可实现图片的放大,鼠标按下不放可平移图片。 应用场景:目前各地铁、医院、大型商场、机场均有单点触摸查询屏,用这个功能可以实现用户的简单自助地图导航。
966 0
|
图形学 内存技术
Flash/Flex学习笔记(55):背面剔除与 3D 灯光
Animation in ActionScript3.0 这本书总算快学完了,今天继续:上一回Flash/Flex学习笔记(50):3D线条与填充 里,我们知道任何一个3D多面体上的某一个面,都可以分解为多个三角形的组合。
916 0
|
内存技术
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学习笔记(51):3维旋转与透视变换(PerspectiveProjection)
Flash/Flex学习笔记(49):3D基础 里已经介绍了3D透视的基本原理,不过如果每次都要利用象该文中那样写一堆代码,估计很多人不喜欢,事实上AS3的DisplayObject类已经内置了z坐标、rotationX、rotationY、rotationZ属性,再加上PerspectiveProjection类用于处理透视转换,基本上可以满足大多数的3D要求。
1020 0
|
Java Spring 内存技术
Flash/Flex学习笔记(39):弹性运动
动画中的弹性运动 从视觉效果上接近 物理经典力学中的单摆运动或弹簧(胡克定律F=Kx)振动 先看下面的模拟演示: 规律:小球先从出发点(初始为最左侧)向目标点(中心点)加速狂奔,奔的过程中速度越来越大,但加速度越来越小,等经过目标点时,发现速度太大刹不住车(此时速度达到最大值,...
601 0