在游戲戰斗中,我們會用到各種各樣的碰撞檢測,來判斷是否打中了目標
比如扇形檢測/圓形檢測

還有矩形檢測,王者榮耀里后羿的大招就是一個很長的矩形碰撞體

這些在Unity3D引擎中其實都封裝好了一些Collider組件去檢測碰撞,但是我最近寫幀同步算法的時候,發現U3D中的碰撞算法執行順序不可控,會導致不同步的現象,所以就只好苦逼的自己寫碰撞檢測算法了。
我們游戲是一個3D動作類游戲,大概的碰撞可以分為幾類
- 圓柱體(把人/怪物的碰撞設定位一個圓柱體,U3D里是膠囊體,是為了解決一些邊緣精度問題,但是我們游戲里的話圓柱就夠用了)
- 球體
- 立方體
需要檢測的碰撞有
1.檢測圓柱體跟球體的碰撞
2.立方體跟圓柱體的碰撞
具體實現:
1.球體跟圓柱體碰撞檢測
1 /// <summary>
2 /// 檢測球體跟圓柱體碰撞
3 /// </summary>
4 /// <param name="x1">球體X</param>
5 /// <param name="y1">球體Y</param>
6 /// <param name="z1">球體Z</param>
7 /// <param name="r1">球體半徑</param>
8 /// <param name="x2">圓柱體X</param>
9 /// <param name="y2">圓柱體Y</param>
10 /// <param name="z2">圓柱體Z</param>
11 /// <param name="r2">圓柱半徑</param>
12 /// <param name="h2">圓柱體高度</param>
13 public static bool CheckCircleAndCylinderCollider(float x1, float y1, float z1, float r1,
14 float x2, float y2, float z2, float r2, float h2) 15 { 16 float dx = x2 - x1; 17 float dy = y2 - y1; 18 float dz = z2 - z1; 19 float disSqua = (dx * dx) + (dz * dz); 20 float rSqua = (r1 + r2) * (r1 + r2); 21 bool heightCheck = Math.Abs(y1 - y2) < r1 + h2 / 2; 22 return heightCheck && disSqua < rSqua; 23 }
1.檢測兩個圓有沒有相交
2.檢測Y軸的距離是否小於球半徑+圓柱體高度的一半
這里是把球體也當成了圓柱體進行檢測,好處就是:效率高。 缺點是:不精確,沒有考慮X,Z軸的旋轉
但由於我們游戲中圓柱體不會有X,Z軸的旋轉,所以這樣的做法是最高效的
精確性問題:把圓柱體變成膠囊體,兩端用兩個球體來計算檢測,這樣會更精確,同時性能也會降低
2.立方體跟圓柱體的碰撞
這里先把問題簡化成矩形跟圓形的碰撞檢測

計算方法是先找到矩形上離圓形最短距離u,然后再比較u是否小於圓形的半徑r
1. 首先利用絕對值把 p - c 轉移到第一象限,下圖顯示不同象限的圓心也能映射至第一象限,這不影響相交測試的結果:

2. 然后,把 v 減去 h,負數的分量設置為0,就得到圓心與矩形最短距離的矢量 u。下圖展示了4種情況,紅色的u是結果。

最后要比較u和r的長度,若距離少於r,則兩者相交。可以只求u的長度平方是否小於r的平方
具體做法可以參考這里:https://www.zhihu.com/question/24251545
對於AABB包圍盒,這樣就已經可以檢測碰撞了,但是如果矩形是旋轉的OBB包圍盒呢?
我這里是實現了一個OBB的包圍盒類,記錄了坐標,角度,碰撞檢測的時候先把圓的角度旋轉到OBB的坐標系里
利用旋轉公式:
x2 = x * Mathf.Cos(rad) - z * Mathf.Sin(rad);
z2= x * Mathf.Sin(rad) + z * Mathf.Cos(rad);
然后再用那篇文章里說的方式計算矩形跟圓是否相交
最后再通過兩者 Y軸的距離 < (圓柱體高度+立方體的高度)/2 ,如果小於則相交
這種方法的優勢:效率高,而且精確
缺點是這個3D的OBB只能沿Y軸旋轉,不過也夠用了
如果像王者榮耀類型的游戲,感覺不需要擴展到3D,2D檢測應該就夠用了
