【unity】2d角色斜坡移動解決方案筆記
·解決思路參考
https://blog.csdn.net/qq_37776196/article/details/115233332
(b站有個國外教學視頻講解了斜坡移動,但我當時沒看懂,可能和這篇文章解決方案類似,感興趣可以搜unity斜坡/面移動找一下)
·問題的提出
不知道大家在做橫版2d的角色上下斜坡時有沒有遇到過這樣的問題,使用膠囊碰撞體的角色,如果給她添加一個水平方向的力,根據(jù)力的平行四邊形法則,會出現(xiàn)這樣的情況:


你問為什么離開地面下落沒有變成下落狀態(tài)?emmm,不要在意這個小bug。
·一些失敗的思路
????固定位置的斜面檢測:一開始的思路是,從角色前方一定位置向地面投射射線,檢測到斜面時,可以得到斜面的法線方向,進而得到坡度方向,然后將速度的方向改變?yōu)槠露鹊姆较颍粋€圓上斜面時,并非它最底部的點先接觸斜面,如下圖所示:

????如果這個射線的位置離圓中心比較遠,比如從圓邊緣向下,檢測到斜面就改變速度方向,就會導致圓提前向上(圖中紅圓位置開始沿坡度方向向上),如果這個射線離圓中心極近,那么在離開下坡時,也會提前改變速度方向,因此這個方案雖然可以做到沿斜坡移動,但上下斜坡時會出現(xiàn)bug。
????動態(tài)的斜面檢測:既然固定位置檢測不行,那如果我可以準確得到斜面與物體接觸的時機,我就可以在正確時間改變方向了,思路是預先檢測前方的斜坡,假如有,從圓心投射斜坡法線反向的射線,如果接觸點接觸到了斜坡(且距離等于圓半徑),說明圓接觸到了斜坡,離開斜坡也做類似的判斷。。。??傊@套方法不太優(yōu)雅其實是有點麻煩,我豬腦過載了
????·讓物體沿固定軌跡移動:類似3d中的導航,根據(jù)地面碰撞體形狀生成導航路徑,角色在地面時始終沿路徑移動,這套方法最大的缺點是,我不會(會的大佬也根本不會看這篇文章吧)
·簡單粗暴的解決方法
讓我們重新描述一下我們的需求,本來是:
????角色在地面時可以左右移動,在斜面上時要始終貼著地面移動。
改為:
????讓角色向著地面,向左右移動。
????然后我就有了這樣的思考:我希望圓在每次移動時,都能多加一個向下直到地面的位移,也就是每次都把圓底部的點移到地面上,參考下圖,可以看作是從D點移到B點,也就是B點的向量-D點的向量,得到的向量歸一化后恰好就是我們所需要的速度方向。

????解決方法這就出來了:
????1.結合碰撞體類型、偏移和物體位置每幀計算出與地面接觸點——這里我們先簡單的用碰撞體最底部中心的點;
????2.根據(jù)角色速度,計算出水平運動時,下一幀會移動到的水平位置,從這個位置發(fā)射一條向下的射線獲得地面的位置,也就是射線碰到的地面上的那個點;
????3.用2里得到的點-1里得到的點,歸一化后就是我們要的速度向量,在地面上就用這個向量乘移動速度來進行移動;
????4.當跳躍/浮空時,要改為用固定的水平速度移動。
我將發(fā)射位置的點固定在了移動方向前方,也就是圓最右/左邊的點,得到的效果如下:

·存在的問題
????最終效果看上去還行,但嚴謹來說,還是存在不少問題的:
????·如果角色站在一個無限深的懸崖邊,在掉下懸崖前存在檢測不到地面的情況,需要額外處理,檢測射線不夠長且站在高臺時也同理;
????·文中的情況,角色在斜坡上總速度等于在水平面上左右移動的速度如果我數(shù)學沒問題應該是這樣,如果想要斜面上和水平面上左右方向的速度相等或是下坡快上坡慢,需要額外處理,前者可以參考文章頂部鏈接里的計算方法;
????·實際上,上面的結論也不對,如果要更精準一點,在斜坡上的速度方向應該使用碰撞體和斜坡接觸的那個點來計算;

????·即使在水平地面上,角色也可能并非絕對精準的移動到預計位置,因為角色底部中心點存在沒貼緊地面的可能。
·代碼參考
????我自己的代碼里寫了一堆bug就不貼了,上一段gpt的回答,方向計算和最下面的點位置有點問題,其它的應該都是對的,有興趣的可以直接復制粘貼進unity看看效果:
好的,以下是代碼實現(xiàn)的基本思路:
在角色對象上添加2D碰撞體組件和剛體組件;
聲明一個變量存儲碰撞體的偏移,并在 Start() 函數(shù)中獲取碰撞體偏移;
編寫移動函數(shù) Move(),該函數(shù)接受一個移動速度參數(shù) velocity,并根據(jù)水平輸入軸(Horizontal)的值判斷角色的朝向;
根據(jù)角色朝向和移動速度計算出預測的移動目標點 targetPosition;
在目標點 targetPosition 的上方發(fā)射一條向下的射線,射線只與地面層(LayerMask)碰撞,如果檢測到地面,記錄碰撞點作為實際目標點 actualTargetPosition;
將實際目標點 actualTargetPosition 減去實際位置點 currentPosition 得到移動方向 moveDirection,并將其歸一化;
將角色剛體的速度設置為移動方向 moveDirection 乘以移動速度 velocity。
下面是基于以上思路的 C# 代碼實現(xiàn):
注:以下代碼假設地面層的 LayerMask 值為 groundLayer。
(ai太強了,我瑟瑟發(fā)抖)