Flash AS3 边界(移除对象、重置对象:喷泉效果、环绕效果、反弹机制)与摩擦力详解(转)

影片通过施加外力使影片运动起来。然而,物体移动到屏幕外后就看不到了。如果在某个角度上运动得过快,那么就没有

办法再让物体退回来,只能选择重新运行影片。
另一个常被忽略的问题是,所处的环境如何改变物体的运动。惯性一词是用来形容物体在空间中穿梭,并保持以同样的方向及速度运动,只有对其施加外力,才会使它的运动发生改变。
环境边界中的边界指为这项活动保留的活动空间。意思是“我只关心发生在这个范围内的事情,如果超出了这个范围,就

不再关注它了。”
只要物体是运动的,那么它就有机会离开这个范围。当物体离开后,我们可以选择忘记它,或将它移动回来,或跟随它。

设置边界
通常,边界就是一个矩形。然后判断所有移动的对象,看它们是否仍在这个空间内,这里可以使用if和else语句判断边界,如果对象X坐标大于右边界,就意味着它超出了右边界。但不可能同时超出左边界,所以不需要再用一条if语句进行判断。因此,只需要在第一个if语句失败后再判断左边边界即可,顶部和底部也是如此。然而,物体有可能在X,Y轴上同时超出边界,所以要氢这

两个判断语句分开。示例如下:
if(objcet.x>stage.stageWidth){
//do something
}else if(object.x<0){
//do something
}
if(object.y>stage.stageHeight){
//do something
}else if(object.y<0){
//do something
}
如果对象出界后,可以执行如下四种操作:
   将对象移除;
   重置到舞台上,像生成一个新对象一样(重置对象);
   重置到舞台上,将同一个对象放置在不同的位置;
   将其反弹回去。

移除对象
如果对象是不断产生的,那么使用一次性删除对象的方法是非常有效的。被删除的对象将会由新的对象所取代,这样舞台就永远不会为空。但也不能生成太多的可移动对象,因为这样会使Flash Player变慢。
调用removeChild(对象名),删除影片或显示对象,会将对象实例从舞台上移除。请注意,被移除的显示对象仍然存在,只是看不到而已。如果要将该对象彻底删除,还应该调用delete 对象名 将其完全删除。
如果移动的对象只是一些影片实例,并且物体的运动只由enterFrame函数进行处理,那么要停止整个程序的执行只需调用removeEventListener(Event.ENTER_FRAME,onEnterFrame);就可以了。另一方面,如果运动的对象很多,要通过持续执行代码使每个对象都动起来,就应该在数组中保存所有对象的引用,然后循环这个数组使里面的每个对象都动起来。随后,当删除了其中的某一个对象后,使用Array.splice方法同时将该对象的引用在数组中删除。
对象的位置由中心的注册点的位置决定,而注册点超出了屏幕的右边界,对象将被删除。如果对象的运动足够快,也许看上去问题不大。但如果运动得非常缓慢,我们会看着它走向屏幕的边界,但还差一半没有走完就被移除了!这就像一个演员只离开了舞台的一半就把戏服脱掉了,破坏了塑造人物的形象。
所以,要让对象完全离开场景,要等到它完全离开视野后再采取处理。实现这个计划,需要考虑到物体的宽度。因为注册点在中心,所以,可以将宽度的一半保存为radius属性。
下面是一个事例:用到二个类,一个是Ball类,绘制一个小球。一个是Removal类,创建20个小球,让它们移动,直到全部移动到屏幕外,删除onEnterFrame事件。
Ball类代码如下:
package {
import flash.display.Sprite;
public class Ball extends Sprite{
   public var radius:Number;
   private var color:uint;
   public var vx:Number=0;
   public var vy:Number=0;
   public function Ball(radius:Number=10,color:uint=0xff0000){
    this.radius=radius;
    this.color=color;
    init();
   }
   public function init():void{
    graphics.beginFill(color);
    graphics.drawCircle(0,0,radius);
    graphics.endFill();
   }
}
}
Removal类代码如下:
package {
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
public class Removal extends Sprite {
   private var count:int=20;
   private var balls:Array;
   public function Removal() {
    init();
   }
   private function init():void {
    stage.scaleMode=StageScaleMode.NO_SCALE;
    stage.align=StageAlign.TOP_LEFT;
    balls=new Array();
    for (var i:int=0; i      var ball:Ball=new Ball(10);
     ball.x=Math.random()*stage.stageWidth;
     ball.y=Math.random()*stage.stageHeight;
     ball.vx=Math.random()*2-1;
     ball.vy=Math.random()*2-1;
     addChild(ball);
     balls.push(ball);
    }
    addEventListener(Event.ENTER_FRAME,onEnterFrame);
   }
   private function onEnterFrame(e:Event):void {
    for (var i:Number=balls.length-1; i>0; i--) {
     var ball:Ball=Ball(balls[i]);
     ball.x+=ball.vx;
     ball.y+=ball.vy;
     if (ball.x-ball.radius>stage.stageWidth || ball.x+ball.radius<0 || ball.y-

ball.radius>stage.stageHeight || ball.y+ball.radius<0) {
      removeChild(ball);
      balls.splice(i,1);
      if (balls.length<=0) {
       removeEventListener(Event.ENTER_FRAME,onEnterFrame);
      }
     }
    }
   }
}
}

重置对象
重置对象是将超出舞台范围的对象进行重置。实际上就是重新配置,重新设置属性。当一个对象离开了舞台后,它就没有作用了,不过可以将其重置到舞台上,让它作为一个新对象再加入进来。永远不要担心对象的数量过多,因为这个数量是固定不变的。这个技术用于制作喷泉效果非常合适:一串粒子不停地喷射,超出舞台的粒子重新加入到水流中。
下面制作喷泉效果,使用Ball类作为水粒,使用Fountain用作效果类,其中gravity表示重力值,wind表示风力。代码如下


Ball类
package {
import flash.display.Sprite;
public class Ball extends Sprite{
   public var radius:Number;
   private var color:uint;
   public var vx:Number=0;
   public var vy:Number=0;
   public function Ball(radius:Number=10,color:uint=0xff0000){
    this.radius=radius;
    this.color=color;
    init();
   }
   public function init():void{
    graphics.beginFill(color);
    graphics.drawCircle(0,0,radius);
    graphics.endFill();
   }
}
}
Fountain类
package {
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
public class Fountain extends Sprite{
   private var count:int=100;
   private var gravity:Number=0.5;
   private var wind:Number=0.1;
   private var balls:Array;
   public function Fountain(){
    init();
   }
   private function init():void{
    stage.scaleMode=StageScaleMode.NO_SCALE;
    stage.align=StageAlign.TOP_LEFT;
    balls=new Array();
    for(var i:int=0;i
     var ball:Ball=new Ball(2,Math.random()*0xffffff);
     ball.x=stage.stageWidth/2;
     ball.y=stage.stageHeight;
     ball.vx=Math.random()*2-1;
     ball.vy=Math.random()*-30-30;
     addChild(ball);
     balls.push(ball);
    }
    addEventListener(Event.ENTER_FRAME,onEnterFrame);
   }
   private function onEnterFrame(e:Event):void{
    for(var i:Number=0;i
     var ball:Ball=Ball(balls[i]);
     ball.vy+=gravity;
     ball.vx+=wind;
     ball.x+=ball.vx;
     ball.y+=ball.vy;
     if(ball.x-ball.radius>stage.stageWidth || ball.x+ball.radius<0 || ball.y-

ball.radius>stage.stageHeight || ball.y+ball.radius<0){
      ball.x=stage.stageWidth/2;
      ball.y=stage.stageHeight;
      ball.vx=Math.random()*2-1;
      ball.vy=Math.random()*-18-18;
     }
    }
   }
}
}

屏幕环绕
屏幕环绕是当一个对象超出了屏幕的左界,就让它在屏幕右边出现;在右边出界,则将它置到左边;上面出界就回到下面;下面出界就到上面。
现在用两个类来实现屏幕环绕:一个是Ball类,这个不用说了吧,呵呵。。。还有一个是Cincture类,控制小球。代码如

下:
Cincture类
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
public class Cincture extends Sprite{
   private var ball:Ball;
   public function Cincture(){
    init();
   }
   private function init():void{
    stage.scaleMode=StageScaleMode.NO_SCALE;
    stage.align=StageAlign.TOP_LEFT;
    ball=new Ball();
    ball.x=Math.random()*stage.stageWidth;
    ball.y=Math.random()*stage.stageHeight;
    ball.vx=ball.vy=2;
    addChild(ball);
    addEventListener(Event.ENTER_FRAME,onEnterFrame);
   }
   private function onEnterFrame(e:Event){
    ball.x+=ball.vx;
    ball.y+=ball.vy;
    var left:Number=0;
    var right:Number=stage.stageWidth;
    var top:Number=0;
    var bottom:Number=stage.stageHeight;
    if(ball.x-ball.width/2>right){
     ball.x=left-ball.width/2;
    }else if(ball.x+ball.width/2
     ball.x=right+ball.width/2;
    }
    if(ball.y-ball.height/2>bottom){
     ball.y=top-ball.height/2;
    }else if(ball.y      ball.y=bottom+ball.height/2;
    }
   }
}
}

反弹
反弹就是当检测到物体超出舞台后,不改变物体的位置,只改变它的速度向量。方法很简单:如果物体超出了左、右边界,只需要使它的X速度向量取反。如果超出了上、下边界,只需要让Y速度向量取反。坐标轴取反非常简单,只需要乘以-1。

对于反弹时机来说,我们不希望等到物体完全超出了舞台后开始反弹。同样,也不希望出现半张图片的效果。因此,首先要判断出物体首次超出边界的瞬间。然后,将物体的运动路径取反,再加上小球宽度/高度的一半。
只要物体超出了舞台,即使只有不少部分,都要使它的速度向量取反,并且还需要将物体重新定位到边界外,这就形成了一个非常明显的撞击反弹的效果。如果不调整物体的位置,到下一帧,在物体移动之前,也许仍然处在边界外。如果这样的话,物体的速度向量又将取反,则向墙内运动!就会产生物体进出墙体的情形,然后在这附近振荡。
反弹的步骤如下:
   判断物体是否超出了边界;
   如果是,将其置到边界外;
   然后将它的速度向量取反。
下面来看代码,还使用以前的Ball类,再使用一个Bouncing类来描述反弹效果。
Bouncing类:
package {
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
public class Bouncing extends Sprite{
   private var ball:Ball;
   private var vx:Number;
   private var vy:Number;
   public function Bouncing(){
    init();
   }
   private function init():void{
    stage.scaleMode=StageScaleMode.NO_SCALE;
    stage.align=StageAlign.TOP_LEFT;
    ball=new Ball();
    ball.x=stage.stageWidth/2;
    ball.y=stage.stageHeight/2;
    vx=Math.random()*20-5;
    vy=Math.random()*20-5;
    addChild(ball);
    addEventListener(Event.ENTER_FRAME,onEnterFrame);
   }
   private function onEnterFrame(e:Event):void{
    ball.x+=vx;
    ball.y+=vy;
    var left:Number=0;
    var right:Number=stage.stageWidth;
    var top:Number=0;
    var bottom:Number=stage.stageHeight;
    if(ball.x+ball.radius>right){
     ball.x=right-ball.radius;
     vx*=-1;
    }else if(ball.x-ball.radius
     ball.x=left+ball.radius;
     vx*=-1;
    }
    if(ball.y+ball.radius>bottom){
     ball.y=bottom-ball.radius;
     vy*=-1;
    }else if(ball.y-ball.radius
     ball.y=top+ball.radius;
     vy*=-1;
    }
   }
}
}

摩擦力
假设有一张纸,将它撕碎后用力丢向空中。纸片会受到重力向下的牵引(Y轴),当我们松手后,纸片的X轴起初运动得非常快,但很快X轴的运动速度又归为零。
很显然,这里面没有负的加速度,但是纸片的速度向量却发生了改变,这就是摩擦力或阻力。虽然它不是一种严格意义上的力,但作用是相同的,因为它改变了物体的速度。原理是,摩擦力只改变速度向量中的速度,而不会改变运动的方向。
摩擦力是与速度向量相反的力,假设有一个摩擦力的数值,就可以将它从速度向量中减去。事实上,是从速度向量的量值或速度中减去,不能只是简单地从X,Y轴上减去。这样做的话,如果物体沿着一定角度运动,其中的一个分速度会提前到达零,使得物体继续垂直或水平地运动一会儿,结果看起来非常奇怪。
所以,我们要做的就是根据速度和方向找出角速度。使用vx和vy的平方和开平方后求出速度。再使用Math.atan2(vy,vx)求出角度,代码如下:
   var speed:Number=Math.sqrt(vx*vx+vy*vy);
   var angle:Number=Math.atan2(vy,vx);
然后就可以从速度向量中减去速度。如果摩擦力大于速度,速度就变为零,计算代码如下:
   if(speed>friction){
    speed-=friction;
   }else{
    speed=0;
   }
这样一来,还需要使用正弦和余弦将角速度转换回vx和vy,如下:
   vx=Math.cos(angle)*speed;
   vy=Math.sin(angle)*speed;
下面给出一个关于摩擦力的事例:
package {
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
public class Friction extends Sprite{
   private var ball:Ball;
   private var vx:Number=0;
   private var vy:Number=0;
   private var friction:Number=0.1;
   public function Friction(){
    init();
   }
   private function init():void{
    stage.scaleMode=StageScaleMode.NO_SCALE;
    stage.align=StageAlign.TOP_LEFT;
    ball=new Ball();
    ball.x=stage.stageWidth/2;
    ball.y=stage.stageHeight/2;
    vx=-Math.random()*7-7;
    vy=-Math.random()*7-7;
    addChild(ball);
    addEventListener(Event.ENTER_FRAME,onEnterFrame);
   }
   private function onEnterFrame(e:Event):void{
    var speed:Number=Math.sqrt(vx*vx+vy*vy);
    var angle:Number=Math.atan2(vy,vx);
    trace(speed);
    if(speed>friction){
     speed-=friction;
    }else{
     speed=0;
     removeEventListener(Event.ENTER_FRAME,onEnterFrame);
    }
    vx=Math.cos(angle)*speed;
    vy=Math.sin(angle)*speed;
    ball.x+=vx;
    ball.y+=vy;
   }
}
}

还有一种简单的方法实现摩擦力:用摩擦力乘以X,Y速度向量,摩擦力常用的值大约为0.9或0.8。因此,在每一帧,vx和vy的值都将变为上一次的80%或90%。理论上,速度向量会无限接近零,但永远不会等于零。在实际应用中,计算机计算如此小的数字的能力是有限的,所以最终都会取整为零。
这种方法最好的一点的速度向量永远不会变为负数,所以不需要进行判断。同样,X、Y轴的速度向量也是同比率变化的,所以不需要再将进行繁琐的转换。
只需要将前面例子中的firction变量设为0.9,然后按如下代码改变onEnterFrame方法:
   private function onEnterFrame(e:Event):void{
    vx*=friction;
    vy*=friction;
    ball.x+=vx;
    ball.y+=vy;
   }
大家可以观察下这两个方法的实现效果,我感觉第二个比第一个更真实。


下面是边界与摩擦力的主要公式:
移除出界对象:
   if(sprite.x-sprite.width/2>right || sprite.x+sprite.width/2 bottom

|| sprite.y+sprite.height/2
    //移除影片的代码
   }
重置出界对象:
   if(sprite.x-sprite.width/2>right || sprite.x+sprite.width/2 bottom

|| sprite.y+sprite.height/2
    //重置影片的位置和速度
   }
屏幕环绕出界对象:
   if(sprite.x-sprite.width/2>right){
    sprite.x=left-sprite.width/2;
   }else{
    sprite.x=right+sprite.width/2;
   }
   if(sprite.y-sprite.height/2>bottom){
    sprite.y=top-sprite.height/2;
   }else if(sprite.y+sprite.height/2
    sprite.y=bottom+sprite.height/2;
   }
摩擦力应用(正确方法):
   speed=Math.sqrt(vx*vx+vy*vy);
   angle=Math.atan2(vy,vx);
   if(speed>friction){
    speed-=friction;
   }else{
    speed=0;
   }
   vx=Math.cos(angle)*speed;
   vy=Math.sin(angle)*speed;
摩擦力应用(简便方法):
   vx*=friction;
   vy*=friction;


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值