對遊戲開發者而言,應該滿熟悉物理引擎的使用,現今已經有不少優秀的物理引擎,有必要自造嗎?當然,可以有各種理由,像是《電玩物理學》中,就談到:「開發遊戲時需要用到某項物理知識時,就將其納入遊戲」、「通用物理引擎的效能可能還會比專用物理引擎差,因為後者是針對所要模擬的對象開發出來的」。

對我來說,是因為看到3D列印的同好,運用了粒子系統、彈簧、反向重力等物理模擬,將方形迷宮逐步展開為一棵樹,雖然我知道迷宮本身就是個樹狀結構,不過這樣的展示過程還是讓我驚奇不已。

之後該位同好又示範了更多物理引擎來做出各種有趣的迷宮,不禁讓我也想試著用OpenSCAD實現看看,畢竟OpenSCAD本身也有製作動畫的功能,問題在於他是用Processing搭配物理引擎做的,然而如果我想在OpenSCAD中玩,代表我須靠自己搞出基本的物理引擎,該入坑嗎?這坑感覺很深啊!

物理引擎的入門資源

在試著理解如何自造物理引擎的過程中,《電玩物理學》看似是個可參考的資源,不過,它比較像是概論,幫你複習一下電玩常用的力學基礎,談談剛體模擬時的積分模型、碰撞、旋轉、連接等,以及物理引擎的基本構成模組等,其中涉及不少公式的導證,然而,程式碼方面的輔助說明少了一些。

以我自造dotSCAD的經驗而言,理解公式的來源確實是必要的,不過我更想知道的是:以程式實現時該注意哪些細節?例如,力該如何表示?如何將力套用到一個物體?如果環境中有多個力的來源時,彼此之間該怎麼互動?如何分離力的計算與力的呈現等問題。

如果你同我一樣有著以上的疑問,《The Nature of Code》會是個比較合適的資源,可以在網路上免費閱讀或付費購買紙本,當中一到四章從力到粒子系統,都使用了範例逐步實現,第五章談到了如何使用物理引擎(Box2D)。

《The Nature of Code》是本2012年出版的書,範例使用Processing實現(也就是Java),不過,最近作者在YouTube錄製了〈The Nature of Code 2〉系列的影片,重新以p5.js(也就是Processing的JavaScript移植版)實現了原書的範例,如果想在網頁上展現一些物理模擬,是非常值得參考的資源。

物理模擬的基礎

在程式中進行物理模擬,自然要去接觸一些座標等計算,因而物理引擎會涉及一些圖學基礎,例如,座標系統的轉換、旋轉公式、向量計算等,這並不是為了繪圖,物理引擎往往是與呈現分離,你只是以物理引擎計算相關資料,取得結果之後,再以想要的方式呈現。

物理模擬可以從單一個物體開始,物體可以僅涉及「位置」、「速度」、「加速度」,這麼一來,就可以模擬簡單的物體移動。舉例來說,若物體是a,position是其位置,speed是速度,acceleration是加速度,那麼,下一個畫面的物體位置就是:

a.speed += a.acceleration
a.position += a.speed

用數值積分方式來解運動方程式是最基本的概念,上式是尤拉法(the Euler method),為最基本的積分方法,雖然不夠精確,容易有誤差,然而易於學習與實作,作為理解物體移動模擬是個不錯的開始,若想認識一下改良方式,可參考《電玩物理學》第七章〈即時模擬〉的內容。

由於速度與加速度都有方向,所以,speed、acceleration會以向量表示。因為向量有方向,而position也會以向量表示,因為常會需要從兩個物體的位置計算出法向量之類的需求,那麼,力呢?力有各式各樣的來源,我們可以設計為各個獨立的物件,而對於物體本身來說,我們可以提供一個applyForce方法,接受各種的力的物件。

因為物體本身只涉及position、speed,以及acceleration,而力要怎麼改變它們呢?根據牛頓第二定律F=M*A,其中F是力,A是加速度,M是代表質量的純量,F與A是向量,因此A=F/M,你的物體需要有質量屬性mass,applyForce若接受force,applyForce可以實現如下:

acceleration = force / mass;
a.acceleration += acceleration;

你可以多次呼叫applyForce,將各種力換算為加速度後累加,得到物體最後的加速度,之後就可以用來計算物體的位置,因此力的設計就可以獨立於物體之外,你可以將重力、摩擦力、彈力等獨立設計,或者使用多個物體來組成粒子系統,這些就是《The Nature of Code》一到四章在討論的內容。

碰撞偵測的基礎

《The Nature of Code》第五章之後,使用了物理引擎Box2D的Java實現,如果沒使用過物理引擎,這裡的內容,是可以對物理引擎的世界觀有個基本認識,而且很簡單地就獲得了物體碰撞偵測的功能,只不過到底來說,碰撞偵測是怎麼實現的呢?畢竟物體可能會有各式各樣的外形!

〈How to Create a Custom Physics Engine〉的系列文件中,第一篇就談到碰撞偵測的基本原理(之後才談力的套用),最基本的就是軸對齊包圍盒(Axis-aligned Bounding Box,AABB)碰撞偵測,簡單來說,就是偵測矩形是否碰撞,這只要以矩形的最小邊與另一矩形的最大邊來進行判斷,就可以了。

問題在於AABB只能處理矩形,而且無法處理旋轉後的情況,這時就要認識分離軸理論(Separating Axis Theorem,SAT),也就是若能在兩個物體間,找到一條可分離兩者的直線,也就是分離軸,而且,這兩個物體之間沒有發生碰撞。

比較生動的比喻是,拿著光以各種角度照射兩個物體,如果有某個角度,光可以投射至牆上,表示兩個物體沒有重疊;換個方式來說,只要各種角度下,物體的影子投影至牆上都不重疊,就表示沒有碰撞,也就是說,這種情況下,就可以進一步套用AABB碰撞偵測。

問題就在於如何找到分離軸,每個角度都要找嗎?就電腦而言,物體的形狀是由頂點組成,將頂點連接就能組成了多邊形,我們可以把每個邊先當成分離軸,求得的法向量來當成投影軸,然後將投影軸上的影子邊界,以AABB碰撞偵測都檢查一遍,就可以了。

SAT的原理比較容易理解,然而缺點是多邊形物體的邊數若增加,效率就會下降,而且只能檢測凸多邊形(可以透過將凹多邊形分解為多個凸多邊形來解決);如果想要看看如何透過程式碼來實現以上的概念,我們可以參考〈遊戲中的碰撞檢測〉,其中使用了JavaScript實現SAT。

更多的資源

認識物理引擎背後的實現原理,從中理解程式面上需要做的考量,以及各種方法的優缺點,其實是滿有趣的摸索過程,我們從中也可以累積閱讀開放原始碼物理引擎的基礎能力。

如果你也曾經想要試著自造物理引擎,然而不得其門而入,可以從以上所談及的資源與觀念來試著入門,若需要更多的學習資源,可以參考〈Game physics from scratch〉,當中還有十幾篇文件。

專欄作者

熱門新聞

Advertisement