遊戲數學:三角函數基礎 – 正弦與餘弦

原始檔案與未來教學更新資訊可於Patreon取得
您可於Twitter上追蹤我

本文屬於遊戲數學系列文

Here is the original English post.
本文之英文原文在此

大綱

三角函數為遊戲開發所用到的數學中,非常基礎的工具。也是我選擇它作為本系列首篇教學主題的原因。能夠掌握三角函數的要領,對解決遊戲開發所會遇到的眾多問題非常有幫助。

你將可透過本教學學會:

  • 兩個基礎三角函數的幾何意義:正弦函數與餘弦函數
  • 比較兩個不同的角單位:角度與弳度
  • 正弦與餘弦的基本特性
  • 如何使物體沿著圓形移動與排列:
  • 如何使物體沿著螺旋路線移動:
  • 如何製作簡諧運動效果:
  • 如何製作阻尼彈簧運動效果:
  • 如何製作鐘擺運動效果:
  • 如何製作漂浮運動效果:

正弦函數與餘弦函數的幾何意義

讓我們先來看看單位圓,即半徑為1且中心位於原點的圓。

選一個位於此圓上的點P,連接此點與原點的線段與X軸(+X方向)形成一個夾角\theta (theta).

這正是正弦(sin\theta – 唸做”sine of theta”)與餘弦(\cos\theta – 唸做”cosine of theta”)這兩個基本三角函數的幾何意義,點P的座標(X, Y)即為(cos\theta, sine\theta)

換句話說,\sin\theta\cos\theta分別為位於單位圓上一點之Y與X座標,\theta則為X軸(+X方向)與連接此點和圓點的線段之夾角。

\sin\cos是函數,所以正式寫法理應含有圍在參數兩端的括弧:\sin(\theta)\cos(\theta),不過很多人和文獻都省略那些括弧而直接寫成\sin\theta\cos\theta

\sin\theta\cos\theta為接收單一參數的函數,並且其運算結果總是介於-1與1之間。仔細想想的話,這很合理,因為一個位於單位圓上的點,其X與Y座標不可能超出[-1, 1]這個範圍。

若角\theta隨著時間以固定速度增加大小,我們便能以該點之X與Y座標針對時間作圖。

若把兩個座標的角對時間作圖並排比較,就能觀察到它們基本上是同一個波浪形狀的週期函數,只是位置相差四分之一週期。

這些函數的週期是360^\circ,也就是函數值每360^\circ循環一次。舉例來說,\sin450^\circ\sin90^\circ的結果相同,因為比90^\circ旋轉超出360^\circ基本上還是得到90^\circ這個角度。

角度與弳度

傳入三角函數的角參數可以是此兩種單位之一:角度(degree)與弳度(radian),弳度又稱弧度,我在校時學的名稱是弳度,所以接下來都將使用弳度稱呼。大部分的人都很孰悉角度符號,如直角90度寫成90^\circ。注意\sin90^\circ\sin90其實是不一樣的,當沒有寫出角度符號時,角的單位不是角度,而是弳度

180等同於\pi (pi)\pi則為著名的圓周率,其代表”圓的周長與直弳的比率”,值大約為3.14。所以1弳大約是\frac{180^\circ}{\pi} \approx 57.3^\circ,接近60^\circ。讓我們來做個簡單的驗證,用工程計算機(角單位設為弳度)輸入\sin1可得到約0.84,非常接近\sin60^\circ \approx 0.87

以下為一些常見的角度與弳度的對應值:

    \begin{alignat*} \ 30^\circ &= \frac{\pi}{6} \hspace{10 mm} &45^\circ &= \frac{\pi}{4} \hspace{10 mm} &60^\circ &= \frac{\pi}{3} \\ \ 90^\circ &= \frac{\pi}{2} \hspace{10 mm} &180 ^\circ &= \pi \hspace{10 mm} &360^\circ &= 2\pi \\ \end{alignat*}

使用Unity開發時,呼叫\sin\cos的函式分別為 Mathf.SinMathf.Cos。請注意此兩個函式的參數單位為弳度,所以如果要計算\cos45^\circ,不要寫成:

// 這其實是將45弳傳入餘弦函數!
float cos45Deg = Mathf.Cos(45.0f);

45弳大約是2578^\circ,對其除以360^\circ取餘數及得到58^\circ,此為2578^\circ的等同角,並與45^\circ不同。

正確的寫法是:

// 將角度轉換成弳度
float cos45Deg = Mathf.Cos(45.0f * Mathf.PI / 180.0f);

或者藉由轉換常數來做角度與弳度的換算:

float cos45Deg = Mathf.Cos(45.0f * Mathf.Deg2Rad);

使用Unity編輯器這類工具時,對使用者來說角度單位比弳度單位還友善,因為大部分的人可以馬上想像45^\circ大概是多少。然而在推導數學與撰寫程式時,不少人(包括我)仍然偏好使用弳度單位。

使用弳度單位的其中一個好處,是能簡化弧長計算。假設要計算一個對應角為30度( \frac{\pi}{6}弳)、半徑為2的弧長:

若使用角度單位,必須先用半徑\times 2 \pi這個公式計算出完整的圓周長,然後把圓周長乘以30^\circ360^\circ的比例:

     \begin{flalign*} arc &= radius \times 2\pi \times \frac{30^\circ}{360^\circ} \\ &= 2 \times 2\pi \times \frac{1}{12} \\ &= \frac{\pi}{3} \\ \end{flalign*}

使用弳度單位的話,弧長公式就是單純的半徑\times弳度

     \begin{flalign*} arc &= radius \times \frac{\pi}{6} \\ &= 2 \times \frac{\pi}{6} \\ &= \frac{\pi}{3} \\ \end{flalign*}

進一步觀察,便能注意到使用圓周長公式可很容易驗證弳度的弧長公式。一個圓基本上就是對應角為2\pi弳的弧,使用弳度公式可以得出此弧長為半徑\times 2\pi,與圓周長公式一模一樣。

正弦與餘弦的基本特性

現在讓我們來看看一些對未來數學推導很有用的正弦與餘弦的特性。

單位圓上的一點之座標為(cos\theta, sin\theta),不管\theta的值為何,此點與原點的距離永遠是1。
畢氏定理(Pythagorean Theorem)說明,此點座標(X, Y)與原點的距離為\sqrt{X^2 + Y^2},於是我們可以得到以下恆等式:

     \begin{flalign*} \sin^2\theta + \cos^2\theta = 1 \\ \end{flalign*}

\sin\theta\cos\theta的平方分別寫成\sin^2\theta\cos^2\theta,一般人不寫成(sin(\theta))^2(cos(\theta))^2可能是因為太懶了吧。

先前展示過正弦與餘弦的並排作圖比較:

可以觀察到餘弦曲線基本上就是正弦曲線往左移90度(\frac{\pi}{2}弳),這表示以下恆等式可用於在兩函數之間轉換:

     \begin{flalign*} \sin\theta &= \cos(\theta - \frac{\pi}{2}) \\ \cos\theta &= \sin(\theta + \frac{\pi}{2}) \\ \end{flalign*}

沿著圓形與螺旋線移動

現在我們知道(cos\theta, sin\theta)是位於單位圓周上之一點2D座標,便可以開始使用Unity讓物件沿著圓形移動了。

以下程式碼可使一個物件以固定速率沿著圓周運動:

obj.transform.position = 
  new Vector3
  (
    Radius * Mathf.Cos(Rate * Time.time), 
    Radius * Mathf.Sin(Rate * Time.time), 
    0.0f
  );

以下程式碼則可使12個物件以固定速率沿著圓周運動,並且使各物件之間沿著圓周的間距相等:

float baseAngle = Rate * Time.time + angleOffset;
for (int i = 0; i < 12; ++i)
{
  float angleOffset = 2.0f * Mathf.PI * i / 12.0f;
  aObj[i].transform.position = 
    new Vector3
    (
      Radius * Mathf.Cos(baseAngle + angleOffset), 
      Radius * Mathf.Sin(baseAngle + angleOffset), 
      0.0f
    );
}

將沿圓周移動與Z軸移動結合,便可使物件沿著3D螺旋線移動:

obj.transform.position = 
  new Vector3
  (
    Radius * Mathf.Cos(Rate * Time.time),
    Radius * Mathf.Sin(Rate * Time.time),
    ZSpeed * Time.time
  );

簡諧運動

複習一下上面已經看過的餘弦函數之作圖:

如果把物件的X座標設成餘弦函數值會如何?

float x = Mathf.Cos(Rate * Time.time);
obj.transform.position = Vector3(x, 0.0f, 0.0f);

我們將會得到此效果:

這種依正弦曲線/正弦波(sinusoid / sine wave)的運動模式稱為簡諧運動(simple harmonic motion, 簡稱S.H.M.)。

由於上面程式中使用的是餘弦函數,物件的初始X座標是\cos0=1。若改用正弦函數,物件的初始X座標則是\sin0=0

傳入正弦與餘弦函數的角度參數稱為相位(phase)。當傳入三角函數的相位為時間的倍數時,大多數人會將此模式寫成\sin \omega tt是時間,時間的倍數\omega (omega)稱作角速度(單位為弳/秒)。舉例來說,\sin2\pi t將產生振盪週期為1秒的簡諧運動。

那麼將簡 諧 運動乘上個指數遞減的係數會如何?

float s = Mathf.Pow(0.5f, Decay * Time.time);
float x = Mathf.Cos(Rate * Time.time);
obj.transform.position = Vector3(s * x, 0.0f, 0.0f);

物件將會改行受阻尼彈簧運動(damped spring motion):

鐘擺運動

若將沿圓形運動的物件之角度設為正弦曲線又會如何?

float baseAngle = 1.5f * Mathf.PI; // 270度
float halfAngleRange = 0.25f * mathf.PI; // 45度
float c = Mathf.Cos(Rate * Time.time);
float angle = halfAngleRange * c + baseAngle;
obj.transform.position = 
  new Vector3
  (
    Radius * Mathf.Cos(angle), 
    Radius * Mathf.Sin(angle), 
    0.0f
  );

這時物件將會進行鐘擺運動(pendulum motion):

可以想成是沿圓形運動的角度值本身進行簡 諧 運動。

漂浮運動

再來追加一個範例。這是幽浮兔,她是我的Unity彈彈特效工具包Boing Kit範例中的角色。

來試著將錯開的簡諧運動分別套用到她的X、Y、Z座標:

Vector3 hover = 
  new Vector3
  (
    RadiusX * Mathf.Sin(RateX * Time.time + OffsetX), 
    RadiusY * Mathf.Sin(RateY * Time.time + OffsetY), 
    RadiusZ * Mathf.Sin(RateZ * Time.time + OffsetZ)
  );

obj.transform.position = basePosition + hover;

如次便可產生漂浮運動(hover motion)的效果:

漂浮運動的位移可以進一步運來計算傾斜角度。這超出本文的主題範圍,所以我就僅列出程式碼與結果:

obj.transform.rotation = 
  baseRotation 
  * Quaternion.FromToRotation
    (
      Vector3.up, 
      -hover + 3.0f * Vector3.up
    );

總結

本教學結束了!

我們認識到了\sin\theta\cos\theta的幾何意義,可表示為於單位圓上的一點之座標。然後,我們了解了兩種不同的角單位:角度與弳度。最後,我們學會了如何使物體沿圓形與螺旋線運動、進行簡諧運動、受阻尼彈簧運動、鐘擺運動、與漂浮運動。

我希望這篇教學能幫助你更加了解兩個基本三角函數:正弦函數與餘弦函數。

下一篇教學,我將介紹另一個基本三角函數:正切函數,並且會介紹更多這三個三角函數的應用。

我們下篇教學再見!

若您喜歡這篇教學,請考慮到Patreon支持我。感謝!

About Allen Chou

Physics / Graphics / Procedural Animation / Visuals
This entry was posted in C#, Gamedev, Math, Programming, Unity and tagged , , , , , , , . Bookmark the permalink.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.