
前言 很多童鞋沒有系統的Unity3D遊戲開發基礎,也不知道從何開始學。 為此我們精選了一套國外優秀的Unity3D遊戲開發教程,翻譯整理後放送給大家,教您從零開始一步一步掌握Unity3D遊戲開發。 本文不是廣告,不是推廣,是免費的純幹貨! 本文全名:喵的Unity遊戲開發之路 - 移動 - 遊泳 - 在水中移動和漂浮
遊泳 在水中移動和漂浮
- 檢測水量。
- 施加水阻力和浮力。
- 在水上遊泳,包括上下遊泳。
- 使物體漂浮。
這是關于控制角色移動的系列教程的第九部分。它可以漂浮在水中并在水中移動。
本教程使用Unity 2019.4.1f建立。它還使用ProBuilder軟體包。
Unity更新
我已更新到Unity 2019.4 LTS和ProBuilder 4.2.3版本,是以某些視覺效果已更改。
效果之一
在遊泳池裡玩。
水
很多遊戲都含有水,而且通常都可以遊泳。但是,沒有針對互動式水的開箱即用的解決方案。PhysX不直接支援它,是以我們必須自己建立一個近似的水。
水景
為了示範水,我建立了一個包含遊泳池的場景。它具有各種岸邊配置,兩個水準面,兩個水隧道,一個水橋以及可以在水底行走的地方。我們的水也可以在任意重力下工作,但是此場景使用簡單的均勻重力。
遊泳池。
水面由具有半透明藍色材料的單面扁平網制成。從上方可見,但從下方看不到。
水面。
必須使用設定為觸發器的對撞機來描述水的體積。我在大多數體積中都使用了不帶網孔的箱式對撞機,縮放比例略大于所需的體積,是以水中不會有任何縫隙。一些地方需要更複雜的ProBuilder網格以适合體積。還必須将其設定為觸發器,這可以通過ProBuilder視窗中的“ 設定觸發器”選項來完成。請注意,作為觸發器的網格碰撞器必須是凸形的。凹面網格會自動生成将其包裹起來的凸面版本,但會導緻它戳出所需水量的地方。彎曲的水橋就是一個例子,為此我制作了一個簡化的凸對撞機。
水對撞機。
忽略觸發器碰撞器
所有水體積對象都在“ 水”層上,應将其排除在運動球體和軌道攝影機的所有層蒙版中。即使到那時,通常我們目前擁有的兩個實體查詢也僅用于正常對撞機,而不是觸發器。可以通過“ 實體/查詢命中觸發器”項目設定來配置是否檢測到觸發器。但是我們永遠都不想使用代碼來檢測觸發器,因為我們現在擁有什麼,是以無論項目設定如何,我們都将其明确化。
第一個查詢在MovingSphere.SnapToGround中。将
QueryTriggerInteraction.Ignore
作為最終參數添加到ray cast。
其次,對OrbitCamera.LateUpdate中BoxCast執行相同操作。
檢測水
現在,我們可以移動水,好像它不存在一樣。但是要支援遊泳,我們必須檢測到它。我們将通過檢查是否在“ 水”層上的觸發區域内來完成此操作。首先,在MovingSphere中添加水面罩以及遊泳材料,我們将用它來證明它在水中。
水面罩和遊泳材料設定。
然後添加一個
InWater
訓示球體是否在水中的屬性。首先,我們将其設為一個簡單的get / set屬性,并在
ClearState中
将其重置為
false
。
如果我們不攀爬,請在Update中使用該屬性選擇中的遊泳材料。
最後,通過添加
OnTriggerEnter
和
OnTriggerStay
方法完成對水的檢測。它們的工作方式
OnCollisionEnter
與
OnCollisionStay
相同,不同之處在于它們适用于對撞機,并且具有
Collider
參數而不是
Collision
。兩種方法都應檢查對撞機是否在水層上,如果設定
IsSwimming
為
true
。
球在水中時是藍色的。
何時調用觸發方法?
所有觸發方法都在所有碰撞方法之前被調用。
淹沒
僅僅知道我們的球體是否與水相交,還不足以使其正常遊泳或漂浮。我們需要知道其中有多少被淹沒,然後我們可以用它來計算阻力和浮力。
浸沒程度
讓我們添加一個淹沒浮點字段來跟蹤球體的淹沒狀态。值零表示沒有水接觸,而值1表示完全在水下。然後進行更改
InWater
,使其僅傳回淹沒是否為正。在ClearState中将其設定回零。
更改觸發器方法,以便它們調用新
EvaluateSubmergence
方法,該方法現在僅将淹沒設定為1。
淹沒範圍
我們将使淹沒範圍可配置。這樣,我們可以精确地控制何時球體算在水中以及何時完全浸入水中。我們從球體中心上方的一個偏移點開始測量,一直到最大範圍。這樣一來,即使我們接觸水面,也可以在整個球體進入該區域之前将其完全淹沒,或者完全忽略水坑之類的低水位。
偏移量和範圍。
使偏移量和範圍可配置。使用0.5和1作為預設值,以比對我們的半徑0.5球體的形狀。範圍應為正。
淹沒偏移和範圍。
現在,我們必須在
EvaluateSubmergence中
使用水罩執行從偏移點一直向下直至浸入範圍的射線投射。在這種情況下,我們确實想擊中水,請使用
QueryTriggerInteraction.Collide
。然後,浸入等于1減去擊中距離除以範圍。
要測試浸水值,請使用它為球臨時着色。
淹沒,不正确。
這一直到球體完全浸沒的那一刻起作用,因為從那時起,我們從已經在水對撞器内部的點開始投射,是以射線投射無法擊中它。但這意味着我們已經完全浸入水中,是以我們隻要不打任何東西就可以将浸入設為1。
但是,由于身體位置與PhysX檢測到觸發時的位置不同,是以從水中移出時可能會導緻無效的1淹沒,這是由于碰撞和觸發方法的調用延遲所緻。我們可以通過将射線的長度增加一個機關來防止這種情況。這不是完美的,但幾乎可以解決所有情況,除非移動速度非常快。退出水時,這将導緻浸水變為負值,這很好,因為這不算在水中。
淹沒,正确。
現在我們可以擺脫淹沒可視化了。
請注意,此方法假定球的中心正下方有水。當球體碰到水體積的側面或底部時(例如,碰到不真實的水牆時),情況可能并非如此。在這種情況下,我們立即進入完全淹沒狀态。
水拖
與水相比,水的運動更為緩慢,因為水比空氣造成更大的阻力。是以,加速明顯較慢,而減速較快。讓我們添加對此的支援,并通過添加水拖動選項(預設設定為1)使其可配置。零到10的範圍是可以的,因為10會引起巨大的阻力。
水拖。
我們将使用簡單的線性阻尼,類似于PhysX。我們将速度縮放1減去阻力乘以時間增量。在FixedUpdate中調用AdjustVelocity之前進行此操作。我們首先應用阻力,是以總是可以加速。
請注意,這意味着如果水阻力等于1除以固定時間步長,則速度會在單個實體步長中下降為零。如果速度變大,速度将反轉。由于我們将最大值設定為10,是以這不會成為問題。為了安全起見,可以確定速度至少縮放為零。
如果我們沒有完全淹沒,那麼我們就不會遇到最大的阻力。是以,因素會浸入阻尼中。
水拖10。
浮力
水的另一個重要屬性是事物傾向于将其漂浮在水中。是以,應向我們的球體添加一個可配置的浮力值,該浮力值的最小值為零,預設值為1。該想法是,浮力值為零的物體像石頭一樣下沉,隻是被水拖慢了速度。浮力為1的對象處于平衡狀态,完全消除了重力。浮力大于1的物體會浮到水面。2的浮力意味着它的上升和正常下降一樣快。
浮力。
我們通過在FixedUpdate中檢查是否不是在攀登但在水中來實作這一點。如果是這樣,請應用按1減去浮力标定的重力,然後再次考慮浸入。這将覆寫重力的所有其他應用。
浮力1.5。
請注意,實際上向上的力會随着深度的增加而增加,而在我們的情況下,一旦達到最大浸入力,向上的力就保持恒定。這足以産生令人信服的浮力,除非在極深的水中玩耍。
浮力似乎失敗的唯一情況是球體最終距離底部太近。在這種情況下,地面彈跳被激活,抵消了浮力。如果我們在水中,我們可以通過中止SnapToGround來避免這種情況。
遊泳
現在我們可以在水中漂浮了,下一步就是支援遊泳,其中應該包括潛水和浮潛。
遊泳門檻
我們隻有在水深的情況下才能遊泳,但是我們不需要完全浸入水中。是以,讓我們添加一個可配置的遊泳門檻值,該門檻值定義遊泳所需的最小浸入度。它必須大于零,是以使用0.01–1作為其範圍,預設值為0.5。如果球體的至少下半部在水下,則可以使球體遊泳。還添加一個
Swimming
訓示是否達到遊泳門檻值的屬性。
遊泳門檻值。
在Update進行調整,以便僅在遊泳時使用遊泳材料。
接下來,建立一個
CheckSwimming
方法,該方法傳回我們是否正在遊泳,如果是,則将地面接觸計數設定為零,并使接觸法線等于上軸。
在
UpdateState中
檢查我們是否接地時,在CheckClimbing之後直接調用該方法。這樣一來,除了攀登外,遊泳淩駕一切。
然後從SnapToGround中取出檢查放在水中。這樣一來,當我們在水中而不是在遊泳時,捕捉動作就會再次起作用。
遊泳速度
添加可配置的遊泳最大速度和加速度,預設情況下均設定為5。
最大遊泳速度和加速度。
在AdjustVelocity中,檢查爬升後是否在水中。如果是這樣,請使用與通常情況相同的軸使用遊泳加速度和速度。
我們在水中越深,我們應該更多地依賴遊泳的加速度和速度而不是正常的速度和速度。是以,我們将基于遊泳因子在正常值和遊泳值之間進行插值,該因子是淹沒除以遊泳門檻值,且最大值限制為1。
其他加速度是正常加速度還是空氣加速度取決于我們是否在地面上。
遊泳的; 浮力1.1。
潛水和堆焊
現在,我們可以像在地面或空中一樣在遊泳時移動,是以受控的移動被限制在地面上。垂直運動目前僅是由于重力和浮力。為了控制垂直運動,我們需要第三個輸入軸。通過将UpDown軸添加到我們的輸入設定中(通過複制Horizontal或Vertical)來支援這一點。我将空格(用于跳躍的鍵)用于正鍵,将X用作負鍵。然後将
playerInput
字段更改為一個Vector3,并在遊泳時将其Z分量設定為UpDown軸,否則在Update将其設定為零。從現在開始,我們必須使用的
ClampMagnitude
版本的
Vector3
。
找到目前和新的Y速度分量,并在AdjustVelocity結尾用它們調整速度。這與X和Z相同,但僅在遊泳時才執行。
上下遊泳;浮力1。
爬和跳
淹沒時應該很難爬上或跳下。我們可以通過在
Update中
遊泳時忽略玩家的輸入來禁止兩者。必須明确取消攀爬的願望。跳躍會重置自身。如果在下一次更新之前進行了多個實體步驟,則仍然有可能在遊泳時進行攀爬,但這很好,因為在過渡到遊泳的過程中會進行攀爬,是以準确的時間無關緊要。要爬出水面,玩家隻需在按下爬升按鈕的同時向上遊泳,爬升就會在某個時候激活。
雖然站在淺水裡有跳的可能,但這使它變得困難得多。我們通過将跳躍速度減小1減去浸沒除以遊泳門檻值,以最小為零來模拟這一點。
在流水中遊泳
在本教程中,我們将不考慮水流,但是我們應該處理整個運動的水量,因為它們具有動畫效果,就像我們站立或攀爬的正常運動幾何一樣。為了使這種可能成為可能,如果我們結束遊泳,将對撞機傳遞給EvaluateSubmergence并使用其連接配接的剛體。如果我們在淺水中,我們将忽略它。
如果我們連接配接到水體,則不應用EvaluateCollision中的另一個水體代替它。實際上,我們根本不需要任何連接配接資訊,是以我們可以在遊泳時跳過EvaluateCollision所有工作。
内置動畫水立方;遊泳加速10。
漂浮物
現在我們的球體可以遊泳了,如果有一些漂浮的物體可以互動,那就太好了。再次,我們必須自己對此進行程式設計,方法是将其支援添加到已經支援自定義重力的現有元件中。
淹沒
像一樣
MovingSphere
,向CustomGravityRigidbody中添加submergenceOffset ,submergenceRange ,buoyancy ,waterDrag 和 waterMask ,除了我們不需要遊泳加速度,速度或門檻值之外。
縮放比例為0.25的多元資料集的淹沒設定。
接下來,我們需要一個淹沒字段。如果需要,在FixedUpdate中施加重力之前将其重置為零。确定淹沒時,我們還需要知道重力,是以也要在野外對其進行跟蹤。
float submergence; Vector3 gravity; … void FixedUpdate () { … gravity = CustomGravity.GetGravity(body.position); if (submergence > 0f) { submergence = 0f; } body.AddForce(gravity, ForceMode.Acceleration); }
然後添加所需的觸發方法以及
EvaluateSubmergence
方法,該方法的工作原理與以前相同,隻是我們僅在需要時才計算向上軸,并且不支援連接配接的物體。
即使漂浮在水中,物體仍然可以進入睡眠狀态。如果是這種情況,那麼我們可以跳過評估淹沒程度。是以,如果身體正在睡覺,請不要調用OnTriggerStay中的 EvaluateSubmergence 。我們仍然在OnTriggerEnter中這樣做,因為這保證了更改。
漂浮
在FixedUpdate中,必要時應用水的阻力和浮力。在這種情況下,我們通過單獨的
AddForce
調用而不是将其與法向重力結合來應用浮力。
我們還将拖動應用于角速度,以使對象在漂浮時不會保持旋轉。
浮動的多元資料集。
浮動對象現在可以在浮動時以任意旋轉結束。通常,物體會以最輕的一面朝上的方式漂浮。我們可以通過添加可配置的浮力偏移矢量(預設設定為零)來模拟這一點。
然後,我們通過調用
AddForceAtPosition
而不是AddForce,在此時應用浮力而不是對象的原點,并将偏移量轉換為單詞空間作為新的第二個參數。
由于重力和浮力現在作用于不同的點,是以它們會産生角動量,進而将偏移點推到頂部。較大的偏移會産生更強的效果,這會導緻快速振蕩,是以應将偏移保持較小。
輕微的浮力偏移。
與浮動對象互動
當在其中漂浮着物體的水中遊泳時,軌道錄影機會來回晃動,因為它試圖停留在物體的前面。可以通過添加一個與正常圖層類似的透視圖層來避免這種情況,隻是将軌道錄影機設定為忽略它。
透明層。
該層僅應用于足夠小以忽略或與之互動的對象。
推動浮動的東西。
當透視對象遮擋視圖時,我們可以使它們不可見嗎?
是的,在這種情況下可以檢測到它,可以用來更改對象的可視化。但是,這不是本教程的一部分。
穩定浮動
我們目前的方法适用于小型物體,但不适用于較大且不均勻的物體。例如,大的浮動塊在球體與其互動時應保持更穩定。為了增加穩定性,我們必須将浮力作用擴充到更大的區域。這需要更複雜的方法,是以
CustomGravityRigidbody
将其複制并重命名為
StableFloatingRigidbody
。用偏移矢量數組替換其浮力偏移。将浸入也轉換為數組,并以
Awake
與偏移數組相同的長度建立它。
進行EvaluateSubmergence調整,以便分别評估所有浮力偏移量的淹沒度。
然後
FixedUpdate中
還要對每個偏移量應用阻力和浮力。阻力和浮力都必須除以偏移量,以使最大效果保持不變。對象所經曆的實際效果取決于淹沒的總數。
通常,對于任何盒子形狀,四個點就足夠了,除非它們很大或經常部分掉出水面。請注意,偏移量随對象縮放。同樣,增加對象的品質使其更穩定。
穩定了四個浮力抵消。
意外的懸浮
如果一個點最終在表面上方足夠高,則其光線投射将失敗,這将使其錯誤地算作完全淹沒。對于具有多個浮點的大型物體來說,這是一個潛在的問題,因為有些物體可能最終落在水面之上,而物體的另一部分仍被淹沒。結果将是高峰最終浮空。您可以通過将一個較大的輕物體部分地從水中推出來實作此目的。
升空後被推。
該問題仍然存在,因為部分物體仍然接觸水。為了解決這個問題,當射線投射無法檢查該點本身是否在水量之内時,我們必須執行一個額外的查詢。可以通過調用
Physics.CheckSphere
位置和小半徑(例如0.01)作為參數,然後調用遮罩和互動模式來完成此操作。僅當該查詢傳回時
true
,我們才應将淹沒設定為1。但是,這可能會導緻大量額外的查詢,是以,通過添加可配置的安全浮動切換項,使其變為可選。僅對于可以充分推入水中的大型物體才需要。
安全浮動。
下一個教程是互動環境。
資源庫(Repository)
https://bitbucket.org/catlikecodingunitytutorials/movement-09-swimming/
往期精選
Unity3D遊戲開發中100+效果的實作和源碼大全 - 收藏起來肯定用得着
Shader學習應該如何切入?
UE4 開發從入門到入土
聲明:釋出此文是出于傳遞更多知識以供交流學習之目的。若有來源标注錯誤或侵犯了您的合法權益,請作者持權屬證明與我們聯系,我們将及時更正、删除,謝謝。
原作者:Jasper Flick
原文:
https://catlikecoding.com/unity/tutorials/movement/swimming/
翻譯、編輯、整理:MarsZhou
More:【微信公衆号】 u3dnotes