Flash學習筆記之三級函數

 
看文章
   
第三章 三角學應用Ⅰ[轉]
2010-03-22 18:25
第三章 三角學應用
從這一章開始,我們將學習三角學,並在第五章開始應用到動畫技術中,其實在下一章的繪圖技術中就會接觸到。如果你已經對三角學有所瞭解或渴望學習動畫方面的知識,那麼可以跳過開始這部分,待日後遇到不懂的問題時,再回來學習。我們用到的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));
好的,不會很痛苦吧?下面再來看一個三角函數:餘弦。


餘弦(Cosine)
在 Flash 中,使用 Math.cos(angle) 就可以計算餘弦值,餘弦的定義爲角的鄰邊與斜邊之比。見圖 3-10。


圖3-10 角的餘弦值爲鄰邊/斜邊
圖3-10中的角度與圖3-9中的相同,這次在圖中直接加入了鄰邊的長度 1.73。角的餘弦值爲 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度的餘弦值。到現在爲止,我們所說的都是左下方的角(degrees)。下面來看看右上方的角,首先,需要重新在座標系中定位該角,這裏的座標系是指 Flash 座標系,見圖3-11。


圖3-11 觀查對角(opposite angle)
該角的正弦值爲對邊與斜邊之比,或1.73/2(0.865),餘弦值爲鄰邊與斜邊之比,1/2(0.5)。因此就得出,一個角的餘弦值等於另一個角的正弦值,請注意它們之間是相互關聯,成比的。


正切(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 中實踐和應用三角學了。


旋轉(Rotation)
我們想讓一個影片剪輯或 Sprite 影片通過旋轉來指向鼠標的位置,這將是個挑戰。旋轉(rotation)將成爲我們工具箱中非常的工具,可以應用於遊戲製作,鼠標追蹤,界面設計等。
下面看一個示例。也可以根據以下步驟或打開文檔類 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.atan2 的好處就顯示出來了,書中會經常用到這個函數。

波形
大家肯定聽說過正弦波,也一定見過圖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的整數倍。


平滑的上下運動
如何使用 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;

}
在運動代碼中沒有使用具體的數值,真是次非常好的練習,以後應儘量這樣做。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章