转 第十章 坐标旋转及角度反弹(as3.0)

Flash as3 多个小球碰撞回弹 flash as3 编写的多个小球碰撞回弹效果! 立即下载

本章介绍了一项特殊技术,著名的坐标旋转。如同其名,它是物体指绕着某点旋转其坐
标,在制作一些非常有趣的效果时,坐标旋转是必不可少的。其中就包括在 Flash 界讨论
了很多年的问题:“如何在斜面上进行反弹?”,本章我会给大家一一解答。
    另一个用坐标旋转完成的程序是两物体之间的交互反弹效果。 我们会在下一章讨论动量
守衡时进行讲解。而本章的坐标旋转,我们之前也已经接触过了。如果大家想跳过这章的话,
我劝您还是先坐下来,浏览一遍为好。

 

简单的坐标旋转
      虽然我们在第三章讲三角学的时候介绍过计算的坐标旋转的方法,但还是先来做一下回
顾。假设知道一个中心点,一个物体,一个半径和一个角度。通过不断地增加或减少角度,
并运用基本的三角学知识让物体绕着中心点旋转。我们可将变量设为 vr (旋转速度)来控
制角度的增加或减少。还有,不要忘记角度应用弧度制来表示。代码的结构如下所示:
vr = 0.1;
angle = 0;
radius = 100;
centerX = 250;
centerY = 200;
// 在 enterFrame 处理函数中:
sprite.x = centerX + cos(angle) * radius;
sprite.y = centerY + sin(angle) * radius;
angle += vr;
      根据角度与半径使用简单的三角函数设置物体的 x,y 属性,并在每帧中改变角度。我
们用 Flash 动画演示一下。下面是第一个例子,文档类 Rotate1.as:
package {
  import flash.display.Sprite;
  import flash.events.Event;
  public class Rotate1 extends Sprite {
   private var ball:Ball;
   private var angle:Number = 0;
   private var radius:Number = 150;
   private var vr:Number = .05;
   public function Rotate1() {
     init();
   }
   private function init():void {
     ball = new Ball();
     addChild(ball);
     addEventListener(Event.ENTER_FRAME, onEnterFrame);
   }
private function onEnterFrame(event:Event):void {
 ball.x = stage.stageWidth / 2 + Math.cos(angle) * radius;
 ball.y = stage.stageHeight / 2 + Math.sin(angle) * radius;
 angle += vr;
   }
  }
}
     这段代码中没有什么新的知识点。大家可以改变一下角度与半径,试验运行结果。但是
如果我们只知道物体与中心点的位置又该怎么办呢?用 x,y 坐标计算出当前的角度(angle)
与半径(radius)也并非难事。代码如下:
var dx:Number = ball.x - centerX;
var dy:Number = ball.y - centerY;
var angle:Number = Math.atan2(dy, dx);
var radius:Number = Math.sqrt(dx * dx + dy * dy);
     这种基于坐标的旋转只对单个物体的旋转效果比较好,尤其是一次性就可确定角度和半
径的情况下。但是在动态的程序中,有时需要旋转多个物体,而它们与中心点的相对位置可
能会发生改变。因此,对于每个物体来说,都需要计算距离,角度和半径,还要用 vr 来增
加角度,最后才能算出新的 x,y 坐标,每帧都如此。这就显得太麻烦了,并且效率也不会
很高。没关系,我们还有更好的办法。

转 <wbr>第十章 <wbr>坐标旋转及角度反弹(as3.0)


图 10-1 坐标旋转
    角度(angle)就是某一时刻内旋转物体位置的大小。这并非当前的角度,也不是旋转后
的角度,而是这两者之间的差值。换句话讲,如果物体在离中心点 45 度的位置,而这次的
角度是 5 度,我们就要在当前角度的基础上再旋转 5 度,到达 50 度这个位置上。这里我
们并不关心最初或最终的角度,只关心旋转了多少度。通常来讲,这个角度都是用弧度制表
示的。OK,让我们来看例子吧。

单物体旋转
      本例中将一个小球放在随机的位置上,并使用前面介绍的方法对它进行旋转(文档类
Rotate2.as):
package {
  import flash.display.Sprite;
  import flash.events.Event;
  public class Rotate2 extends Sprite {
   private var ball:Ball;
   private var vr:Number = .05;
    private var cos:Number = Math.cos(vr);
    private var sin:Number = Math.sin(vr);
   public function Rotate2() {
     init();
   }
   private function init():void {
     ball = new Ball();
     addChild(ball);
     ball.x = Math.random() * stage.stageWidth;
     ball.y = Math.random() * stage.stageHeight;
     addEventListener(Event.ENTER_FRAME, onEnterFrame);
   }
   private function onEnterFrame(event:Event):void {
     var x1:Number = ball.x - stage.stageWidth / 2;
     var y1:Number = ball.y - stage.stageHeight / 2;
     var x2:Number = cos * x1 - sin * y1;
     var y2:Number = cos * y1 + sin * x1;
     ball.x = stage.stageWidth / 2 + x2;
     ball.y = stage.stageHeight / 2 + y2;
   }
  }
}
    在使用 vr 之前将它设成了 0.05,再计算这个角度的正弦和余弦值。根据小球与舞台
中心点的位置计算出 x1 和 y1。然后使用前面讲到的坐标旋转公式,计算出小球的新位置
x2 和 y2。由于这个位置是小球与中心点的相对位置,所以我们还需要把 x2 和 y2 与中心
点相加求出最终小球的位置。
    实验一下,我们发现这个例子与早先那个版本执行的结果是一样的。也许大家会问,既
然功能完全一样,为什么还要使用这个看起来很复杂的公式呢?如果处理的内容非常简单,
也许您的说法是正确的。下面让我们来看看这个公式在简化问题时的应用。首先,考虑多物
体旋转的情况。

多物体旋转
      假设要旋转多个物体,所有的影片都保存在命为 sprites 的数组中。那么 for 循环应
该是这样的:
for (var i:uint = 0; i < numSprites; i++) {
  var sprite:Sprite = sprites[i];
  var dx:Number = sprite.x - centerX;
  var dy:Number = sprite.y - centerY;
  var angle:Number = Math.atan2(dy, dx);
  var dist:Number = Math.sqrt(dx * dx + dy * dy);
  angle += vr;
  sprite.x = centerX + Math.cos(angle) * dist;
  sprite.y = centerY + Math.sin(angle) * dist;
}
  然而如果使用高级坐标旋转的方法应该是这样的:
var cos:Number = Math.cos(vr);
var sin:Number = Math.sin(vr);
for (var i:uint = 0; i < numSprites; i++) {
  var sprite:Sprite = sprites[i];
  var x1:Number = sprite.x - centerX;
  var y1:Number = sprite.y - centerY;
  var x2:Number = cos * x1 - sin * y1;
  var y2:Number = cos * y1 + sin * x1;
  sprite.x = centerX + x2;
  sprite.y = centerY + y2;
}
     请注意第一个版本中我们在 for 循环里调用了四次 Math 函数,这意味着每个物体的
旋转都要执行四次函数调用。第二个版本中只执行了两次函数调用,而且都是在 for 循环
以外面执行的, 意味着它们只执行了一次,与物体的数量无关。举个例子,如果我们有 30 个
影片,如果使用第一个版本的代码每帧需要调用 120 次 Math 函数。大家可以想想哪个版
本的效率最高。
     在前上一个例子程序中,删去 enterFrame 中的 sin 和 cos 函数。  这是因为这段程序
中的角度是固定的,因此可以直接给出结果。但是在很多情况下,旋转的角度不是固定不变
的,这就需要每次进行重新计算正余弦的值。
     解释一下最后这个概念。举个例子,用鼠标位置控制多个物体旋转的速度。如果鼠标在
屏幕中心,则不产生旋转。如果鼠标向左移动,则物体逆时针旋转,并且越向左速度越快。
如果向右移动,则顺时针旋转。除了创建多个 Ball 实例,并以数组存储以外,这个例子与
前面那个非常相似。下面是文档类(Rotation3.as):
package {
  import flash.display.Sprite;
  import flash.events.Event;
  public class Rotate3 extends Sprite {
   private var balls:Array;
   private var numBalls:uint = 10;
   private var vr:Number = .05;
   public function Rotate3() {
    init();
   }
   private function init():void {
     balls = new Array();
     for (var i:uint = 0; i < numBalls; i++) {
       var ball:Ball = new Ball();
       balls.push(ball);
       addChild(ball);
       ball.x = Math.random() * stage.stageWidth;
       ball.y = Math.random() * stage.stageHeight;
     }
     addEventListener(Event.ENTER_FRAME, onEnterFrame);
   }
   private function onEnterFrame(event:Event):void {
     var angle:Number = (mouseX - stage.stageWidth / 2) * .001;
     var cos:Number = Math.cos(angle);
     var sin:Number = Math.sin(angle);
     for (var i:uint = 0; i < numBalls; i++) {
       var ball:Ball = balls[i];
       var x1:Number = ball.x - stage.stageWidth / 2;
       var y1:Number = ball.y - stage.stageHeight / 2;
       var x2:Number = cos * x1 - sin * y1;
       var y2:Number = cos * y1 + sin * x1;
       ball.x = stage.stageWidth / 2 + x2;
       ball.y = stage.stageHeight / 2 + y2;
     }
   }
  }
}
    我们可以看到,代码并不是那么复杂。如果大家已经掌握了这个方法,可以试用角度与
弧度的方法各写一遍代码,看看执行的效果是好还是坏。
    在第十五章讨论 3D 时,回来再看这个公式。事实上,我们将会在同一个方法中使用两
次这个公式让物体绕着两个轴与三个维旋转。不要被我说的这些吓到了,因为在学到这里以
前我们还要学习很多的内容。

角度反弹
    我还记得在我刚刚开始痴迷于 Flash,数学和物体时,我就解决了物体撞墙地面、天花
板后反弹的效果。如果这些障碍物只是水平和垂直的,我就知道该怎么去做。但是渐渐地仅
知道这些已经不能满足需求了。在现实情况下,障碍物不仅仅只是水平或垂直的,而是带有
一定角度的。这时就没法在 Flash 中模拟反弹效果了。于是,我就去各个 Flash 论坛问了
一圈,发现我不是第一个问这个问题的人。在论坛里分别有三个帖子,讨论的题目都是“角
度反弹”。
    一些精通数学的版主试着回答这些问题。比如说反射角等于入射角。 记得有个非常简单
的公式告诉我们运动物体在碰撞到有角度的平面时,角度的变化。看起来不错,但也只是解
决了一部分问题。我们回忆一下物体碰撞在障碍物后反弹的问题,总结出如下几步:
  1.确实何时越过边界。
  2.直接在边界上重置物体的位置。
  3.改变碰撞轴上的速度。
    知道最终的角度只解决了步骤 3 中一半的问题。但是并不能知道何时物体与斜面发生
碰撞,也不知道物体碰撞前停止在斜面上的位置。似乎没人能回答这些问题。我试着用学过
的所有知识来回答,画的图稿可以放满整个仓库,写的程序装满整个硬盘,可最后还是失败
了。如果物体与平面碰撞那就太简单了,如果与斜面碰撞就太难了。但是为什么要在本章讨
论这个内容呢。
    那时,www.illogicz.com 的 Stuart Schoneveld 已经制作出超强的在线物理引擎,并
且可以实现这种平滑干净的碰撞。后来,我求他给我一些参考信息,他没有给我任何代码,
只用了一两句话说明了整体思想,让我茅塞顿开。
    他是这样说的“斜面反弹吗?先把斜面旋转成平面,然后执行反弹,最后再把它旋转回
去。”
    哇!这正是我想要的。我们只需要旋转坐标系,让斜面像平面一样。这就意味着旋转斜
面,旋转物体坐标,再旋转物体速度向量。
    现在考虑一下旋转速度向量的问题, 我们把速度向量保存到 vx 和 vy 中,变量中简单
地定义一个向量(角度与速度) 如果知道角度,
                            。             可以直接进行旋转。但是如果只知道 vx 和
vy,就可以使用高级坐标旋转公式实现。
    用图解释一下也许效果比文字要好些。 如图 10-2 所示,我们看到斜面与小球发生了碰
撞,而这个带箭头的向量表示小球运动的方向与速度。

转 <wbr>第十章 <wbr>坐标旋转及角度反弹(as3.0)

 

图 10-2 小球与斜面发生碰撞
在图 10-3 中,我们看到整个斜面被旋转成为一个水平面。注意速度向量也随之改变。

转 <wbr>第十章 <wbr>坐标旋转及角度反弹(as3.0)

图 10-3 旋转后的情景
  现在就很容易实现反弹了吧。调整小球位置,并改变 y 轴速度,如图 10-4 所示。


转 <wbr>第十章 <wbr>坐标旋转及角度反弹(as3.0)

图 10-4 反弹之后
  现在小球有了新的位置和速度。接下来,将所有的一切再旋转成为最初时的样子,如图 10-5
所示。


转 <wbr>第十章 <wbr>坐标旋转及角度反弹(as3.0)

 

图 10-5 旋转之后
    瞧!我们在斜面上检测出了碰撞的发生,调整了位置,改变了速度向量。希望这些图会
对大家有所帮助,下面来看程序。

 

实现旋转
     首先需要一个类似斜面的东西,只为了能够看到,并无实际用途。对于平面反弹,我们
可以使用舞台的边界。对于斜面反弹,我们就需要一条带有角度的线(line)来表示,以便看
到小球在斜面上的反弹。
     因此,创建一个 Sprite 影片,加入显示列表,然后使用绘图 API 绘制一条水平线,
再将影片进行一定角度的旋转。
     我们同样需要 Ball 类,现在应该保证它就在手边。在为物体定位时,要确保小球在线
上,以便小球可以落在线上。下面是文档类(AngleBounce.as):
package {
  import flash.display.Sprite;
  import flash.events.Event;
  public class AngleBounce extends Sprite {
   private var ball:Ball;
   private var line:Sprite;
   private var gravity:Number = 0.3;
   private var bounce:Number = -0.6;
public function AngleBounce() {
  init();
}
private function init():void {
  ball = new Ball();
  addChild(ball);
  ball.x = 100;
  ball.y = 100;
  line = new Sprite();
  line.graphics.lineStyle(1);
  line.graphics.lineTo(300, 0);
  addChild(line);
  line.x = 50;
  line.y = 200;
  line.rotation = 30;
  addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
private function onEnterFrame(event:Event):void {
  // 普通的运动代码
  ball.vy += gravity;
  ball.x += ball.vx;
  ball.y += ball.vy;
  // 获得角度及正余弦值
  var angle:Number = line.rotation * Math.PI / 180;
  var cos:Number = Math.cos(angle);
  var sin:Number = Math.sin(angle);
  // 获得 ball 与 line 的相对位置
  var x1:Number = ball.x - line.x;
  var y1:Number = ball.y - line.y;
  // 旋转坐标
   var x2:Number = cos * x1 + sin * y1;
   var y2:Number = cos * y1 - sin * x1;
   // 旋转速度向量
   var vx1:Number = cos * ball.vx + sin * ball.vy;
   var vy1:Number = cos * ball.vy - sin * ball.vx;
   // 实现反弹
   if (y2 > -ball.height / 2) {
     y2 = -ball.height / 2;
     vy1 *= bounce;
   }
   // 将一切旋转回去
   x1 = cos * x2 - sin * y2;
   y1 = cos * y2 + sin * x2;
   ball.vx = cos * vx1 - sin * vy1;
   ball.vy = cos * vy1 + sin * vx1;
   ball.x = line.x + x1;
   ball.y = line.y + y1;
 }
}
}
     开始,声明变量 ball,line,gravity,bounce。在 enterFrame 函数中执行基本运动代
码。
     然后,获得 line 的角度并转化为弧度制。有了角度,就可以求出正余弦的值。
     接下来, ball 的位置减去 line 的位置,
             用                              获得 ball 的 x,y 与 line 的相对位置。
     下面,准备对物体进行旋转!在看到这两代码时,注意到好像是错的。
    // 旋转坐标
     var x2:Number = cos * x1 + sin * y1;
     var y2:Number = cos * y1 - sin * x1;
     这里的加号与减号与最初的坐标旋转公式是相反的,最初我们的公式是:
     x1 = cos(angle) * x - sin(angle) * y;
     y1 = cos(angle) * y + sin(angle) * x;
     这并没有错。考虑一下,假如 line 旋转了 17 度,如果使用最初的公式,求出旋转度
数会比 17 度大,是 34 度!我们实际想要旋转 -17 度,这样才会使度数变为 0。那么就
应该这样计算正余弦值 Math.sin(-angle) 和 Math.cos(-angle)。但是,最后还需要用到
最初的角度来使所有物体都旋转回去。
     因此,可以使用一个角度旋转的变形公式进行反角度的旋转。我们看到,只需要调换加
减符号即可,就这么简单。如果 line 旋转了 17 度,那么所有的物体都要旋转 -17 度,
使得 line 为 0 度,或说是平面,速度向量也是如此。
     请注意,我们实际不需要旋转影片中的 line。只是为了让人们看到小球是在哪里被反
弹的。
接下来执行反弹,我们使用 x2,y2 的位置和 vx1,vy1 向量的值。注意因为 y2 是与 line
影片相对的,“底部”边界就是 line 自己。考虑一下小球的位置,判断什么时候 y2 大于
0 – ball.height / 2。简短的写法就是这样:
if(y2 > -ball.height / 2)
     边界的设置非常明显。
     最后使用最初的公式将所有的物体旋转回去。这些新的数值都是对 x1, y1, ball.vx,
ball.vy 更新过的值。我们所需要的就是重新设置小球的实际位置,通过 x1 和 y1 与
line.x 和 line.y 相加得出。
     花些时间来实验一下这个例子。试改变 line 的 rotation 属性以及改变 line 与
ball 不同的位置,观查运行结果。确保它们都能正常工作。

 


优化代码
    前面我们已经看过一些代码优化的例子。通常是使用一次执行代替多次执行,或干脆不
执行。
    我们前面写的那段代码只是为了看得比较清楚。其中有一些代码实际上并不需要执行。
多数代码只有在 ball 与 line 产生接触时才执行。因此,多数时间只需要执行基本的运动
代码。换句话讲,我们要将代码放到 if 语句中去:
if(y2 > -ball.height / 2)
    所以我们只需知道变量 y2。为了得到它需要 x1 和 y1 以及 sin 和 cos。但是如果
ball 没有碰到 line,就不需要知道 x2 或 vx1 和 vy1。因此,这些都可以只在 if 语句
中出现。同样,如果没有产生碰撞,就不需要对任何物体进行旋转或设置 ball 的位置。因
此,所有 if 语句后面的内容都可以放在 if 语句里面执行。于是就得出了优化版的
onEnterFrame 方法(见 AngleBounceOpt.as):
private function onEnterFrame(event:Event):void {
    // 普通的运动代码
    ball.vy += gravity;
    ball.x += ball.vx;
  ball.y += ball.vy;
  // 获得角度及正余弦值
  var angle:Number = line.rotation * Math.PI / 180;
  var cos:Number = Math.cos(angle);
  var sin:Number = Math.sin(angle);
  // 获得 ball 与 line 的相对位置
  var x1:Number = ball.x - line.x;
  var y1:Number = ball.y - line.y;
  // 旋转坐标
  var y2:Number = cos * y1 - sin * x1;
  // 实现反弹
  if(y2 > -ball.height / 2) {
     // 旋转坐标
     var x2:Number = cos * x1 + sin * y1;
     // 旋转速度向量
     var vx1:Number = cos * ball.vx + sin * ball.vy;
     var vy1:Number = cos * ball.vy - sin * ball.vx;
       y2 = -ball.height / 2;
       vy1 *= bounce;
     // 将一切旋转回去
     x1 = cos * x2 - sin * y2;
     y1 = cos * y2 + sin * x2;
     ball.vx = cos * vx1 - sin * vy1;
     ball.vy = cos * vy1 + sin * vx1;
     ball.x = line.x + x1;
     ball.y = line.y + y1;
  }
}
    所有粗体的内容都是从 if 语句外面移到里面去的,所以只有产生碰撞时它们才会执
行,这样做比每一帧都执行要好很多。可以想象我们节省了多少 CPU 资源吗?这样的考虑
是非常重要的,尤其是当影片变得越来越多,代码变得越来越复杂时。
动态效果
    现在我们可以将这个程序变得更加动态些,实时地改变 line 的角度。只需要一行代码
即可搞定,在 onEnterFrame 方法的第一行写入:
line.rotation = (stage.stageWidth/ 2 - mouseX) * .1;
    现在我们只要前后移动鼠标,line 就会随之倾斜,小球也会立即进行调整。完整的代
码可见文档类 AngleBounceRotate.as。
修正“跌落”问题
    大家也许注意到这样一个问题,即使 ball 离开了 line,它依然会沿着那条边运动。
看起来有些奇怪,但是要记住 ball 并不是真的与 line 影片产生交互。虽然执行的结果是
非常准确的,我们可以认为实际上没有碰撞,一切都是计算出来的结果。因此 ball 根本就
不知道 line 影片,也不知道影片的起点与终点。但是我们可以告诉它 line 在哪里——使
用简单的碰撞检测或更精确的边界检测方法。下面让我们看看这两种方法,由您决定使用哪
一种。
碰撞测试
    找出 line 位置最好的方法是将除了基本移动代码以外所有的内容都放到碰撞检测的
if 语句中,如下:
private function onEnterFrame(event:Event):void {
    // 普通的运动代码
    ball.vy += gravity;
    ball.x += ball.vx;
    ball.y += ball.vy;
    if(ball.hitTestObject(line) {
        // 剩下的所有内容都在这个函数中
    }
}
    虽然方法很简单,但是已经可以满足大部分的实际需要,下面还有一种更加精确一些的
方法,也是我比较喜欢的一种方法。当然,这就需要更加复杂一些的计算。
判断边界
    个人认为 getBounds 方法是 ActionScript 中最不被充分利用的方法。我曾考虑在第
九章中配合碰撞检测提一下,但那章的内容已经很多了, 而且我发现最适合使用的地方应该
是下面这个地方。所以把它放在这一章为大家讲解。
    回顾一下碰撞检测一章,一定还记得矩形边界吧。为了让大家回忆起来,我们一起来回
顾一下。矩形边界是指用于包围舞台上一个显示对象可见图形元素的矩形边框。
hitTestObejct 和 hitTestPoint 函数都应用了这种边界。
    getBounds 函数直接给出了矩形边界的位置和大小的值。下面是这个函数的基本用法:
    bounds = displayObject.getBounds(targetCoordinateSpace)
    可以看到,这个方法作为任何显示对象的方法来调用,并返回 flash.geom.Rectangle
的实例,描述了矩形的大小与位置。
    首先,来看一下这个唯一的参数,targetCoordinateSpace。是什么意思?
    我们使用 targetCoordinateSpace 参数来指定用哪种视角来描述矩形边界。大多数情
况下,这个参数是该物体的父级显示对象。比如,如果主文档类就是一个 Sprite 影片,我
们叫它 sprite,那么 getBounds(this),就表示“根据主影片的坐标,给出这个 sprite 的
矩形边界”。另外,如果在一个 sprite 里面又创建或加载了其它 sprite,就需要通过外
层影片的位置得到矩形边界。写法如下:
childSpite.getBounds(parentSprite);
    这个意思是说,我们要得到 childSprite 影片的矩形边界,而这个影片位于
parentSprite 的里面,并且我们想要用 parentSprite 坐标空间的视角来描述它。显然,
targetCoordinateSpace 应该是个显示对象,或是继承自 DisplayObject 类的实例。文档
类,Sprite, MovieClip 都是显示对象,没问题。
    下面看看 getBounds 函数的返回值。前面说过,返回值是一个 Rectangle 的实例,里
面包涵了矩形边界的数据。以前在使用 Rectangle 类时看到过,它里面有四个属性:x, y,
width, heigth。并且我们可以使用这些信息。它还包括其它一些非常有用的属性:
left,right,top,bottom。大家应该可以猜出它们的意思吧。
    让我们来试一试。将一个 Ball 类的实例加入显示列表,然后插入如下代码(不要忘记
导入 flash.geom.Rectangle 类):
var bounds:Rectangle = ball.getBounds(this);
trace(bounds.left);
trace(bounds.right);
trace(bounds.top);
trace(bounds.bottom);
    做一个有趣的实验,将第一行代码改为:
var bounds:Rectangle = ball.getBounds(ball);
    现在,小球的边界是从它自身的视角来看的;换句话讲,这个位置与它自身注册点的位
置是相关的。因为小球是以 0,0 点作为中心绘制的,left 和 top 的值应该是负数,实际
上就等于 –right 和 –bottom。这里值得一提的是,如果我们调用 getBounds 时没有给
出参数,那么结果是相同的,物体本身也就是它的目标坐标空间。
    现在也许大家都忘了为什么要讨论边界了吧, 回忆一下。我们想要知道物体何时从 line
的上面跌落下来,还记得吗?因此,可以在 line 上面调用 getBounds,   然后得到它的 left
和 right。如果 ball 的 x 小于 bounds.left,或大于 bounds.right, 就说明它从 line 上
面掉落了。再怎么说也不如看代码的好,以下是程序:
private function onEnterFrame(event:Event):void {
    line.rotation = (stage.stageWidth/ 2 - mouseX) * .1;
    // 普通的运动代码
    ball.vy += gravity;
    ball.x += ball.vx;
    ball.y += ball.vy;
    var bounds:Rectangle = line.getBounds(this);
    if(ball.x > bounds.left && ball.x < bounds.right) {
        // 剩下的所有内容都在这个函数中
    }
}
    大家可以在 AngleBounceBounds.as 中找到完整的程序。


修正“线下”问题
    碰撞检测与边界判断这两种方法都要先确定 ball 与 line 相互接触,然后进行坐标旋
转获得调整后的位置与速度。如果 ball 的 y 坐标旋转后的位置 y2 超过了 line, 则执行
反弹。但是如果 ball 自下而上穿过了 line,又该怎么办?假设 line 在舞台的中心位置,
然后 ball 从下面反弹上来。如果碰撞检测与边界判断的结果都是 true,Flash 就认为
ball 刚刚从 line 上弹出去,它会把 ball 从 line 下面移到上面去。那么我的解决办法
是比较 vy1 和 y2,并且只在 vy1 大于 y2 时才进行反弹。如图 10-6 所示。



转 <wbr>第十章 <wbr>坐标旋转及角度反弹(as3.0)

 

图 10-6 线上通过还是线下通过?
    左面 ball 的 y 速度大于 ball 与 line 的相对距离。这就意味着只要它进行运动就
会跑到线上面去。而右面 ball 的速度小于 ball 与 line 的相对距离。换句话讲,在这一
帧内,它位于线下,并且下一帧还是在线下。只有在小球从线上往下掉落时从进行反弹。下
面看一下这段代码的修改。以下是 enterFrame 代码段:
// 旋转坐标
var y2:Number = cos * y1 - sin  * x1;
// 实现反弹
if(y2 > -ball.height / 2) {
// 旋转坐标
var x2:Number = cos * x1 + sin  * y1;
// 旋转速度向量
var vx1:Number = cos * ball.vx  + sin * ball.vy;
var vy1:Number = cos * ball.vy  - sin * ball.vx;
...
我们只需要把 y2 < vy1 加入到 if 语句中:
if(y2 > -ball.height / 2 && y2 < vy1)
如果这样的话,就需要先计算出 vy1。代码如下:
// 旋转坐标
var y2:Number = cos * y1 - sin * x1;
// 旋转速度向量
var vy1:Number = cos * ball.vy - sin * ball.vx;
// 实现反弹
if(y2 > -ball.height / 2 && y2 < vy1) {
// 旋转坐标
var x2:Number = cos * x1 + sin * y1;
// 旋转速度向量
var vx1:Number = cos * ball.vx + sin * ball.vy;
...
    这样一来每帧都要多做一些计算,这也是为了获得较高精确度和真实度付出的代价。大
家可以决定是否需要它。如果说小球不可能到达线下面的话,就不必考虑这个问题。
    在文档类 AngleBounceFinal.as 中,我加入了墙壁与地面的反弹,以便可以让大家看
到小球从线上掉落后的情况。试将我们刚才讨论的代码删除,观察有何不同。
    OK,下面我们进入本章最后一个大型的例子程序。
多角度反弹
    目前为止,我们都是在讨论一条线或一个斜面的问题。  要处理多个斜面也不是很复杂的
事情,只需要创建多个斜面并进行循环。我们可以把角度反弹的代码抽象到一个函数中,每
次进行调用。
    本章前面的这些例子中,我都让代码尽可能地简单,只给大家必要的代码进行演示。然
而,接下来这段代码是一个完正的程序,用到了前面几章学过的一些技术(为的是唤醒大家
的记忆)。
    本例与前面那几个例子很像,都使用了同样的 ball 和 line 影片,  只不过这次我把线
缩短了一些,以便可以多放几条进去。在舞台上放置了五条线和一个小球。线条由数组 lines
保存,并放置于舞台上。如图 10-7 所示。



转 <wbr>第十章 <wbr>坐标旋转及角度反弹(as3.0)

图 10-7 多线条
      以下是代码(可在 MultiAngleBounce.as 中找到):
package {
  import flash.display.Sprite;
  import flash.events.Event;
  import flash.display.StageScaleMode;
  import flash.display.StageAlign;
  import flash.geom.Rectangle;
  public class  MultiAngleBounce extends Sprite {
   private var  ball:Ball;
   private var  lines:Array;
   private var  numLines:uint = 5;
   private var  gravity:Number = 0.3;
   private var  bounce:Number = -0.6;
   public function MultiAngleBounce() {
     init();
   }
private function init():void {
 stage.scaleMode = StageScaleMode.NO_SCALE;
 stage.align = StageAlign.TOP_LEFT;
 ball = new Ball(20);
 addChild(ball);
 ball.x = 100;
 ball.y = 50;
 // 创建 5 个 line 影片
 lines = new Array();
 for (var i:uint = 0; i < numLines; i++) {
  var line:Sprite = new Sprite();
  line.graphics.lineStyle(1);
  line.graphics.moveTo(-50, 0);
  line.graphics.lineTo(50, 0);
  addChild(line);
  lines.push(line);
 }
 // 放置并旋转
 lines[0].x = 100;
 lines[0].y = 100;
 lines[0].rotation = 30;
 lines[1].x = 100;
 lines[1].y = 230;
 lines[1].rotation = 45;
 lines[2].x = 250;
 lines[2].y = 180;
 lines[2].rotation = -30;
  lines[3].x = 150;
  lines[3].y = 330;
  lines[3].rotation = 10;
  lines[4].x = 230;
  lines[4].y = 250;
  lines[4].rotation = -30;
  addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
private function onEnterFrame(event:Event):void {
  // normal motion code
  ball.vy += gravity;
  ball.x += ball.vx;
  ball.y += ball.vy;
  // 舞台四周的反弹
  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;
  }
  // 检查每条线
  for (var i:uint = 0; i < numLines; i++) {
   checkLine(lines[i]);
  }
}
private function checkLine(line:Sprite):void {
  // 获得 line 的边界
  var bounds:Rectangle = line.getBounds(this);
  if (ball.x > bounds.left && ball.x < bounds.right) {
   // 获取角度与正余弦值
   var angle:Number = line.rotation * Math.PI / 180;
   var cos:Number = Math.cos(angle);
   var sin:Number = Math.sin(angle);
   // 获取 ball 与 line 的相对位置
   var x1:Number = ball.x - line.x;
   var y1:Number = ball.y - line.y;
// 旋转坐标
var y2:Number = cos * y1 - sin * x1;
// 旋转速度向量
var vy1:Number = cos * ball.vy - sin * ball.vx;
// 实现反弹
if (y2 > -ball.height / 2 && y2 < vy1) {
 // 旋转坐标
 var x2:Number = cos * x1 + sin * y1;
 // 旋转速度向量
 var vx1:Number = cos * ball.vx + sin * ball.vy;
 y2 = -ball.height / 2;
 vy1 *= bounce;
 // 将一切旋转回去
 x1 = cos * x2 - sin * y2;
        y1 = cos * y2 +  sin * x2;
        ball.vx = cos *  vx1 - sin * vy1;
        ball.vy = cos *  vy1 + sin * vx1;
        ball.x = line.x  + x1;
        ball.y = line.y  + y1;
      }
     }
   }
  }
}
      代码很多,不过很容易解释,其中的每一部分大家都应该认识。复杂的程序并不代表复
杂的代码,而常常是由许多熟悉的代码段构成的。本例中,checkLine 方法与前面版本中的
onEnterFrame 是一样的。只不过是被 for 循环调用了五次而已。
      大家感兴趣的话可以做一个小小的优化。 比方说每次都要循环判断多个斜面,但在很多
系统中,   只要发现小球与某个斜面发生了碰撞并产生交互的话,我们就不需要再继续判断其
它的斜面了,这时我们就可以退出这个循环。为了实现这个功能,需要让 checkLine 函数
返回 true 或 false,   来告诉我们是否发生了碰撞。然后在 onEnterFrame 函数中执行这样
的循环:
for(var i:uint = 0; i < numLines; i++) {
      if(checkLine(lines[i])) {
           break;
      }
}

    在一些情况下,尤其是线条非常密集的时候,需要每帧都判断所有的线条。如果是这样,
那么是否决定使用优化,决于个人的需要。

 


本章重要公式
    下面回忆一下本章的两个主要公式。
坐标旋转:
x1 = Math.cos(angle) * x - Math.sin(angle) * y;
y1 = Math.cos(angle) * y + Math.sin(angle) * x;
反坐标旋转:
x1 = Math.cos(angle) * x + Math.sin(angle) * y;
y1 = Math.cos(angle) * y - Math.sin(angle) * x;

简单的as3.0代码实现文字旋转 简单的as3.0代码实现文字旋转,成圆形旋转,并与鼠标有交互功能,非常常见的as效果。 立即下载

相关推荐

AS3.0 图片点击缩放、旋转移动

带例子,第三方类库,使用方便,能移动,缩放、旋转图片,可自定义样式

转 第三章 三角学应用(3)(as3.0)

圆和椭圆      目前为止我们已经掌握了正弦波,下面再来看看它的兄弟,余弦波。与正弦波的形成相 同,只不过是使用余弦函数代替了正弦函数而已。 如果你还记得前面所说的正弦和余弦是怎 样一种相反关系的话,就能理解,它们只是波形都相同,只是所处位置不同了。图 3-19 为 余

roadProgram的专栏 2663

最新推荐-2026年固态电池+锂电正极+硅基负极等关键技术实践PPT合集(55份).zip

2026年最新固态电池+锂电正极+硅基负极等关键技术实践PPT合集,共55份。供大家查阅。 一、固态电池 1.全固态电池用硅基负极材料研究进展 2.全固态电池硅基负极的开发思考 3.功能型固态电解质设计及界面调控 4.固态电池及关键材料研发与产业化进展 5.固态电池的产业化应用探索 6.固态锂电池开发现状及未来展望 7.固态锂离子电池正极结构设计与材料选择 8.固态锂金属电池设计与应用 9.硫化物全固态电池石墨和硅负极性能调控技术 10.硫化物固态电解质行业应用 11.聚合物基固态电解质与固态电池的关键技术 12.蜂鸟声共振技术-固态电池产业化的革命性创新 13.长安汽车固态电池应用探索 14.高安全性固态电池技术发展趋势 15.高性能硫化物全固态锂电池解决方案 16.高硅体系的膨胀解决方案 二、硅基负极 1.化学气相沉积碳包覆技术在硅基负极材料中的应用 2.新型粘结剂解决 硅基负极膨胀问题 3.氧化亚硅负极材料化学预锂化与界面设计 4.流化床法生产磷酸铁锂及硅负极材料研究 5.石墨烯包覆硅碳负极 6.硅基材料的发展现状及展望 7.硅基负极与固态电解质 8.硅基负极技术与进展 9.硅基负极材料体积膨胀解决策略 10.硅基负极材料及其结构设计 11.硅基负极材料的冶金法制备 12.硅基负极材料的发展与应用 13.硅基负极材料研究进展 14.硅基负极材料:从基础研究到产业应用 15.硅基负极析锂行为及调制研究 16.硅碳负极低成本制造技术报告 17.硅碳负极制备用多孔碳的开发 18.硅碳负极挑战和进展 19.硅碳负极新路线及关键材料制备 20.硅碳负极锂离子电池应力特性研究 21.锂电石墨负极材料工程化技术进步 22.锂离子电池负极材料 23.高容量石墨烯-硅基复合负极材料的研究进展 三、锂电正极 1.基于AI和图论结构化学的锂电池的材料基因探索 2.废旧磷酸铁锂电池梯次及再生利用进展 3.电动汽车大功率充电标准体系与检测认证实践 4.磷酸铁及磷酸铁锂异物防控管理 5.磷酸铁锂正极材料:液相法合成及产业化应用研究 6.磷酸锰铁锂正极材料的研究现状 7.车用动力电池安全标准化工作进展及未来展望 8.锂电层状氧化物正极材料的原子尺度失效机制 9.锂电正极材料工厂异物防控管理图册 10.锂离子电池高熵正极材料成分设计及储锂机制研究 11.高压实磷酸铁锂正极材料生产工艺配方.docx 12.高安全正极材料添加剂提升三元锂电安全性 13.高比能富锂锰基电池关键技术 14.高能量密度锂离子电池正极材料及应用研究 15.高镍单晶三元正极材料研究 16.三元高电压单晶正极材料

转 第三章 三角学应用(2)(as3.0)

反正切(Arctangent)     大家可能都猜到了,反正切简单地说就是正切函数的反函数。我们只要输入对边与邻边 的比值,就可以得到相应的角度。     在 Flash 中有两个函数可计算反正切。第一个就是像前面介绍过的函数一样 Math.atan(ratio),只需

roadProgram的专栏 1639

CARLA 0.9.15从零开始:环境搭建与基础场景实战指南

本文是CARLA 0.9.15从零开始的完整环境搭建与基础运行教程。详细介绍了在Linux系统下配置Python环境、下载安装CARLA核心软件包、启动服务器以及运行第一个基础场景的步骤。重点演示了如何通过Python客户端脚本生成随机车流,并手动控制EGO车辆在仿真城市中行驶,为自动驾驶算法开发构建了坚实的仿真测试基础。

weixin_29053383的博客 780

科研效率革命:Zotero与Web of Science联动实现千篇文献一键获取

本文详细介绍了如何利用Zotero与Web of Science联动实现科研文献的高效管理,包括批量导入、智能下载和自动整理千篇文献的技巧。通过精准检索、RIS文件导出和Zotero插件设置,科研人员可以大幅提升文献获取和管理的效率,特别适合开题报告和综述写作等场景。

weixin_29219539的博客 255

AS3学习之 弧度制与角度制

弧度制与角度制   弧度=角度*Math.PI/180 角度=弧度*180/Math.PI 三角函数中必须使用弧度制。 正弦   Math.sin() 余弦   Math.cos() 正切   Math.tan() 反正弦 Math.asin() 反余弦 Math.acos() 反正切 Math.atan() 反正切2 Math.atan2(y,x)

etdeng的专栏 802

setoolkit克隆网站获取用户信息

一、环境 kali、DVWA 二、步骤 首先打开kali,输入setoolkit 依次选择选项1-选项2-选项3-选项2 输入钓鱼网站的网址(默认为本地网址) 输入要克隆的网址URL 这里我的网址如图,将网址复制 然后模拟攻击场景,攻击者向受害者发送了一条钓鱼链接,此链接与DVWA的登陆页面完全一样,受害者点击之后并进行登陆信息 点击login之后,用户信息就被setool...

BeatRex的博客 2998

转 第九章 碰撞检测(as3.0)

到目前为止,我们已经学习了物体在其空间的内交互运动。接下来研究一下物体之间的 交互运动。这就需要确定物体间何时发生了碰撞,这就是我们所讲的碰撞检测(Collision detection 或 Hit testing)。     本章我会尽量将所有需要掌握的相关知识告诉大家。

roadProgram的专栏 4627

国际项目投标那些事(三)海外项目招标文件的组成

以一个实际的项目作为例子,是我2020年初投的一个东南亚国家的水厂的竞争性投标。这个投标的招标基于FIDIC yellow book (Design and Build) 合同模板。 海外项目的招标一般是有专门的咨询公司(consulting engineer)写招标文件,有的咨询公司非常专业,对于技术、合同、业绩的要求非常细致。 我这个项目的招标文件共5册 400多页(全英文),附件26册将...

WTSolutions的博客 2006

SCCM部署教程:(3)SCCM安装

这两则摘要介绍了WSUS和SCCM的安装步骤: WSUS安装:通过服务器管理器添加"Windows服务器更新服务"角色,选择SQL数据库并指定安装路径,验证数据库连接后完成安装。 SCCM安装:先运行预检工具Prereqchk.exe,选择主站点安装类型,配置语言、站点代码和名称,设置SQL数据库连接参数(保留默认端口4022),选择通信方法和系统角色(管理点和分发点),最后完成约1小时的安装过程。 两则安装指南都强调要确保数据库配置与前期准备一致,并提供了关键配置选项的说明。

小V锅的专栏 919

转 第十一章 撞球物理(1)(as3.0)

我们都希望技术性的书籍,可以潜入深,由简单到复杂。本章内容的复杂度达到了顶点。 并不是说接下来的章节会越来越容易,但是希望大家在学习本章内容时不要偷工减料。我会 带大家一步步地学习本章的概念,如果到现在为止大家都能很好地跟上我的步伐,那就再好 不过了。     本章我们要

roadProgram的专栏 1069

华为OD机考2025B卷 - 报文响应时间(Java & Python& JS & C++ & C )

华为OD机考2025B卷 - 报文响应时间

算法大师! 8083

Making Things Move! 中文版

 Making Things Move! 中文版 今已全部杀青今天是我最后一次发部关于这本书的中文版(非官方“邮电”译版)的消息了,因为我已经将它全部译完了。    历经四个月的时间,今天终于把 Foundation Actionscript 3.0 Animation —— Making Things Move! 一书全部译完。非常感谢本书作者 Keith Peters 带给我们这么好的

xyzvalue的专栏 1296

QT+OSG/osgEarth编译之七十六:gltf+Qt编译(一套代码、一套框架,跨平台编译,版本:osgEarth-3.3插件库osgdb_gltf)

通过一套gltf代码和框架,实现osgdb_gltf跨平台编译。 在Qt环境下,集成头文件、库文件,构建跨平台编译的pro文件。 通过构建的一套配置工程,基于Qt Creator IDE,完成跨平台的编译实践。 在Windows、Linux、MacOS等操作系统上进行测试,成功编译,形成的成果(头文件、库文件等)可在不同系统下调用或使用,从而更好地构建跨平台解决方案。 采用的是osgEarth3.3版本。 读者可参考博客中的集成原理,构建不同版本的osgdb_gltf跨平台包。

LuckyHanMo的博客 1802

金砖大赛-第二届网络安全防护治理实战技能大赛全国总决赛WP

格式进行md4加密,然后将小写的md4密文进行作为flag信息进行提交,提交格式为flag{MD4小写},例如调用频率异常高的user_id是user_xxx,使用的非公开模型名称是model-name,具体调用次数为10,那么按照格式,对user_xxx:model-name:10进行md4加密后,提交范例为:flag{dd00cb9e393f4a9ac639294b3cfe4d6a}挖矿木马是通过远程脚本进行部署的,请登录到失陷的服务器上,找到并分析挖矿木马是从那个远程IP下载的。

Laffrey的专栏 245

AS3.0中的三角函数

以下是一些在AS3.0中常用的三角函数。代码如下: trace (Math.tan(45*Math.PI/180));//已知45度角求正切值; trace (Math.atan(1)*180/Math.PI);//已知正切值为1求出角度; trace (Math.sin(30*Math.PI/180));//已知30度角求正弦值; trace (Math.asin(1/2)*18...

weixin_34348805的博客 297

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

AS3 碰撞检测

package { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Sprite; import flash.display.StageAlign; i

当 C 遇 到 ++ 946

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

AS3.0抽奖盘,控制角度和圈数

参照夏树的例子,自己也模仿了一个,夏树的地址:http://blog.csdn.net/hero82748274/article/details/6527162 自己加以理解也注释了一部分。 package { import flash.display.MovieClip; import flash.events.*; import flash.display.Shape;

GavinFly的专栏 5038

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

micropython控制舵机_玩转micro:bit-用BXY控制小舵机、360舵机和带驱动N20电机

今天分享的是BXY的一个小功能--Servo(伺服机构),您用它可以做出好多好玩的小作品来,可以是六足虫,可小车或者其它有创意的好玩的东西。无标题.png (102.93 KB, 下载次数: 10)2018-5-5 15:15 上传在makecode中,就是设置某引脚伺服机构的角度。专题分享Servo,是因为我在前面的学习中走了弯路,在写舵机程序时用了啰嗦的方法。而现在,我发现,DF其实给的有简单...

weixin_39760368的博客 2484

AS3.0碰撞检测 hitTestObject 与 hitTestPoint

早先 Flash 中的影片剪辑都有内置 hitTest 方法,这个方法有很多种用途。而现在已 经将它划分为两种方法,这样做更加合理。hitTestObject 方法用于判断两个显示对象间是 否发生了碰撞,而 hitTestPoint 方法用于判断某个点与显示对象间是否发生了碰撞。 碰撞检测两个影片     使用 hitTestObject 判断两个影片是否碰撞也许是最简单的碰撞检测方法

学习记录 1万+

AS3.0旋转过渡效果

package ch13_1 { import fl.transitions.*; import fl.transitions.easing.*; import flash.display.Graphics; import flash.display.MovieClip; import flash.events.MouseEvent; public class RoTateTex

红土豆jie 2041

转 第十八章 矩阵数学 (as3.0)

本章我们不去介绍一些新的运动、物理学或渲染图形的方法。我要给大家介绍的是矩阵 (Matrix),它给我们提供了一个新的可选方案。      矩阵在 3D 系统中 3D 点的旋转,缩放以及平移(运动)中使用得非常频繁。在各种 2D 图形的变换上也很常用。您也许可以回想到 be

roadProgram的专栏 2266

as3碰撞检测(两图重叠区)(例子)

<br />1 package<br />2 {<br />3     <br />4      import flash.display.Sprite;<br />5      import flash.events.MouseEvent;<br />6      import ws.tink.display.HitTest;<br />7      import flash.geom.Rectangle;<br />8 <br />9      public class ComplexIntersect

tian5034的专栏 852

as3.0两点之间简单的运动,斜着运动,任意两点

import flash.utils.Timer;import flash.events.TimerEvent;//fixed结束点//sprite初始点var fixedX:Number = fixed.x;var fixedY:Number = fixed.y;var speed:Number = 2;var timer:Timer = new Timer(25); var dx:Numbe...

weixin_30332705的博客 231
上一篇: 转 第九章 碰撞检测(as3.0)
下一篇: 转 第十一章 撞球物理(1)(as3.0)
coding_most
博客等级 码龄15年 13粉丝 38原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值