閱讀408 返回首頁    go 阿裏雲 go 技術社區[雲棲]


深入探討Box2D中ghost collision問題解決方案

在使用Box2D引擎時,我們必須牢記它隻能對物理世界進行近似的仿真。這其中最根本的原因是幀速受限,而且在pix<->meter換算以及其它乘除法的約算上,會衍生出大量的邊界問題。在box2d官方的FAQ頁上可以看到這樣一條提示:What are the biggest mistakes made by new users?  Expecting Box2D to give pixel perfect results.  這句話應當引起我們相當的注意。

對於“高速剛體穿越碰撞塊”以及本文將討論的“ghost collision”問題,目前還沒有一個100%完美的解決方案。有時我真為這種bug感到抓狂,但是細想一下,如果要我自己去設計這樣一套引擎,未必能做得更好。

ghost collision問題如下圖所示:

A塊和B塊代表固定的地麵,藍色塊代表正在移動中的人物。雖然我們把A塊和B塊放在了一個水平麵上,但仍有相當的機率使得當Hero在水平方向上移動時,卡在A、B塊的交界處。一個典型的情境是:你在一個超級瑪麗類的平台遊戲中使用了貼片地圖(TMX),在TMX中又設了一個地麵層或是平台層之類的。你在場景初始化時遍曆這個平台層的每一個Tile,對它賦一個polyshape的body,然後我們的瑪麗奧大叔走著走著,就卡住不動了,仿佛麵前有一堵隱形的牆一樣。

究其根本原因,我們可以仔細思索一下“What are the biggest mistakes made by new users?  Expecting Box2D to give pixel perfect results. ”這句話中包含的信息量。事實上,box2d並不能保證在任何一幀中,Hero一定是處在AB水平麵之上的。此時Hero與B的碰撞可以分為以下兩種情形:

情形1                                          情形2

把hero與B的相交部分四邊形高設為Δy, 寬設為Δx。如果如情形1所示,Δy < Δx,則碰撞後的應力向量是在y軸方向的,反之則在x軸上。我們可以想到,Δy始終都是在一個比較小值範圍內波動的,但是 Δx的波動範圍則直接與hero的移動速度相關。由於hero的移速一般是要比Δy大很多的,在一幀內所形成的Δx’也會是一個相對較大的值,因此大部分情況下我們遇到的是情形1。

為解決這個問題,有了這樣一些解決方案:


1.將A、B拚合成一個polyshape


將A、B簡單的拚合成一個polyshaper後,自然消除了Hero <-> B之間的碰撞。當然了,這裏的拚合必然不是手動拚合,而是用代碼實現的相鄰塊自動拚合。這樣的做的麻煩在於:1.拚合算法本身要花點功夫。2.對於不規則圖形束手無策。polyshape的默認的最大頂點數為8,不規則圖形會輕易超出這個界限。

另一個問題是,拚合將消除A、B塊自身的特性,使得他們在一次碰撞檢測中變得難以分辨。例如:

對於這樣一個情境,如果把所有5個tile拚合成一個polyshape,當我們的hero頂到了問號上或者是頂到了磚塊上,將會觸發同一個碰撞回調。這時想要區分到底是哪一個被撞到了,除了全局坐標外,幾無它法。如果把polyshaper換成edge chain,同樣無法解決內部區分這個問題。


2.消除過於分明的棱角


如果我們把hero原來的90度棱角全部做一個鈍化處理,也能緩解完全卡住的現象。因為這樣一來,至少不會出現一個完全是水平方向上的碰撞反饋。然而問題沒這麼簡單,此方案副作用是我們的英雄在平地上走著走著,會莫名其妙地小跳一下,仿佛被小石頭絆了一個踉蹌似的。有人說把A、B的棱角消除後能解決此問題,我試過後仍覺無用。


3.使用Edge Shape作為每一個Tile的body形狀


如果我們用上、下、左、右四個edge shape代替原來的那個polyshape,我們會發現這個問題會奇跡般的得到解決。網上大家都說使用edge shape可以在絕大多數情況下解決這個問題,但是還沒有誰說得清楚這是為什麼。其實在非常苛刻的情況下,edge shape同樣會使hero完全卡住,這一點,可以參見iforce2d兄的一個演示視頻https://www.youtube.com/watch?v=oP7ZLeyc9HU
如果想要用edge shape完美的解決卡頓,可以使用ghost vertices,這一點見iforce 2d的教程https://www.iforce2d.net/b2dtut/ghost-vertices

盡管edge shape 配合ghost vertices完美解決了人物的卡死,但這又連帶出兩個衍生問題:


1)多次碰撞


由於原本的1個shape現在由4個shape組成,而box2d中的碰撞是以fixture為單位而非body進行檢測的,所以現在如果Hero從A的上表麵完整的滑過去,會分別與左邊、上邊、右邊進行三次碰撞回調。在某些情況下,我們會非常不願意收到這種針對同一個body的重複碰撞回調。一個取巧的方法是把左邊和右邊的長度分別縮小幾個小像點,如下圖所示.

這樣可以初步解決人物在僅在表麵滑動時產生的多次碰撞。如果hero不僅僅是在表麵滑動的話,這種削減左、右邊的方法就不再適用,需要您根據具體情境來想辦法了。


2)Hero被鎖死在edge shape內部


在poly shape的方案中,由於polyshape是完全致密的,所以絕無可能在碰撞完畢後,hero被A塊完全包絡住。但是當我們采用了edge shape的方案後,由於中間部分完全是空心的,在一些特殊的情況下可能導致hero完全被鎖死在A的內部空間中。這時,我們就需要開動聰明的腦瓜子,針對具體情況想點新辦法了。

Over。

參考文獻:

https://www.box2d.org/manual.html

https://www.iforce2d.net/b2dtut/ghost-vertices

https://www.cocos2d-iphone.org/forum/topic/31787

最後更新:2017-04-03 12:56:30

  上一篇:go HDU4520-小Q係列故事——最佳裁判
  下一篇:go CareerCup之1.1字符串中字符判重