現實世界裡我們對於是否碰撞的判斷可以說極其容易而且準確,比如下圖。在二進位制的世界裡,一切就沒這麼直觀了。
gjk(gilbert-johnson-keerthi distance algorithm)
gjk 就是此次要實現的碰撞檢測演算法。如果對碰撞演算法有過了解的話,大概率聽過另乙個碰撞檢測演算法 sat(separating axis theorem)。
gjk 相較於 sat 有什麼優點嗎?快簡單
實際上就我目前了解的碰撞檢測演算法,應用物件都是凸多邊形(convex polygon)。如果不是凸多邊形,問題也不大,可以事先分割。
minkowski difference
由於不知道 min 到底是「明」還是「閔」,所以下面都用 md 表示了。
md 是 gjk 演算法的理論基礎。那麼到底什麼是 md ?
假設有兩個凸多邊形:
s1 =[,,,]
s2 =[,,]
那麼它們的位置看起應該像下圖這樣。
md 就是 s1 與 s2 所有點的差形成的集合。
md(s1, s2):
s3 =for p1 in s1:for p2 in s2:
s3.push(p1 - p2)return s3
md(s1, s2)=>[,,,,,,,,,,,]
這些點的布局如下圖所示:
關鍵的地方來了。首先要介紹乙個新的概念叫 convex hull。
convex hull 是包含點集的最小凸多邊形。拿上面的例子來說:
鋪墊了這麼久,現在可以說結論了。
我們把 s1 - s2 點集形成的 convex hull 命名為 s3。如果 s3 包含點 (0, 0),那麼 s1 和 s2 發生碰撞。
有沒有覺得很簡單?對,原理就是這麼簡單。
functionwrap(points)for(const p of points)let i =0, end
while(true)}
i +=1
current = end
if(end.x === hull[0].x && end.y === hull[0].y)break}return hull
最後就是判斷點是否在多邊形內的演算法了,可以看我之前的文章。
互動示例
思考有小夥伴不禁要問:這樣的巢狀迴圈真的比 sat 快?確實,上面的實現並不是真正意義上的 gjk 演算法。但是核心思想是一樣的:
如果兩個凸多邊形的 minkowski difference 所形成的 convex hull 包含點 (0, 0),那麼這兩個凸多邊形相交。
怎麼優化這個實現呢?
我們只需要盡早的從已知的條件裡判斷出是否包含原點即可。
真正的 gjk 演算法用了乙個很取巧的方式來減少迴圈次數,而且效果很理想。當然這也是下篇文章裡的內容了。
感興趣的小夥伴也可以從下面的參考資料裡先嘗試一波。
參考資料
國際慣例先放圖。
gjk 是怎麼快速判斷出這兩個圖形是否碰撞的呢?
support function
support function 用來計算凸體在給定方向上的最遠點。什麼意思呢?
圖示中的例子帶入,可以得到 (9, 6) = (0, 4) - (-9, -2),正是圖二三角形的乙個頂點。
將方向取反再呼叫 getsupport,我們可以得到 (-1, -2) = (-6, 0) - (-5, 2),也是圖二三角形的乙個頂點。
這樣做的意義是什麼呢?因為可以確保我們所取的兩個點跨度足夠大,有更大的概率包含原點,減少迴圈次數。
那麼問題來了:
初始給定的方向是怎麼來的?
已經獲取了兩個點,那麼第三個點如何確定呢?
通過 a(9, 6),b(-1, -2),可以計算出垂直於向量 ab(-10, -8) 且指向原點方向的向量,這個向量將會作為 direction 來獲取第三個點。
這裡要用到向量積來計算出 direction。
**片段
核心演算法
獲取到三個點後,我們需要判斷原點的是否在這三個所形成的多邊形內。如果在說明碰撞,不在則剔除乙個點後繼續尋找下乙個點。
上面這種情況:w * ao < 0,說明原點在 ab 內部,則驗證剩餘的邊(實際上不需要驗證所有的邊)。假如我開始獲取到的兩個點是 b,c,則我們只需要驗證 ab,ac,因為原點一定在 bc 內部。
這裡的關鍵點在於:如何計算出垂直於 ab 且指向遠離點 c 的方向的向量 w ?
直接貼**了,畢竟也解釋不了為何是這樣的運算順序。
**片段
互動示例
上面只是介紹了我覺得實現 gjk 演算法中比較重要的點。整個流程可能並不夠清晰,所以這裡附上完整的步驟分解示例。
總結gjk 演算法並不複雜,完整的**不到 200 行。主要用到的數學知識是數量積和向量積。
參考資料
碰撞檢測 膠囊體碰撞檢測
膠囊體 給定一條線段l,所有道l的距離為r的點的集合。由定義可知,膠囊體由半徑r和線段l標識。檢測兩個膠囊體是否發生碰撞,即檢測兩條線段l1 l2的最短距離d是否大於l1 l2的半徑r1 r2之和,d r1 r2 則未碰撞,否則發生碰撞。設線段l1端點為a1 a2,線段l2端點為b1 b2,號表示兩...
H5 JS 遊戲常用演算法 碰撞檢測 畫素檢測演算法
使用畫素碰撞檢測法算是最精確的演算法了,當然,帶來的代價也是比較明顯的,那就是效率上的低下。除非是在極為特殊的情況下,要求使用非常精確的碰撞,否則,一般情況下在遊戲中是不建議使用這種演算法,特別是在執行效率不太高的html5遊戲中。一般來說在使用畫素碰撞檢測之前會使用aabb矩形包圍盒先檢測兩個精靈...
Unity碰撞檢測
碰撞個必要條件為兩個角色必須都掛載 rigibody 剛體元件,至少乙個角色掛載 collider指令碼 第一種 觸發器,必須開啟 collider的 istrigger 為 true 兩個碰撞物件會相互穿過 void ontriggerenter collider collider 開始觸發器 v...