Unity超级角色控制器研究(四)——地形检测
一個地形檢測反例
因此我們想知道什么在角色腳底下呢?首先是距離腳下有多遠。我們會想知道角色的腳是否貼著地面還是在半空中。我們還會想知道腳下地面具體位置坐標,這對于上一章強調過的鉗住地面是很重要的。第三個就是會想知道腳下地形的法線是多少。而最后很有必要知道腳下的GameObject是誰,這使得我們可以為地形添加組件(在超級角色控制器當中,會有SuperCollisionType可以添加到地形當中來描述地形)。
看看之前研究Unity物理API的文章,找到最合適的解決方案是Physics.Raycast。我們可以通過向下打射線找到地面。雖然RaycastHit結構體可以讓我們得到碰撞點,打擊距離,打擊法線以及發生碰撞的對象。第一眼看上去就完全滿足了我們的需求,但是仔細想想還是會有一些問題。
我們的角色控制器是用一系列的球體來表示的,這意味著當直接站在平面上的時候,平面上的最近點就恰好距離底部球體球心一個半徑距離。但是如果角色站在斜坡上就會出問題。如果我們現在還是像之前一樣直接向下打射線,所得的點就不再是底部球體的最近點了。這對于鉗住地面來講會出錯的(具體后面會談到)。
?
射線直接從底部球心直接向下打出。在角色站在斜坡的情況下,下面的點是不正確的。而當角色鉗住地面的時候,那個點會讓角色稍稍與斜坡交叉。對于陡峭的坡體來說,這個問題就更加明顯了。
幸運的是,我們有救兵Physics.SphereCast。與其投射一條細微的射線出去,我們可以投射一個球體。這樣就能解決上面提到的問題,從而確保角色總是完全在地表之上。
在使用SphereCast能夠順利工作的同時,也帶來了一些問題。首先是SphereCast碰到碰撞體邊緣的時候,hit.normal返回的是兩個相鄰面的插值。這與Vector3.Lerp函數來類似。
?
動畫演示了SphereCast返回的hit.normal的插值情況。
我發現很有必要知道所站的表面法線的真實值,而不是插值。為了解決SphereCast帶來的插值問題。我用單獨的射線分別跟蹤SphereCast所得到鄰近的兩個面,這樣就能夠得到正確的法線(在超級角色控制器的ProbeGround方法中,這個稱為nearHit與farHit,分別表示距離控制器中心的最近的面與最遠的面)。
SphereCast的下一個問題是我們用了它來做地形檢測,但還是會有準確性的問題。我們已經默認了SphereCast打中的所有碰撞體都是地形,我們的角色可以站上去(或者在有的游戲中是滑上去)。但實際上這不是總能行得通的。我們可以有理有據地認為游戲世界的物理表面(角色與之碰撞的對象)可以劃分為地形與墻體,而只有地形表面才應該被地形檢測所檢測出來。最簡單的劃分方法就是將表面法線與某個向量(比如世界空間的Vector3.up)的夾角來劃分。小于90度的是地面,大于90度的是墻面。這樣我們就可以確保不會將墻體也當做地面來處理。我們的地形檢測里這個問題很天然的解決了:我們總是向下投射球體,這就是說這不可能打中90度的墻體。但是,游戲中的墻體法線會接近90度,甚至處于85到90之間。我們想把這些85度的表面看做是墻體,也就是說它們應該忽略SphereCast。
?
我們的角色控制器是上不去85度墻的。對于這個墻來說我們的SphereCast的碰撞點是黃色標記,而不是腳下方的平面。這會使得我們的角色會認為自己站在陡坡上,而不是平地上。
最常見的辦法是用Physics.SphereCastAll。它會理想地同時打中墻體與地面,然后我們可以遍歷所有碰撞點,從而找到合適的立足點。不幸的是,SphereCastAll只能每個對象揀選一個碰撞點,也就是說如果墻體與地面是同一個對象,那么這個方案會出錯。
對于上面的問題,我們可以通過減小SphereCast的半徑來解決。這樣確實能解決一些問題,但不是全部。我們還是要想一種可靠的方法來來處理貼著陡坡這個問題。
「PS: 在SuperCharacterController中,陡坡的定義是SuperCollisionType組件的StandAngle值定義的,可以放在所有與角色碰撞的碰撞體上。」
為了解決這個問題,可以想象一下我們腳底下某種地形,如果沒有被陡坡影響,我們是能夠通過SphereCast檢測出來的。賣手機靚號為了找到這個地面,我們可以在SphereCast打中的地方用Raycast。這是主要用于檢驗是否存在這塊地面,而且可以獲取它的法線。
最初SphereCast的碰撞點在黃色標記位置。因為我們與陡坡有碰撞,我們可以Raycast(紅色)來檢測陡坡之下是否有一塊地面在我們之下(紫色)。
這讓我們知道下面有什么,但是由于用了Raycast,而不是SphereCast,我們會又一次遇到之前的問題,碰撞點會讓地形與球體重合。給定我們有的信息(腳下地形的法線),我們可以將其轉換為近似于SphereCast的數據嗎?答案是Yes。
每當你從控制器底部向下SphereCast,然后碰到了一個表面,就會就在碰撞表面法線與碰撞點之間存在一種聯系。在我們從斜坡碰撞點向下打射線得到正確地面的法線之后,我們的任務就是找到控制器底部向下SphereCast與地面的交點。下面我們看看二維的情況,然后再回來解決三維問題。
?
動畫演示了SphereCast的碰撞點與碰撞平面法線的關系。SphereCast的原點在黃色球那里,碰撞在紅色球那里,碰撞點標記為藍色。注意到隨著斜坡越來越斜,紅色圓形就越往上。
由于我們想找一個二維空間的點,而這兩個值分別是x和y。如果計算正確,x和y會將我們圓形的往下與地面碰撞的點。對應上面的圖來看,我們要用給定的地面法線,找到上圖中藍色交點的位置。
幸運的是,這其實并不困難。復習一下中學的數學知識,通過法線作為角度傳入,我們可以使用三角函數來計算x和y的位置。
?
復制代碼
要注意的是,Unity的Sine和Cosine需要以弧度的方式傳入夾角,需要轉換一下。
?
通過sine與cosine計算碰撞點的位置
現在我么可以用這些值來調整控制器的位置(乘上半徑)。
「PS: 在SuperCharacterController中,近似計算SphereCast值的方法叫SimulateSphereCast。」
以上就是超級角色控制器中用到所有地形檢測技術。不像之前的文章,這里地形檢測話題是很開放的–上面只是其中的一種,但我法線它在實際中運行的很好。
總結
以上是生活随笔為你收集整理的Unity超级角色控制器研究(四)——地形检测的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何利用多核CPU提高虚拟现实性能?
- 下一篇: 游戏中的物理与数学(一):物体运动算法的