zoukankan      html  css  js  c++  java
  • 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});
    

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

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

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

    作者:菩提树下的杨过
    出处:http://yjmyzz.cnblogs.com
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    Java学习个人备忘录之继承
    Java学习个人备忘录之文档注释
    Java学习个人备忘录之数组工具类
    Java学习个人备忘录之关键字static
    Java学习个人备忘录之构造函数&this
    Java学习个人备忘录之面向对象概念
    Java学习个人备忘录之数组
    Java学习个人备忘录之入门基础
    hdoj1162-Eddy's picture(kruskal)
    hdoj1102-Constructing Roads(Kruskal)
  • 原文地址:https://www.cnblogs.com/yjmyzz/p/1714664.html
Copyright © 2011-2022 走看看