天天看點

喵的Unity遊戲開發之路 - 複雜重力

轉載(拷貝)自微信公衆号(u3dnotes),圖檔和視訊請檢視原文:

https://mp.weixin.qq.com/s?__biz=MzUxMDM3MTYwNQ==&mid=2247493087&idx=1&sn=ade85373d6d88b1597b82cff34c1d561&chksm=f90157a5ce76deb30c0b7082d52e35ff8596041bae0c73cda375e1f552675e26bb80c835fd3a&scene=158#rd

前言

        很多童鞋沒有系統的Unity3D遊戲開發基礎,也不知道從何開始學。為此我們精選了一套國外優秀的Unity3D遊戲開發教程,翻譯整理後放送給大家,教您從零開始一步一步掌握Unity3D遊戲開發。 本文不是廣告,不是推廣,是免費的純幹貨!本文全名:喵的Unity遊戲開發之路 - 移動 - 複雜重力 - 重力平面,球體和盒子

  • 支援多種重力源。
  • 限制重力範圍。
  • 使重力随距離減小。
  • 建立平面,球形和盒形重力源。

這是有關控制角色移動的教程系列的第六部分。它通過支援多個重力源(包括平面,球體和盒子)擴充了我們的自定義重力。

本教程使用Unity 2019.2.21f1制作。它還使用ProBuilder軟體包。

效果之一

喵的Unity遊戲開發之路 - 複雜重力

多重引力源

如果您隻需要相同的重力源,那麼上一教程中介紹的方法就足夠了。但是,如果您需要使用不同種類的重力(每個場景使用不同的重力,或者同一場景中使用多個重力),則需要一種更通用的方法。

預設重力源

為了在一個場景中支援多個重力源,我們将建立一個新的

GravitySource

元件類型,使用類似于CustomGravity中的一個具有單個位置參數的公共方法GetGravity,隻是在這種情況下它不是靜态的。最簡單的實作是隻傳回

Physics.gravity

,是以我們将這樣做。可以将其用作預設重力源,可以将其添加到任何場景中,這将使我們的球體在标準實體重力下工作。

using UnityEngine;public class GravitySource : MonoBehaviour {  public Vector3 GetGravity (Vector3 position) {    return Physics.gravity;  }}
           

重力來源清單

為了每個場景支援多個重力源,

CustomGravity

必須跟蹤所有活動源。我們将為此提供一個靜态的來源清單。

using UnityEngine;              using System.Collections.Generic;              public static class CustomGravity {              static List<GravitySource> sources = new List<GravitySource>();              …              }                

該清單如何工作?

請參閱“ 持久性對象”教程的1.5節中的通用清單說明。

調整

GetGravity

僅具有位置參數的方法,以使其周遊光源并累積其重力。

在另一種方法中執行相同的GetGravity操作,該方法也提供向上軸。現在我們别無選擇,隻能通過歸一化和求反最終重力矢量來得出軸。

當隻有一個來源時,我們不能優化它嗎?

是的,但是我們不會對來源數量做任何假設。如果您一次隻使用一個信号源,則根本不需要循環。

GetUpAxis

 必須以相同的方式進行調整。

使用清單是最好的方法嗎?

如果同時有幾個重力源處于活動狀态,則清單是最簡單的方法,也是最好的方法。隻有在有許多資源發揮作用時,才開始考慮更智能的空間分區政策(例如,限制體積層次結構)會變得有益。一種替代方法是使用對撞機和觸發器來确定哪些對象受哪些重力源影響,但這會增加大量開銷。還有一種方法是禁用離玩家太遠而不會影響遊戲體驗的音源。但是本教程将使其保持盡可能簡單。

還請記住,一個區域具有多個彼此靠近的強重力源,這是不直覺的。我們沒有類似的經驗。少數場景可能很有趣,但是具有許多重力源的空間可能使導航變得令人沮喪。

注冊和登出源

為了能夠改變主動重力源,增加了public的 

Register

Unregister

方法。他們隻是在清單中添加或删除給定的重力源。

public static void Register (GravitySource source) {              sources.Add(source);              }              public static void Unregister (GravitySource source) {              sources.Remove(source);              }                

這個想法是單個來源隻能注冊一次,否則其效果将成倍增加。同樣,僅取消登出先前已注冊的源才有意義。可以多次登出和注冊同一源,這是很好的。我們可以添加斷言以通過調用Debug.Assert來驗證這一點,以捕獲程式設計錯誤。

public static void Register (GravitySource source) {              Debug.Assert(              !sources.Contains(source),              "Duplicate registration of gravity source!", source              );              sources.Add(source);              }              public static void Unregister (GravitySource source) {              Debug.Assert(              sources.Contains(source),              "Unregistration of unknown gravity source!", source              );              sources.Remove(source);              }                

Debug.Assert是做什麼的?

如果第一個參數是false,它将使用第二個參數消息(如果提供)記錄斷言錯誤。第三個參數是如果在控制台中選擇了消息,則在編輯器中突出顯示的内容。此調用僅包含在開發版本中,不包含在發行版本中。好像Debug.Assert(…);從未編寫過代碼。是以,這是在開發過程中添加不會影響最終版本的檢查的好方法。

我們可以通過分别在

OnEnable

OnDisable

方法中進行GravitySource的注冊和登出自己。這樣,它在建立,銷毀,激活,停用,啟用,禁用源代碼以及跨編輯器熱加載時都有效。

void OnEnable () {              CustomGravity.Register(this);              }              void OnDisable () {              CustomGravity.Unregister(this);              }                

現在,我們可以将所有場景調整為再次使用預設重力,隻需

GravitySource

向每個場景添加帶有元件的遊戲對象即可。

喵的Unity遊戲開發之路 - 複雜重力

允許擴充

這個想法是

GravitySource是

其他重力源的基礎,就像我們建立的所有自定義元件類型的基礎MonoBehaviour一樣。新的重力源類型将通過

GetGravity

用自己的實作覆寫方法來完成其工作。為了使之成為可能,我們必須将此方法聲明為

virtual

publicvirtualVector3 GetGravity (Vector3 position) {
		return Physics.gravity;
	}
           

什麼是virtual關鍵字?

請參閱“ 持久對象”教程末尾的說明。

重力平面

預設的實體重力僅定義一個代表普遍向下拉動的向量。該想法的最簡單擴充是定義重力平面。這樣做完全相同,隻是平面還将空間分成上下兩部分。我們可以用它來限制平面重力的範圍。

新重力源類型

建立一個

GravityPlane

擴充的新元件類型

GravitySource

。給它一個可配置的重力場。這是它應用于其範圍内的所有物體的加速度,将其拉低。是以,正值表示通過重力的法向吸引力,而負值表示排斥,表示反重力。

重寫

GetGravity

以使其傳回指向正向上的矢量,該矢量由取反的配置重力縮放。必須通過向其添加

override

關鍵字來顯式地覆寫方法。

using UnityEngine;              public class GravityPlane : GravitySource {              [SerializeField]              float gravity = 9.81f;              public override Vector3 GetGravity (Vector3 position) {              Vector3 up = Vector3.up;              return -gravity * up;              }              }                

現在,我們可以建立一個重力平面,如果将其配置為指向下方,則其工作原理與預設實體重力矢量相同。

我們也可以通過使用遊戲對象轉換的向上矢量來支援任何方向的平面。

可視化平面

為了更容易檢視平面的位置,我們将使用一些小物件将其可視化。如果啟用了切換選項,它們将在場景視窗以及編輯器的遊戲視窗中繪制。盡管平面是無限的,但我們必須使用有限的可視化效果,是以我們将使用正方形。

通過添加

OnDrawGizmos

方法并使用類Gizmos的各種靜态方法和屬性來完成繪制小控件。首先将其設定

Gizmos.color

為黃色,然後通過Gizmos.DrawWireCube繪制線框立方體 ,該立方體位于原點,其大小設定為(1,0,1),是以将其展平為一個正方形。

預設情況下,小物件在世界空間中繪制。為了正确定位和旋轉正方形,我們必須使用平面的變換矩陣(将其localToWorldMatrix配置設定給Gizmos.matrix)。這也使我們能夠縮放平面對象,以便更容易看到正方形,即使這不影響平面的重力。

例如,我制作了一個小場景,其中包含兩個相對的20×20矩形區域以及相應的重力平面,它們位于它們的上方和下方。底部是正常區域,頂部是倒置區域。頂面旋轉,是以它也上下颠倒,這意味着其重力已翻轉。

我們是否必須重置其中的顔色和矩陣OnDrawGizmos?

不,那是自動發生的。

重力範圍

當兩個具有相同重力的平面朝相反的方向拉動時,它們會互相抵消,是以根本不會産生重力。如果我們想要一個重力在一個地方向下而在另一個地方向上的場景,則我們必須限制每個平面的影響。我們将通過添加可配置範圍來實作

GravityPlane

。它表示相對于平面本身有效的重力,是以最小範圍為零。平面的影響力沒有極限,可以一直持續下去。

我們可以在

GetGravity中

通過擷取平面上矢量和該位置減去平面位置的點積來找到位置的距離。如果距離大于該範圍,則合成重力應為零。

我們不能隻切斷平面上方的重力嗎?

是的,但是稍後我們将使用該範圍逐漸減小重力,而不是使其變為二進制。

我們可以通過繪制第二個正方形(最初是一個機關)來可視化此範圍。讓我們給它一個青色。

但是在這種情況下,我們要使用偏移量的範圍,而不用飛機遊戲對象的比例來修改。為此,我們可以通過自己構造一個矩陣

Matrix4x4.TRS

,然後将其位置,旋轉和比例傳遞給它。我們隻需要用範圍替換對象局部比例尺的Y分量。

我對這些平面進行了配置,以使它們的範圍平方最終位于完全相同的位置。是以,它顯示了重力翻轉的平面。

如果範圍是零,那麼我們就不用費心畫青色正方形了。

相機對準速度

假設我們可以跳得足夠高,我們現在可以在場景的一側行走并跳到另一側。當我們這樣做時,重力會翻轉,這也會突然使相機翻轉,這會迷失方向。我們可以通過減慢OrbitCamera的重新排列來改善重力突然變化的體驗。

添加可配置的向上對齊速度,以限制相機調整其向上矢量的速度,以每秒度數表示。讓我們使用360°作為預設值,這樣完整的重力翻轉需要半秒鐘才能完成。

現在,我們必須調整如何調整重力對齊四元數。首先将代碼移至單獨的

UpdateGravityAlignment

方法,然後使用變量跟蹤目前和所需的向上矢量。

void LateUpdate () {              //gravityAlignment =              //  Quaternion.FromToRotation(              //    gravityAlignment * Vector3.up,              //    CustomGravity.GetUpAxis(focusPoint)              //  ) * gravityAlignment;              UpdateGravityAlignment();              UpdateFocusPoint();              …              }              void UpdateGravityAlignment () {              Vector3 fromUp = gravityAlignment * Vector3.up;              Vector3 toUp = CustomGravity.GetUpAxis(focusPoint);              Quaternion newAlignment =              Quaternion.FromToRotation(fromUp, toUp) * gravityAlignment;              gravityAlignment = newAlignment;              }                

我們可以通過取上矢量的點積,然後通過

Mathf.Acos

乘以Mathf.Rad2Deg,将結果轉換為度,進而找到上矢量之間的夾角。最大允許角度是由時間增量縮放的上對齊速度。

arccos函數僅對落在-1到1範圍内的輸入産生有效的結果。由于精度限制,點積最終可能會超出該範圍,進而産生非數字NaN值。為了防止這種情況

Mathf.Clamp

來清理點積。

如果角度足夠小,那麼我們可以照常直接使用新的對齊方式。否則,我們必須在目前旋轉和所需旋轉之間進行插補,最大角度除以所需角度作為插補器。為此,我們執行球面插值Quaternion.Slerp,以便獲得适當的旋轉。

由于我們已經確定僅在必要時才進行插值,是以保證插值器位于0–1範圍内,是以我們可以使用它

SlerpUnclamped

來避免不必要的額外鉗位。

插值采用最短路徑,但是在180°翻轉的情況下,插值可以沿任何方向進行。數學偏愛某個方向,是以即使看起來很奇怪,它也總是以相同的方式進行。

重力衰減

該平面範圍的點是逐漸減小其重力,而不是突然将其切斷。為了示範此方法的用處,我回到了重力盒的場景,并在盒子的六個側面都添加了一個重力平面。我限制了它們的範圍,是以盒子中間的大部分開放空間都沒有重力。那裡漂浮的任何東西要麼保持靜止不動,要麼保持它已經擁有的動力。

由于每個平面的重力的二值截止,在立方體的邊緣和角落附近發生了奇怪的事情。重力突然以陡峭的角度變化,很難導航。我們可以通過

GravityPlane

随着距離從零到最大範圍線性地減小的重力來改善這一點。如果位置位于平面上方,則隻需将其乘以1減去距離除以範圍即可完成此操作。

現在,當我們接近盒子的邊緣或角落時,重力會更平滑地過渡。但是它仍然很奇怪,并且可能很難從角落中逃脫,因為三個平面被拉到那裡,導緻重力增加。稍後我們将提出一個更好的解決方案。

引力不應該除以平方距離嗎?

實際上,實際重力會根據距離的平方減小。但是,這假定了從其質心開始測量的大緻球形重力源。這對于我們的重力平面沒有意義,因為它是平坦的,無限的并且沒有質心。您可以對衰減進行平方,但由于沒有最大的重力範圍,是以無法實作。該範圍用于建立不真實的場景,其中不同區域僅受某些重力源的影響。衰減通常是線性還是二次無關緊要。Linear更易于設計,這就是我所使用的。

重力球

現在我們有了一個功能性重力平面,讓我們對球體應用相同的方法。

半徑和衰減

建立具有可配置重力,外半徑和外衰減半徑的GravitySphere。我使用術語“外部半徑”而不是“半徑”,因為它表示重力達到最大強度時的距離。這不必與球體的表面半徑比對。實際上,将其擴充到足夠遠的位置是一個好主意,以便正常遊戲區域能夠承受恒定強度的重力。這使得設計起來容易得多。否則,您會發現在略微升高的位置進行正常跳躍可能已經将您帶入太空。

在這種情況下,我們可以用Gizmos.DrawWireSphere來可視化重力,再次将黃色用作第一個門檻值,将青色用作第二個門檻值。如果衰減球大于外球,我們隻需要顯示它即可。

using UnityEngine;              public class GravitySphere : GravitySource {              [SerializeField]              float gravity = 9.81f;              [SerializeField, Min(0f)]              float outerRadius = 10f, outerFalloffRadius = 15f;                  void OnDrawGizmos () {              Vector3 p = transform.position;              Gizmos.color = Color.yellow;              Gizmos.DrawWireSphere(p, outerRadius);              if (outerFalloffRadius > outerRadius) {              Gizmos.color = Color.cyan;              Gizmos.DrawWireSphere(p, outerFalloffRadius);              }              }              }                
喵的Unity遊戲開發之路 - 複雜重力

衰減半徑小于外半徑沒有意義,是以請強制使其始終至少與 

Awake

OnValidate中的

方法 一樣大。

void Awake () {              OnValidate();              }              void OnValidate () {              outerFalloffRadius = Mathf.Max(outerFalloffRadius, outerRadius);              }                

應用重力

對于球體,

GetGravity

可以通過找到從位置指向球體中心的向量來工作。距離是矢量的大小。如果超出外部衰減半徑,則沒有重力。否則,它是按配置的重力縮放的歸一化向量。請注意,再次使用正重力表示标準拉動,而負重力會将對象推開。

我們已經計算了向量的長度,是以我們可以将配置的重力除以該長度來說明向量的長度,而不必對其進行歸一化。

我們不能從比較平方距離開始嗎?

是的,這将是一種優化,當位置最終超出範圍時,避免平方根運算。您必須在方法中平方配置的半徑或将其存儲在字段中。

在外半徑和外衰減半徑之間線性減小重力的作用類似于減小平面。我們隻需要更改數學即可使其落在正确的範圍内。是以,我們乘以一減去外半徑的距離再除以衰減範圍。該範圍等于衰減半徑減去外半徑。我将最後一位隔離在單獨的衰減因子中,并将其存儲在字段中,并在OnValidate中對其進行了初始化。這避免了一些數學運算,但是我主要是這樣做的,因為它使GetGravity代碼保持較短。

float outerFalloffFactor;              public override Vector3 GetGravity (Vector3 position) {              …              float g = gravity / distance;              if (distance > outerRadius) {              g *= 1f - (distance - outerRadius) * outerFalloffFactor;              }              return g * vector;              }                  …                  void OnValidate () {              outerFalloffRadius = Mathf.Max(outerFalloffRadius, outerRadius);              outerFalloffFactor = 1f / (outerFalloffRadius - outerRadius);              }                

通過衰減操作,現在可以使用多個具有重疊衰減區域的重力球,進而在它們之間進行平滑過渡。請注意,存在一個抵消區域,物體可能最終在兩個球體之間繞圓軌道運作,但是當進入具有動量的區域時,很少會陷入其中。

從一個球體跳到另一個球體可能需要一些練習。特别是當強重力場在大面積上重疊時,您可能會陷入相當長的不穩定軌道。同樣,當一個球體的一部分受到另一個重力源的強烈影響時,重力會變得有些奇怪。

倒球

我們還可以支援倒置重力球。僅僅抵消重力是不夠的,因為我們可能希望在球體中心有一個重力死區。這是球體的重力抵消其自身的區域,這對于正常行星和反向行星都是如此。它還可以在内部放置不受更大重力影響的另一個重力源。添加可配置的内部衰減半徑和内部半徑以實作此目的。讓我們再次用青色和黃色可視化它們,但前提是它們大于零。

内部衰減半徑的最小值為零,它設定了内部半徑的最小值,而後者又設定了外部半徑的最小值。還添加一個内部衰減因子,該函數與其他因子的作用相同,隻是半徑的順序相反。

現在,

GetGravity中

當距離小于内部衰減半徑時,我們也可以中止。如果不是,我們還必須檢查該距離是否落在内部衰減區域内,如果這樣,則可以适當地縮放重力。

重力箱

通過傳回重力框場景結束本教程。我們将使用一個單獨的盒形重力源,而不是使用六個平面使它可以在盒子内部行走。

邊界距離

GravityBox

為基于框的重力源建立新類型。這個想法類似于球體,但是重力直接向下拉到最近的面,而不是随方向平滑變化。為此,我們需要可配置的重力以及定義盒子形狀的方法。為此,我們将使用邊界距離矢量,其作用有點像半徑,但分别針對所有三個次元。是以,這些是從中心直線到面的距離,這意味着盒子的大小是其邊界距離的兩倍。最小邊界是零向量,我們可以通過該

Vector3.Max

方法強制執行。我們将通過Gizmos.DrawWireCube使用紅色線框立方體可視化此邊界。

using UnityEngine;              public class GravityBox : GravitySource {                  [SerializeField]              float gravity = 9.81f;                  [SerializeField]              Vector3 boundaryDistance = Vector3.one;              void Awake () {              OnValidate();              }              void OnValidate () {              boundaryDistance = Vector3.Max(boundaryDistance, Vector3.zero);              }              void OnDrawGizmos () {              Gizmos.matrix =              Matrix4x4.TRS(transform.position, transform.rotation, Vector3.one);              Gizmos.color = Color.red;              Gizmos.DrawWireCube(Vector3.zero, 2f * boundaryDistance);              }              }                

内部距離

我們将再次定義一個内部區域,其中重力處于最大強度,外加一個内部區域,該區域将重力降至零,而内部則是一個沒有重力的區域。這次的半徑沒有意義,是以我們将它們定義為相對于邊界向内的内部距離。這三個次元的距離都相同,是以我們可以使用兩個可配置的值。

兩個内部距離的最大值等于最小邊界距離。除此之外,内部衰減距離必須至少與内部距離一樣大。

兩種距離都可以通過線框立方體可視化,其大小等于邊界距離減去相關距離的兩倍。

衰減

計算盒子的重力衰減比球體要複雜一些,但是我們可以再次使用一個内部衰減系數。

float innerFalloffFactor;              …              void OnValidate () {              …              innerFalloffFactor = 1f / (innerFalloffDistance - innerDistance);              }                

首先考慮與平面相似的單個重力分量是最容易的。為此建立一個帶有兩個參數的方法GetGravityComponent。第一個是相對于盒子中心的相關位置坐标。第二個是沿相關軸到最近的臉的距離。它沿同一軸傳回重力分量。

如果該距離大于内部衰減距離,則我們位于零重力區域,是以結果為零。否則,我們必須檢查是否必須像對待球體一樣減小重力。唯一的額外步驟是,如果坐标小于零,則必須翻轉重力,因為這意味着我們位于中心的另一側。

然後,該GetGravity方法必須确定相對于盒子位置的位置,并從零向量開始。然後計算到中心的絕對距離,調用

GetGravityComponent

最小距離,并将結果配置設定給向量的适當分量。結果是重力相對于最近的面直線向下拉,或者在負重力的情況下朝其推。

最後,為了支援任意旋轉的立方體,我們必須旋轉相對位置以與立方體對齊。我們通過

InverseTransformDirection

對其進行轉換而忽略了它的規模來做到這一點。最終重力矢量必須通過TransformDirection反向旋轉。

這種方法的結果是,當最近的面孔發生變化時,重力突然改變了方向。當漂浮在盒子内部時,這很好用,但是使盒子内部的面之間旅行變得困難。我們很快會對此進行改進。

在箱子外面

像球體一樣,我們還将支援外部距離和外部衰減距離,以及外部衰減因子。

  • [SerializeField, Min(0f)]              float outerDistance = 0f, outerFalloffDistance = 0f;              float innerFalloffFactor, outerFalloffFactor;              …              void OnValidate () {              …              outerFalloffDistance = Mathf.Max(outerFalloffDistance, outerDistance);              innerFalloffFactor = 1f / (innerFalloffDistance - innerDistance);              outerFalloffFactor = 1f / (outerFalloffDistance - outerDistance);              } 
               

    盒子内部的重力必須突然改變,但是在盒子外面,它會變得更加模糊。假設我們正在盒子的外面移動,被拉向盒子。如果我們經過一個面的邊緣,重力仍然會拉低我們,但是如果我們繼續将它與最近的邊緣對齊,那麼我們将跌過盒子而不是朝着盒子。這表明如果我們不直接位于臉部上方,則應該朝最近的邊緣或角落掉落。而且,應該确定相對于該邊緣或面的距離,是以我們最終得到的是圓角立方體形狀的區域。

    沒有使用可視化圓形立方體的便捷方法,是以讓我們建立一個簡單的近似值。首先添加一個Gizmos方法,該方法通過四個點繪制閉環,我們将使用該方法繪制矩形。

    接下來,建立一種繪制給定距離的外部立方體的方法。給它四個向量變量并進行設定,以便我們在适當的距離為右臉繪制一個正方形。
    然後取反這些點的X坐标,以便繪制左面。
    對其他四個臉重複此過程。
    DrawGizmosRect(a, b, c, d);              a.x = d.x = boundaryDistance.x;              b.x = c.x = -boundaryDistance.x;              a.z = b.z = boundaryDistance.z;              c.z = d.z = -boundaryDistance.z;              a.y = b.y = c.y = d.y = boundaryDistance.y + distance;              DrawGizmosRect(a, b, c, d);              a.y = b.y = c.y = d.y = -a.y;              DrawGizmosRect(a, b, c, d);              a.x = d.x = boundaryDistance.x;              b.x = c.x = -boundaryDistance.x;              a.y = b.y = boundaryDistance.y;              c.y = d.y = -boundaryDistance.y;              a.z = b.z = c.z = d.z = boundaryDistance.z + distance;              DrawGizmosRect(a, b, c, d);              a.z = b.z = c.z = d.z = -a.z;              DrawGizmosRect(a, b, c, d);                
    如果需要,在OnDrawGizmos中繪制兩個外部立方體。
    喵的Unity遊戲開發之路 - 複雜重力
    這向我們顯示了圓形外部立方體的平坦區域,但未顯示圓形邊界。我們可以建立一個非常詳細的可視化效果來顯示它們,但這将需要大量代碼。取而代之的是,在立方體的圓角點之間添加一個單獨的線框立方體就足夠了。這些點與邊界立方體之間的距離一定,在所有三個次元上均等地偏移。是以,我們需要的距離等于邊界立方體的距離加上以√(1/3)縮放的相關距離。
    void DrawGizmosOuterCube (float distance) {              …              distance *= 0.5773502692f;              Vector3 size = boundaryDistance;              size.x = 2f * (size.x + distance);              size.y = 2f * (size.y + distance);              size.z = 2f * (size.z + distance);              Gizmos.DrawWireCube(Vector3.zero, size);              }                
    喵的Unity遊戲開發之路 - 複雜重力
    您也可以沿圓角的邊緣繪制直線,使用在兩個次元上由√(1/2)縮放的距離,但是我們目前的方法就足夠了。

    檢測側面

    現在我們必須确定給定位置是位于GetGravity中的框内還是框外。我們根據每個次元确定此數字,并計算出最終有多少外部。首先,檢查位置是否超出右臉。如果是這樣,請将向量的X分量設定為X邊界減去X位置。調整向量使其直接指向臉部而不是中心。另外,增加外部計數。
    如果我們不在右邊,請檢查我們是否在左邊。如果是這樣,請相應地調整矢量,這次是從負邊界距離減去位置。
    分别對Y和Z面執行相同的操作。
    else if (position.x < -boundaryDistance.x) {              vector.x = -boundaryDistance.x - position.x;              outside = 1;              }              if (position.y > boundaryDistance.y) {              vector.y = boundaryDistance.y - position.y;              outside += 1;              }              else if (position.y < -boundaryDistance.y) {              vector.y = -boundaryDistance.y - position.y;              outside += 1;              }              if (position.z > boundaryDistance.z) {              vector.z = boundaryDistance.z - position.z;              outside += 1;              }              else if (position.z < -boundaryDistance.z) {              vector.z = -boundaryDistance.z - position.z;              outside += 1;              }                
    完成之後,檢查我們是否在至少一張臉之外。如果是這樣,則到邊界的距離等于調整後的矢量的長度。然後,我們可以使用與球體外相同的方法。否則,我們必須确定内部的重力。
    else if (position.z < -boundaryDistance.z) {              vector.z = -boundaryDistance.z - position.z;              outside += 1;              }              if (outside > 0) {              float distance = vector.magnitude;              if (distance > outerFalloffDistance) {              return Vector3.zero;              }              float g = gravity / distance;              if (distance > outerDistance) {              g *= 1f - (distance - outerDistance) * outerFalloffFactor;              }              return transform.TransformDirection(g * vector);              }              Vector3 distances;                
    請注意,如果我們恰好在一個面的外面,那麼我們就在該面的正上方,這意味着矢量的分量中隻有一個非零。如果盒子很大,那就很常見。我們在這裡取向量分量的絕對和就足夠了,這比計算任意向量的長度要快。

    如果使邊界框小于表面框,則采用這種方法時,重力會再次沿邊緣和角平滑變化。在這些地區,重力也不再變得更強。您必須確定外部距離足以到達各個角落。

    現在也有可能用自己的怪異重力創造出類似盒子的行星。請注意,除非您緩慢移動,否則跑出面部會使您掉到盒子外面一小會兒。可以通過使重力框的邊界小于表面邊界來緩解這種情況,當您接近邊緣或拐角時,這會更早地開始彎曲重力。即使這樣,當快速移動時,您可能也想增加盒子的重力以貼近表面。

    喵的Unity遊戲開發之路 - 複雜重力

    下一個教程是“ 移動地面”。

    資源庫(Repository)

    https://bitbucket.org/catlikecodingunitytutorials/movement-06-complex-gravity/

    往期精選

    Unity3D遊戲開發中100+效果的實作和源碼大全 - 收藏起來肯定用得着

    Shader學習應該如何切入?

    UE4 開發從入門到入土

    聲明:釋出此文是出于傳遞更多知識以供交流學習之目的。若有來源标注錯誤或侵犯了您的合法權益,請作者持權屬證明與我們聯系,我們将及時更正、删除,謝謝。

    原作者:Jasper Flick

    原文:

    https://catlikecoding.com/unity/tutorials/movement/complex-gravity/

    翻譯、編輯、整理:MarsZhou

    More:【微信公衆号】 u3dnotes

繼續閱讀