发新话题
打印

[教材] 第三章 三角学应用(译Making Things Move)

第三章 三角学应用(译Making Things Move)

从这一章开始,我们将学习三角学,并在第五章,应用于动画技术中。其实上,在下一章的绘图技术中就会接触到。如果你已经对三角学有所了解或渴望学习动画方面的知识,那么可以跳过开始这部分。待日后遇到不懂的问题时,再回来学习。我们用到的90%的三角学都需要 Math.sin 和 Math.cos 这两个函数。在我写本书的第一版时,曾说过,除了在中学学习过的那些代数和几何外(而且由于时间久远大多都记不清了),我没有接受过正规的数学培训,最初在本章中的内容都是来自于各种书籍,网站或是其它网络资源。这是因为它并不难学,既然我能够学会,那么你也一定可以的。而现在我已经完成了大学代数和微积分课程,对于三角学也有了更为全面和系统的了解。我可以很荣幸地说,这一章的内容非常好,修正了之前的内容,对于这个学科也有了更深入的了解。

什么是三角学(trigonometry)
    三角学是一门研究三角形与其边和角关系的学科。当我们观察一个三角形时,发现它有三条边和三个角(因此称为三角),而且在这些边和角之间存在着一些特殊的关系。例如,增大任何一个三角形的其中一个角,那么该角所对应的边就会变长(假设其它两条边长度不变),同时,其它两个角会变小,实际上,究竟它们变化了多少,加以计算后都可以得出一个比例。在一个三角形中,如果其中有一个角为90度,那么就称为直角三角形,并在该角的夹角处标出一个正方形(垂足),只有在直角三角形才会这样。学习这些不同的关系要比推导基本公式简单得多。这使得直角三角形成为一种非常有用的结构,本章及该书后面的内容大多都是直角三角形。

角(angle)
三角学最主要的是研究角,让我们先来解决这个问题。角是由两条相交线而构成的图形,或是两条相交线之间的那部分空间,空间远大,夹角越大。事实上,两条相交的线会形成四个角,见图 3-1:

图 3-1 两条线形成四个角

弧度制(radian)与角度制(degress)
弧度制与角度制是角度测量中的两种特殊制度。我们也许对于角度制最为熟悉,闭着眼都能画出 45度或90度的角。圆的360度体系,已经成为了一种文化。人们常说“180度转弯”就是指“转到相反的方向”,我们并不是指转弯的方向,而是指一种相反的观点。我们所讨论的角度,对于计算机来说,就是弧度。所以,不管你是否喜欢,都要对它有所了解。1弧度约等于 57.2958度。你也许会问“这符合逻辑吗?”确实有其逻辑所在。一个圆,360度,计算出的弧度为 6.2832。仍然没有任何意义?好,想一下圆周率派 Pi(π) 约等于 3.1416,而一个圆(6.2832弧度)就等于2 pi。我们知道 360 度相当于 2 pi,180 度相当于 pi,90 度相当于 pi/2,等等。图 3-2 给出一些常用的弧度制。

图 3-2 弧度与角度
现在,我们先抛开这个话题,你只要知道从现在起我们就要开始使用弧度制了。今后我们会遇到很多用弧度表示度的情况。例如,影片剪辑和sprite影片的rotation属性都要使用角度制,而且会经常使用这个属性。如,一辆汽车需要旋转到运动的方向,如果使用三角学计算运动方向,那么所得到的角度就是弧度制的,而汽车的旋转则需要使用角度制。相反,如果要指定某个对象向某个方向前进,我们就要获得它的旋转(rotation)角度,而这是用角度制表示的,如果要在三角函数中使用它就要把它转换为弧度制。角度制,还应用在滤镜上,如果使用(drop shadow filter)阴影滤镜,来为物体投射45度的阴影,就需要指定其角度而非弧度,不论是在 Flash IED 中还是使用 ActionScript 代码都一样。为什么在一个编程体系里有两种截然不同的制度呢?也许这就是Flash双重性的表现。一方面,这是设计人员的工具,在Flash IDE 中我们拥有所有的绘图和变形工具,可以让我们绘制出漂亮的图形。如果你对一名设计者说把你制作的logo文字旋转一个弧度,我想你肯定会遭白眼。另一方面,Flash 也是一个开发工具,更像一种编程语言,ActionScript用户使用弧度制。总之,不论你是否喜欢,我们都要使用到它们,而且我们需要掌握角度制与弧度制间的相互转换。以下是公式:
弧度(radians) = 角度(degrees) * Math.PI /180
角度(degrees) = 弧度(radians) * 180 / Math.PI
在学习本书的过程中,我们会遇到很多公式。无论哪里,遇到需要记忆的公式时,我都指出来,希望大家能够识记,这里是第一个公式。如果在每次需要使用时,都去查找这些公式,那你不会得到任何现成的代码,这些代码需要你自己敲出来。我使用 ActionScript 写这些公式, 使用 Math.PI 要比使用 pi 或其它字符要好,因为这和你输入时是一致的。
180度大约等于3.14…弧度。换句话讲,半圆为 pi 个弧度,整圆为 2 pi个弧度,一个弧度大概为 57.29…度。

Flash 坐标系
    在我们讨论角度时,就要提到 Flash 坐标系。如果我们习惯于数学坐标系,那么对于Flash坐标系可能会有些晕,因为在这里一切是颠倒(upside down)的。在普通坐标系中,用X表示水平轴,用Y表示垂直轴,Flash 也是一样。当x=0,y=0时,坐标(0,0)通常显示在中心,X为正数时在右边,X为负数时在左边,Y为正数时向上,Y为负数时向下,如图 3-3 所示

图3-3 标准坐标系
然而 Flash 是基于视频屏幕的坐标系,0,0 点为左上角,如图3-4。X值从左向右不断增长, 但Y轴是相反的,正值向下,负值向上。这个系统有其历史根源,与电子枪扫描屏幕建立图像的原理一样,从左到右,从上到下。

图3-4 Flash坐标系
我们可以这样想,建立一个普通的坐标系并可以在里面输入数值,只需要把Y轴颠倒一下,并把屏幕中心迁移到屏幕的左上角即可。
下面我们就来说说角度制。在一般的坐标系中,角度是以逆时针计算的,并以0度为起点向正X轴引一条线,如图3-5所示。

图3-5 普通的角度制
在Flash中是颠倒的,如图3-6所示。角度是顺时针旋转的,顺时针就意味着负角。

图3-6 Flash的角度制
三角形的边
    对于三角形的边,没有太多可说的,但它们都有各自的术语。我们以直角三角形为例,如图3-7所示,每条边都有各自的名称,与90度角相接的两条边称为直角边(legs),相对的边称为斜边,它总是那个最长的边。

图3-7 直角三角形各部分
    刚才说到对边时,说它是与该角不相接的边。说到邻边时,说它是与角相接的边。在很多例子中,都是与其余两个不是90度的角打交道。在这个例子中,我们所说的相邻,是指邻边而不是斜边。在三角形中最有趣的就是角与边的关系,这些关系对于动画制作非常有用,下面就让我们来看看。

三角函数
    ActionScript 拥有一套用于计算不同三角关系的三角函数:正弦,余弦,正切,反正弦,反余弦和反正切。下面我们就开始定义和使用这些函数,而后还会介绍它们在现实中的使用。

正弦(Sine)
下面是三角学的第一个部分。一个角的正弦值等于该角的对边比斜边(当我们说到正弦时,通常指某个角的正弦)。在 ActionScript中,我们使用 Math.sin(angle) 函数来表示。图3-8所示为一个30度角的正弦。对边长为1,斜边长为2,两条边的比为1比2,或记作1/2或0.5。因此,我们可以说30度角的正弦值为0.5,我们可以在Flash中测试一下:
trace(Math.sin(30));

图3-8 角的正弦值为对边/斜边
输出结果为 –0.988031624092862,你能够发现原因吗?我们忘记转换为弧度制了。我感说你以后会常犯这种错误(我也一样),所以一定要小心。以下是正确的写法:
trace(Math.sin(30 * Math.PI / 180));
成功!输出为 0.5
    事实上,我们也许会得到 0.4999…这样的值,这并不是程序的错误,而是由于二进制计算机常以浮点形式表示数值。它这个已经非常接近了,所以我们就认为它等于0.5。我们最好像这样把一个三角形抽象为,角度为30,两条边长为1和2。下面我们把它移到普通坐标系中,不要忘了,Flash坐标系的Y轴向下,角度是顺时针的。所以,对边和角度都是相反的,见图3-9。

图3-9 在Flash坐标系中创建相同的角
因此,比例也变成了-1/2,我们就称它为 -30度角的正弦值。同时,我们把表达式改为:
trace(Math.sin(-30 * Math.PI / 180));
    好的,不会很痛苦吧?下面再来看一个三角函数:余弦。
欢迎光临我的AS教学博客:
http://blog.sina.com.cn/yyy98

TOP

余弦(Cosine)
在Flash中使用Math.cos(angle)就可以计算余弦值,余弦的定义为角的邻边与斜边之比。见图 3-10。

图3-10 角的余弦值为邻边/斜边
图3-10中的角度与图3-9中的相同,这次在图中直接加入了邻边的长度 1.73。请注意这个角是向右的,因为X轴是正向的。角的余弦值为 1.73/2,或 0.865。因此,我们可以说 -30度角的余弦值为 0.865,下面测试一下:
trace(Math.cos(-30 * Math.PI / 180));
    与上次一样,只不过调用的是 Math.cos 函数,这次输出结果为 0.866025403784439,非常接近 0.865。之所以会产生不同,是因为我把邻边的值取整了,真正的长度应该近似于1.73205080756888。如果我们用这个数除以2,就会得到非常接近 -30度的余弦值。到现在为止,我们所说的都是左下方的角。下面来看看右上角,首先,我们需要重新在坐标系中定位该角,这里的坐标系是指 Flash 坐标系,见图3-11。

图3-11 观查对角(opposite angle)
    该角的正弦值为对边与斜边之比,或1.73/2(0.865),余弦值为邻边与斜边之比,1/2(0.5)。基本上,一个角的余弦值等于另一个角的正弦值。虽然我不知道这对于Flash来说有什么用,但请注意它们之间是相互关联的,成比例的。

正切(Tangent)
另一个重要的三角函数是正切,用Flash表示为Math.tan(angle)。它反应对边与邻边的关系见图3-12所示。

图3-12 角的正切值为对边/邻边
两者的比例为 -1/1.73 或 -0.578。直接在Flash中进行验证,会得到更准确的结果:
trace(Math.tan(-30 * Math.PI / 180));
我们得到 –0.577350269189626,证实了我们的计算,在ActionScript中,这个函数并不常用,我们发现使用正弦和余弦的时候要多一些,而正切可被用于制作一些有趣的效果。另外,反正切是非常有用的,我们下面就来看看,并且请大家记住正切函数的比例关系。

反正弦(Arcsine)和反余弦(Arccosine)
与正切相似,反正弦和反余弦在一般的Flash动画中很少使用。然而,我们还是要学习一下它们的用法,实际上就是与正弦和余弦函数的反函数。换言之,就是我们输入一个比例值,返回一个角度值(以弧度表示)。在ActionScript函数中为Math.asin(ratio)和Math.acos(ratio)。下面让我们来测试一下,我们已经知道30度的正弦值为0.5,所以0.5的反正弦值应为30度,检验一下:
trace(Math.asin(0.5) * 180 / Math.PI);
别忘记将结果转换为角度制,才能得到角度制30度,而不是弧度制0.523。我们知道,30度角的余弦值大约为 0.865,下面我们以同样的方法来测试一下:
trace(Math.acos(0.865) * 180 / Math.PI);
    得到结果为 30.1172947473221。如果我们把30度的余弦值输入都更准确,那么所得的结果也会更为精确。怎么样,不难吧?

反正切(Arctangent)
大家可能都猜到了,反正切简单地记就是正切函数的反函数。我们只要输入对边与邻边的比值,就可以得到相应的角度。在Flash中,我们有两个函数可计算反正切值。第一个就是像我们前面介绍过的函数一样 Math.atan(ratio),只需提供对边与邻边的比例值。例如,前面学过 30度角的正切值约为0.577。试一下:
trace(Math.atan(0.577) * 180 / Math.PI);
结果得到一个接近30的值,这样不是非常直观易懂吗,为什么还需要另一个函数呢?下面请看图3-13,让它来回答:

图3-13 四个象限上的角
如图3-13所示,有四个不同的角:A,B,C,D。角A和B,在X轴上为正数,角C和D在X轴上为负数,同样,角A和D在Y轴上为负数,而角B和C在Y轴上为正数。因此,四个内角的比例分别为:
A: –1/2 (–0.5)
B: 1/2 (0.5)
C: 1/ –2 (–0.5)
D: –1/ –2 (0.5)
所以,用对边比上邻边为0.5,我们输入Math.atan(0.5),并转换为角度制,结果大约为 26.57,那么我们所指的是角B还是角D呢?既然两个比例都是0.5那我们无法分辨了,这看似没什么,但对于我们日后的工作确有很大的影响。下面请欢迎 Math.atan2(y,x),这是Flash的另一个反正切函数,它比Math.atan(ratio)要有用得多。实事上,我们只需要学会这种用法就可以了,这个函数中包括两个值:对边长度与邻边长度。有时常会误写成 x,y,请注意应该是 y,x。请看如下示例,输入 Math.atan2(1,2),然后记住这个结果:
trace(Math.atan2(1, 2) * 180 / Math.PI);
输出结果为 26.565051177078,这正是角B的度数。下面输入-1/-2(角D),再来试试:
trace(Math.atan2(-1, -2) * 180 / Math.PI);
出乎意料的结果–153.434948822922.为什么会这样?图3-14能给你解释。

图3-14 一个角的两种表示方法
    从角D自身的底边算,它确实为26.57度,但别忘了 Flash 的角度是从X轴的正半轴顺时针计算的。因此,从Flash 的角度测量,该角被视为-153.43。下面我们就开始在Flash中实践和应用三角学吧。
欢迎光临我的AS教学博客:
http://blog.sina.com.cn/yyy98

TOP

旋转(Rotation)
我们想让一个影片剪辑或sprite通过旋转指向鼠标的位置,这将是个挑战。旋转(rotation)将成为我们工具箱中非常的工具,不仅可以应用于游戏制作,鼠标追踪,界面元件等。事实上,旋转不仅限于鼠标。由于鼠标只需要x,y两个值,我们可以把这个技术用于,将影片剪辑移动到目标位置或其它影片,或是屏幕的中心或角落。
下面我们看一个示例。也可以根据以下步骤或打开文档类 RotateToMouse.as 和 Arrow.as(与本书中其它代码一同在www.friendsofed.com下载),这些是已写好的代码。
首先,我们需要让一些物件旋转,这可以是个在sprite中绘制的箭头(Arrow)。事实上,如果我们要反复应用到这个箭头,可以把它制作成一个类:
package {
import flash.display.Sprite;
public class Arrow extends Sprite {
  public function Arrow() {
   init();
  }
  public function init():void {
   graphics.lineStyle(1,0,1);
   graphics.beginFill(0xffff00);
   graphics.moveTo(-50,-25);
   graphics.lineTo(0,-25);
   graphics.lineTo(0,-50);
   graphics.lineTo(50,0);
   graphics.lineTo(0,50);
   graphics.lineTo(0,25);
   graphics.lineTo(-50,25);
   graphics.lineTo(-50,-25);
   graphics.endFill();
  }
}
}
这里使用到了绘图的API(会在下一章介绍)来绘制箭头。现在,无论何时需要一个箭头,只需写一句 new Arrow()即可,我们可以在图3-15中看到结果。当绘制一些图像并进行旋转时,要注意它的指向,默让地指向右边,X的正半轴,这就是它旋转到0度时的状态。
我们先要创建一个Arrow类的实例,放致于舞台中心,并让它指向鼠标的方向,见图3-16。

图3-15 使用绘图API绘制的箭头

图3-16 下一次需要计算的值
很熟悉吗?与我们之前所讲的三角形相同,只不过多加入了鼠标与箭头的坐标。鼠标的位置只需使用 mouseX 和 mouseY 属性即可读取,同样可以使用x,y属性,可获得箭头的位置。使它们相减,我们就得到了两条边的长度。现在我们只需要使用Math.atan2(dy,dx)就可以求出夹角,然后把它转换为角度制,最后让箭头的rotation属性等于这个夹角。代码如下:
var dx:Number = mouseX - arrow.x;
var dy:Number = mouseY - arrow.y;
var radians:Number = Math.atan2(dy, dx);
arrow.rotation = radians * 180 / Math.PI;
    当然,为了使之形成一个动画,我们需要加入循环。如同前一章提到的,使用事件处理函数将会是最好的方法,请使用 enterFrame 事件。以下是这个完整的文档类:
package {
import flash.display.Sprite;
import flash.events.Event;
public class RotateToMouse extends Sprite {
  private var arrow:Arrow;
  public function RotateToMouse() {
   init();
  }
  private function init():void {
   arrow=new Arrow  ;
   addChild(arrow);
   arrow.x=stage.stageWidth / 2;
   arrow.y=stage.stageHeight / 2;
   addEventListener(Event.ENTER_FRAME,onEnterFrame);
  }
  public function onEnterFrame(event:Event):void {
   var dx:Number=mouseX - arrow.x;
   var dy:Number=mouseY - arrow.y;
   var radians:Number=Math.atan2(dy,dx);
   arrow.rotation=radians * 180 / Math.PI;
  }
}
}
请确认RotateToMouse.as文件与Arrow.as文件在同一目录下,以RotateToMouse作为文档类,并为它创建SWF。怎么样?就像施了魔法一样!假设如果我们没有Math.atan2这个函数,我们就要先通过,dy除以dx求出对边与邻边的比值,然后再写入Math.atan函数。我们就要用Math.atan函数来代替Math.atan2,如下:
var radians = Math.atan(dy / dx);
    试试这种写法,我们马上就会发现问题。如果鼠标位于箭头的左侧,箭头不会指向鼠标,并与之相背离。能说说为什么吗?回到有 A,B,C,D四个角的图(图3-13),不要忘记角A和C拥有相同的比值,角B和D也是一样。这样一来,Flash就无法知道所指的是哪个角,所以它只给出A与或B。如果,鼠标处于D角区域,Flash会回到B角区域并把箭头指向这个角度。毫无疑问,这时Math.ata2的好处就显示出来了,在我们的书中会经常用到。

波形(Wave)
让我们把三角学具体地应用到Flash中。之前,你一定听说过正弦波(Sine wave),你肯定也见过图3-17所示的图形。

图3-17 正弦波形
那么为什么要把正弦函数与正弦图像两个不相干的东西联系到一起呢?如果将0到360度(或着0到2pi)代入到正弦函数中,那么就会得到这个正弦函数。从左到右代表所使用的角度值,而图中y坐标变化,代表这些角的正弦值。图3-18中,标出了一些特殊的角度,我们可以看到 0度的正弦值为0,90度或pi/2的正弦值为1,180度或pi的正弦值又回到0,270度或3/2pi的正弦值为-1,360度的正弦值为又为0。我们用Flash来试一下正弦波形,把以下代码放入文档类的框架中进行测试:
for (var angle:Number = 0; angle < Math.PI * 2; angle += .1) {
trace(Math.sin(angle));
}
    从现在起,我们要开始习惯只使用弧度制。处了使用rotation或其它只使用角度制的属性,我们要开始不去使用角度制。

图3-18 正弦图像值
    在这个例子中,角度从0开始,每次递增0.1直到大于Math.PI*2为止,并输出该角的正弦值。看一下输出结果,我们发现角度是从0开始,增加到1后,开始减小,减少到-1时,再回归至0。我们不会真正准确地到达1或0,因为每次增加0.1,所以永远不会得到pi或pi/2的整数倍。
平滑的上下运动(Smooth up and down motion)
如何使用Math.sin(angle)呢?你是否想让物体上下或前后移动呢?那么你就要用到这个函数。考虑:使用0~1~-1~0的变化来实现这个动画,并且反复地使用这个波形。值域仅仅只是1和-1,把这些数值放大一些,比如100,这样我们就拥有了一个从100到-100的波形,并且连绵不断。在下面这个文档类Bobbing.as 中,使用一个在Ball类中定义的sprite影片,请看代码:
package {
import flash.display.Sprite;
public class Ball extends Sprite {
  private var radius:Number;
  private var color:uint;
  public function Ball(radius:Number=40, color:uint=0xff0000) {
   this.radius = radius;
   this.color = color;
   init();
  }
  public function init():void {
   graphics.beginFill(color);
   graphics.drawCircle(0, 0, radius);
   graphics.endFill();
  }
}
}
    当这个类被实例化后,就能绘制一个圆。我们还可以自行给出圆的半径(radius)和颜色(color)。如果不给的话,就会使用默认的参数半径为40,颜色为红色(这是AS3.0新增的功能)。这个类非常简单,但却非常有用,今后在书中会经常用到,所以请大家掌握它。
    文档类创建一个Ball类的实例,并加入到舞台上,再为它增加一个enterFrame侦听器,这样就可以让小球上下移动了。
package {
import flash.display.Sprite;
import flash.events.Event;
public class Bobbing extends Sprite {
  private var ball:Ball;
  private var angle:Number = 0;
  public function Bobbing() {
   init();
  }
  private function init():void {
   ball = new Ball();
   addChild(ball);
   ball.x = stage.stageWidth / 2;
   addEventListener(Event.ENTER_FRAME, onEnterFrame);
  }
  public function onEnterFrame(event:Event):void {
   ball.y = stage.stageHeight / 2 + Math.sin(angle) * 50;
   angle += .1;
  }
}
}
首先,我们需要创建一个角度属性(angle)初始值为0。
在onEnterFrame方法中,我们使用该角的正弦值并把它扩大50倍,这样,取值的范围就成为50到-50。再在这个值上加舞台高度的一半,我们的值就变为从250到150(设舞台高度为400像素),用这个值作为小球的Y坐标,最后为下一次循环增加0.1个弧度,这样就完成了平滑的上下运动。每一次循环的值都不相同,我们发现如果将0.1变为另一个数值的话,就改变了运动的速度。角度(angle)变化得快慢,与Math.sin从1到-1变化的速度成正比。很明显,改变50这个值,就改变了小球移动的距离,而改变 stage.stageHeight / 2 的值,就改变了小球所围绕的位置。我们可以给出这些抽象的值作为变量,如下(只给出需要改变或增加的部分):
// at the top of the class:
private var angle:Number = 0;
private var centerY:Number = 200;
private var range:Number = 50;
private var speed:Number = 0.1;
// and the handler function:
public function onEnterFrame(event:Event):void {
ball.y = centerY + Math.sin(angle) * range;
angle += speed;
}
    在运动代码中没有使用具体的数值,真是次非常好的练习,我们以后应尽量这样做。
上下直线运动(Linear vertical motion)
在 Wave1.as文件中,加入了上下直线运动,只是为我们制作动画增加一些灵感。
以下是这个文件的代码:
package {
import flash.display.Sprite;
import flash.events.Event;
public class Wave1 extends Sprite {
  private var ball:Ball;
  private var angle:Number = 0;
  private var centerY:Number = 200;
  private var range:Number = 50;
  private var xspeed:Number = 1;
  private var yspeed:Number = .05;
  public function Wave1() {
   init();
  }
  private function init():void {
   ball = new Ball();
   addChild(ball);
   ball.x = 0;
   addEventListener(Event.ENTER_FRAME, onEnterFrame);
  }
  public function onEnterFrame(event:Event):void {
   ball.x += xspeed;
   ball.y = centerY + Math.sin(angle) * range;
   angle += yspeed;
  }
}
}
欢迎光临我的AS教学博客:
http://blog.sina.com.cn/yyy98

TOP

脉冲运动(Pulsing motion)
我们可以用正弦值作为一种工具,不仅用于控制物理位置。在 Pulse.as 文件中,使用一个值来影响小球的缩放大小(scale),实现一个搏动的效果,代码如下:
package {
import flash.display.Sprite;
import flash.events.Event;
public class Pulse extends Sprite {
  private var ball:Ball;
  private var angle:Number = 0;
  private var centerScale:Number = 1;
  private var range:Number = .5;
  private var speed:Number = .1;
  public function Pulse() {
   init();
  }
  private function init():void {
   ball = new Ball();
   addChild(ball);
   ball.x = stage.stageWidth / 2;
   ball.y = stage.stageHeight / 2;
   addEventListener(Event.ENTER_FRAME, onEnterFrame);
  }
  public function onEnterFrame(event:Event):void {
   ball.scaleX = ball.scaleY = centerScale + Math.sin(angle) * range;
   angle += speed;
  }
}
}
    原理是一样的,有中心点(center point: 表示100%的缩放比),范围(range)和一个速度(speed)变量。不仅如此,正弦波还可以应用于alpha,rotation等属性中。

双角波形
再给大家一种思想:比只有一个角要好,设置两套数值,angle1 和 angle2,并为它们增加各自的中心点(center)和速度(speed)。使一个正弦波作为一个属性,另一个正弦波作为另一个属性,比如位置(position)或缩放(scale)。我不敢保证能够得到什么有用的东西,但像这样一来,我们就等于让这些函数自由发挥。
从 Random.as 文档类开始,这里面拥有两个角度(angle),两个速度(speed)和两个中心点(center),将其中一个角度作为球的X坐标,另一个角作为Y坐标。结果就像只虫子在房间里飞,虽然这些数字都是预先定义好的,但结果看起来还是没有规律可言。代码如下:
绘制波形
最后,在 Wave2.as中,我们不再使用小球,转而使用绘图API来绘制正弦波形。代码如下:
package {
import flash.display.Sprite;
import flash.events.Event;
public class Wave2 extends Sprite {
  private var angle:Number = 0;
  private var centerY:Number = 200;
  private var range:Number = 50;
  private var xspeed:Number = 1;
  private var yspeed:Number = .05;
  private var xpos:Number;
  private var ypos:Number;
  public function Wave2() {
   init();
  }
  private function init():void {
   xpos = 0;
   graphics.lineStyle(1, 0, 1);
   graphics.moveTo(0, centerY);
   addEventListener(Event.ENTER_FRAME, onEnterFrame);
  }
  public function onEnterFrame(event:Event):void {
   xpos += xspeed;
   angle += yspeed;
   ypos = centerY + Math.sin(angle) * range;
   graphics.lineTo(xpos, ypos);
  }
}
}
    下一章我们会详细讲述绘图API,大家也应该有兴趣来执行一些这个文件,看看它绘制出的不同的波形。注意,由于Flash的Y轴是反向的,所以绘制出的波形也是颠倒的。
圆(Circle)和椭圆(Ellipse)
现在我们已经掌握了正弦波,下面我们来看看它的表弟,余弦波。与正弦波的形成相同,只不过是使用余弦函数代替了正弦函数而已。如果你还记得前面所说的正弦和余弦是怎样一种相反关系的话,你就能理解,为什么它们的波形相同,只是所处位置不同了。图3-19为余弦波图像:

图3-19 余弦波形
    可见,余弦图像中0度2pi(或360度)时为值都为1,从1开始经过0,-1,0,最后回到1。所以,它与正弦曲线相同,只不过位置发生了一点偏移。

圆形运动
在执行振荡运动时,完全可以使用余弦来代替正弦。实际上,余弦和正弦协同工作时,才能形成一个更加有用的功能:使物体沿圆形运动。如图3-20 所示一个物体在沿圆形移动时的几个点。

图3-20 物体沿圆形运动时的几个点
如图3-20所示,以图中的圆为例,盯住右侧的那条边,开始对它进行旋转,我们发现这条边正在被倒置。这条边的中心是圆心,而它的运动范围就是这个圆的半径。就像在第一个正弦实验中一样,我们可以计算出这条边的长度:角的正弦值乘以半径。在这里,使用正弦函数非常合适,当我们从侧面观察这个圆时,就可以算出 y 的长度——对边的长度。如果我们把这个圆放倒,再来观察它,发现角是在向前向后向左向右移动的。这时,我们可以使用余弦函数计算出 x 的长度——邻边的长度。重要的一点是,两个的夹角都是相同的,而不像 Random.as 那个例子一样,x,y坐标计算时使用了不同的角度。我们只需要用正弦函数计算 y ,用余弦函数计算 x。下面请看ActionScript 代码:
package {
import flash.display.Sprite;
import flash.events.Event;
public class Circle extends Sprite {
  private var ball:Ball;
  private var angle:Number = 0;
  private var centerX:Number = 200;
  private var centerY:Number = 200;
  private var radius:Number = 50;
  private var speed:Number = .1;
  public function Circle() {
   init();
  }
  private function init():void {
   ball = new Ball();
   addChild(ball);
   ball.x = 0;
   addEventListener(Event.ENTER_FRAME, onEnterFrame);
  }
  public function onEnterFrame(event:Event):void {
   ball.x = centerX + Math.sin(angle) * radius;
   ball.y = centerY + Math.cos(angle) * radius;
   angle += speed;
  }
}
}
    大家可以自己写这个例子,也可以打开Circle.as作为文档类,里面的代码已经写好了。执行后发现,获得了一个完美的圆。请注意,上面两个例子中的范围就是指三角形的斜边,还等于圆的半径。完成所有的这些代码都是使用余弦来确定 x 坐标,使用正弦来确定 y 坐标,你应该对他们的关系非常了解了。在Flash中,只要提到 x ,你就应该马上想到余弦,并且还能联想到 y 使用正弦。请在最后这段代码上多花些时间,它将是 ActionScript 动画工具箱中最有用的工具之一。

椭圆运动
有时我们并不需要一个圆,我们也许想要一个椭圆,问题就在于半径。如果让x运动和y运动的大小相同,那么就得到一个圆。如果想得到一个椭圆形,我们只需要在计算x和y位置时使用不同的半径值,我称它们为X半径(radiusX)和Y半径(radiusY)。从严格的几何观点来看,使用这两个名称实在不怎么好,但是它们确实非常简单易懂,也非常好记非常直观,所以我还是坚持使用这两个变量名。下面看看它们是如何配合的,见Oval.as:
package {
import flash.display.Sprite;
import flash.events.Event;
public class Oval extends Sprite {
  private var ball:Ball;
  private var angle:Number = 0;
  private var centerX:Number = 200;
  private var centerY:Number = 200;
  private var radiusX:Number = 200;
  private var radiusY:Number = 100;
  private var speed:Number = .1;
  public function Oval() {
   init();
  }
  private function init():void {
   ball = new Ball();
   addChild(ball);
   ball.x = 0;
   addEventListener(Event.ENTER_FRAME, onEnterFrame);
  }
  public function onEnterFrame(event:Event):void {
   ball.x = centerX + Math.sin(angle) * radiusX;
   ball.y = centerY + Math.cos(angle) * radiusY;
   angle += speed;
  }
}
}
    这里,radiusX为200,意味着小球在距离centerX的200个像素内左右运动。 radiusY为100,意味着小球上下运动的范围只有100像素。这样,我们就得到了一个不匀称的圆,不是个圆而是个椭圆。

勾股定理(Pythagorean Theorem)
最后,介绍一下勾股定理。虽然并不能算是三角学中正式的一部分,但是它与我们这个学科还是有一些关系并且还涉及一个我们将来会经常使用的公式。所以在这里介绍它非常合适。
勾股定理是很久以前一个希腊人发明的,这是节历史课。简单地说,这个定理是说 A的平方 + B的平方 = C的平方,听起来好像是儿歌,如果大家之前学过这个定理,那么交流起来效果最好。下面让我们来深入探讨一下,另一种对该定理的叙述是将直角三角形的两条直角边的平方和等于斜边的平方,这句话真正说到点子上了。请看图3-21所示直角三角形。
    两条直角边A和B长度为3和4。斜边C长度为5。毕达哥拉斯(Pythagoras)先生告诉我们 A2 + B2 = C2。我们加入数字来检验一下,32+42 = 52,计算出 9 + 16 = 25。是的,非常正确。

图3-21 一个直角三角形
    如果我们知道了这三个长度,勾股定理只不过说的是一种有趣的关系。如果我们知道其中两个长度,那么勾股定理就派上用场了,可以用它很快地求出第三条边的长度。在Flash中,最常见的情况是我们知道两条直角边的长度要求出斜边的长度。比如,我们想求出两点间的距离。

附件

3-22.jpg (5.7 KB)

2008-2-23 01:05

3-22.jpg

3-23.jpg (8.08 KB)

2008-2-23 01:05

3-23.jpg

欢迎光临我的AS教学博客:
http://blog.sina.com.cn/yyy98

TOP

两点间距离
假设在舞台上有两个sprite影片,我们想求出它们之间的距离。这是勾股定理在Flash中最为常见的用法。我们样怎么实现呢?已知两个sprite的x,y位置,把第一个影片的位置称x1,y1,另一个影片的位置称x2,y2,见图3-22。

图3-22 两个物体间的距离是多少?
如果你在本章中看了太多的直角三角形,你就会把图3-22的内容看成一个直角三角形,而那条距离线(distance)就是三角形的斜边。在图3-23中,加入了这个三角形并填入了数字。

图3-23 变成一个直角三角形
dx为两个影片之间的x轴,dy为它们之间的y轴。用x1减x2很容易就得到了dx的值:58 – 50 = 8,同样,用y1-y2等于6得到dy的值。现在使用勾股定理,将dx,dy的平方相加,就得到了距离(distance)的平方。换言之,62 + 82 = dist2,相当于 36 + 64,或100 = dist2。基础代数学讲过可以通过开平方把它转化为 = dist。这样一来,我们就可以得出两个影片之间的距离为 10。
现在,我们把它抽像成一个公式,这样的话,我们今后再遇到同样的问题,直接可以使用这个公式了。有两个位置 x1,y1 和 x2,y2,先计算出 x 的距离和 y 的距离,然后求出它们的平方和,最后求出平方根,下面请看 ActionScript 写法:
dx = x2 – x1;
dy = y2 – y1;
dist = Math.sqrt(dx*dx + dy*dy);
请特别注意这些代码,它们将是我们工具箱中又一个最大的工具。前两句是获得x,y轴上的距离。如果我们只想测一下距离,而不是使用dx和dy计算夹角的话,那么是否使用x2-x1都无所谓,最后的结果 dist 永远为正数。最后一句分为三个步骤:计算每个值的平方,把它们相加,最后求出平方根。为了能看清楚,我们可以分步写。一但你熟悉了这句,那么它对你来说就非常显而易见了,看到它后就会想到“勾股定理”,之后你会发现自己真的被洗脑了。下面我们就来实践一下,下一个文档类是 Distance.as,创建两个sprite影片,随机摆放,最后计算出它们之间的距离。
package {
import flash.display.Sprite;
public class Distance extends Sprite {
  public function Distance() {
   init();
  }
  private function init():void {
   var sprite1:Sprite = new Sprite();
   addChild(sprite1);
   sprite1.graphics.beginFill(0x000000);
   sprite1.graphics.drawRect(-2, -2, 4, 4);
   sprite1.graphics.endFill();
   sprite1.x = Math.random() * stage.stageWidth;
   sprite1.y = Math.random() * stage.stageHeight;
   var sprite2:Sprite = new Sprite();
   addChild(sprite2);
   sprite2.graphics.beginFill(0xff0000);
   sprite2.graphics.drawRect(-2, -2, 4, 4);
   sprite2.graphics.endFill();
   sprite2.x = Math.random() * stage.stageWidth;
   sprite2.y = Math.random() * stage.stageHeight;
   var dx:Number = sprite1.x - sprite2.x;
   var dy:Number = sprite1.y - sprite2.y;
   var dist:Number = Math.sqrt(dx * dx + dy * dy);
   trace(dist);
  }
}
}
编译执行这个动画,我们就得到了两个影片之间的距离。每次执行,两个影片的位置都会不同。不论它们处于什么位置,我们都可以获得距离的值且一定是正数。有趣吧,但是还不够动态,下面这个示例,我们可以实时地获得影片的距离,请试一下这个文档类,MouseDistance.as:
package {
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.text.TextField;
public class MouseDistance extends Sprite {
  private var sprite1:Sprite;
  private var textField:TextField;
  public function MouseDistance() {
   init();
  }
  private function init():void {
   sprite1 = new Sprite();
   addChild(sprite1);
   sprite1.graphics.beginFill(0x000000);
   sprite1.graphics.drawRect(-2, -2, 4, 4);
   sprite1.graphics.endFill();
   sprite1.x = stage.stageWidth / 2;
   sprite1.y = stage.stageHeight / 2;
   textField = new TextField();
   addChild(textField);
   stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
  }
  public function onMouseMove(event:MouseEvent):void {
   graphics.clear();
   graphics.lineStyle(1, 0, 1);
   graphics.moveTo(sprite1.x, sprite1.y);
   graphics.lineTo(mouseX, mouseY);
   var dx:Number = sprite1.x - mouseX;
   var dy:Number = sprite1.y - mouseY;
   var dist:Number = Math.sqrt(dx * dx + dy * dy);
   textField.text = dist.toString();
  }
}
}
在这里dx和dx的值是用sprite1的位置减去当前鼠标位置得出的,dist的值放入一个文本框中显示,并在影片和鼠标之间绘制一条线(在下一章绘图API中将会学到)。最后,将所有这些放到处理函数onMouseMove中,每次鼠标移动时进行刷新。测试一下这个文件,并移动鼠标,鼠标与影片剪辑间会联接上一条线,并实时读取线的长度。
在后面的章节中,当我们学到碰撞检测(collision detection)时,我们会发现内置的碰撞检测(hit testing)方法所存在的不足,而后会看到任何使用勾股定理公式完成基于距离(distance-based)碰撞检测方法。它也非常适合用于计算重力或弹力等,这些力的大小与两个物体之间的距离成正比。
欢迎光临我的AS教学博客:
http://blog.sina.com.cn/yyy98

TOP

本章重要公式
我们现在已经有了一个全新的工具箱,同时里面也有了不少工具,全部所有的工具将会在第19章列出,那么让我们看看现在都有了哪些工具。注意,这些公式要尽量地抽象和简化,里面不包括数据类型和变量定义,在类中使用这些公式时,是否使用这些给出的句型取决于你。

基本三角函数的计算:
角的正弦值 = 对边 / 斜边
角的余弦值 = 邻边 / 斜边
角的正切值 = 对边 / 邻边

角度制与弧度制的相互转换:
弧度 = 角度 * Math.PI / 180
角度 = 弧度 * Math.PI / 180

向鼠标旋转(或向某点旋转)
// substitute mouseX, mouseY with the x, y point to rotate to
dx = mouseX - sprite.x;
dy = mouseY - sprite.y;
sprite.rotation = Math.atan2(dy, dx) * 180 / Math.PI;

创建波形
// assign value to x, y or other property of sprite or movie clip,
// use as drawing coordinates, etc.
public function onEnterFrame(event:Event){
value = center + Math.sin(angle) * range;
angle += speed;
}

创建圆形
// assign position to x and y of sprite or movie clip,
// use as drawing coordinates, etc.
public function onEnterFrame(event:Event){
xposition = centerX + Math.cos(angle) * radius;
yposition = centerY + Math.sin(angle) * radius;
angle += speed;
}

创建椭圆
// assign position to x and y of sprite or movie clip,
// use as drawing coordinates, etc.
public function onEnterFrame(event:Event){
xposition = centerX + Math.cos(angle) * radiusX;
yposition = centerY + Math.sin(angle) * radiusY;
angle += speed;
}

计算两点间距离
// points are x1, y1 and x2, y2
// can be sprite / movie clip positions, mouse coordinates, etc.
dx = x2 – x1;
dy = y2 – y1;
dist = Math.sqrt(dx*dx + dy*dy);
欢迎光临我的AS教学博客:
http://blog.sina.com.cn/yyy98

TOP

1、代表闪友(我猜他们也一定想说)感谢楼主分享;
2、期待楼主坚持翻译完整本;
3、能否同时提供.doc文档,便于学习。
4、一直在关注楼主,也会继续关注。

TOP

楼主辛苦了
ckhxlt.blog.163.com

TOP

看过英文版的这个书,感觉很不错,感谢楼主的努力和分享。

TOP

TOP

感谢大家支持!!!
欢迎光临我的AS教学博客:
http://blog.sina.com.cn/yyy98

TOP

绝对支持
没有你所不能,只有你所不想

TOP

哪位帮帮忙

有人能指点下AS不?
地图上   “移动”按钮
on (press) {
        stopDrag();
        removeMovieClip(canvas_mc);
        this.isDrawing = false;
        s = 0;
        k2 = 0;
}
on (release) {
        s = 1;
        move();
}
怎么加个限制让它不能移出图的范围? 谢谢了。俺是个菜鸟,请大虾把完整的代码写上~~~多谢了~~~

TOP

非常感谢楼主,你辛苦了

TOP

学习一下

TOP

谢谢,好好地学学,几乎全忘光了。

TOP