旋转(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;
}
}
}