深入了解 Unity 可配置关节 Configurable Joints)
本文內容翻譯自:Luna Tech Series: A Deep Dive into Unity Configurable Joints 并在自己理解的基礎上加入了部分注解。
附:Unity可配置關節官方說明
ConfigurableJoint(可配置關節)是Unity物理引擎中非常強大的一個部分,該關節包含其他關節類型的所有功能。其它關節包括角色關節 (Character Joint) 、固定關節 (Fixed Joint)、鉸鏈關節 (Hinge Joint)、彈簧關節 (Spring Joint) ,它們不在本文討論范圍內。通過超過40個可配置的選項,你幾乎可以創建從門鉸鏈到布娃娃等所有需要兩者系在一起的東西。
遺憾的是,Unity官方文檔對該部分的講解十分的有限,難免看完之后仍有疑惑。一大堆的屬性帶來無盡的自由,同時又讓你深陷泥潭。你不得不在這一堆充滿迷惑性的屬性中不斷摸索,不斷試錯。想要把它應用于自己的項目十分的困難。
所以我們團隊(Luna Labs)盡自己最大的努力的面對了這項挑戰,使我們完成了Luna Tech Series的第二篇文章。(致敬?。。。?br />
通過幾個星期的研究試錯,我們創建了一個能完美跑起來,包含上百個關節的測試場景。并對如何使用這些關節有了非常不錯的理解。
在介紹所有信息之前,先來看一下ConfigurableJoint的基本內容,特別對于對該內容比較生疏的人。
有bug的布偶娃娃——開啟早晨的完美方式(Buggy ragdolls — a perfect way to start the morning. 直譯,有點生硬。英語高手指點一下。)
基礎:錨點和連接體(anchors and connected body)
錨點(anchor)和連接錨點(connectedAnchor)是所有關節里的兩個主要屬性。他們是關節世界的羅密歐與朱麗葉——兩個千方百計緊緊粘連在一起的點。Unity會嘗試通過關節移動對象(object),直到這兩個點在同一位置為止。它一般都會成功,除非你使用了彈簧(springs)或沿某個軸的自由運動(free motion)(我們將在后面介紹)。另外,請記住,默認情況下,錨點是對象空間(object’s space,也就是local space)中的位置,連接錨點是世界空間(world space)中的位置。所以,對象移動時,錨點也會移動,但連接錨點不會。
(譯者注:這里所說的“連接錨點是世界空間中的位置”,我認為并不準確,我認為連接錨點是連接體的一個相對位置,也就是說,連接錨點的坐標是關于連接體的相對坐標。原文這么說,可能是當連接體為空時,看起來像是世界空間坐標。Unity官方對連接體(connectedBody)這樣描述:關節連接到的另一個剛體對象。可將此屬性設置為None來表示關節連接到空間中的固定位置,而不是另一個剛體。也就是說,可以認為,當連接體為空時,有一個連接體在世界空間中(0,0,0)的位置,那連接錨點設置的坐標就剛好看起來是世界坐標。Unity會讓錨點和連接錨點靠近。另外,此時,由于“空的連接體”無法移動,所以會把錨點移到連接錨點的位置。當連接體不為空時,就可以很顯然的看出來:連接錨點就是連接體的一個相對位置。另外,錨點應該就是本體(body)的一個相對位置。簡而言之,錨點(anchor)是相對本體的位置,連接錨點(connectedAnchor)是相對連接體的位置。)
來看下面這個例子:
unity移動Object把錨點和連接錨點拉到一起(Unity is moving object to pull Anchor and Connected Anchor together)
譯者注:左邊錨點和連接錨點是分開的,運行后,Unity會把物體拉上去,使錨點和連接錨點重合。但要注意,想更好表現這個效果,不要勾選自動配置連接錨點(autoConfigureConnectedAnchor ),勾選了的話,會把連接錨點直接拉到錨點位置。另外,也先別設置連接體(connectedBody)。這里可以理解為:連接錨點和錨點使勁靠近,但此時連接錨點不能動,所以,相對的,把物體拉上去了。
如你所見,可以給一個物體綁定關節然后通過連接錨點來移動物體。當然,這不是非常有用,平常也不會這么用。要更好地使用它的話,你可以使用連接體(connectedBody)。當設置了連接體后,連接錨點的坐標會關聯到連接體的位置。所以,當你移動連接體的時候,Unity會重新計算連接錨點,并嘗試把錨點和連接錨點拉到一起。結果,你的物體就會移向該連接體。
看,生效了,美滋滋!(It works. Sweet!)
如果你勾選了自動配置連接錨點(autoConfigureConnectedAnchor),Unity會完全忽略你在編輯器上設置的連接錨點(connectedAnchor)的值,并在初始化時自動計算它使它和錨點在相同的位置。(譯者注:經測試,去掉勾選,隨意設置一個連接錨點的位置,再勾選上自動配置。運行Unity,物體不會動,Unity只會直接把連接錨點拉到錨點位置。)
下面是我們自己寫的一段用于更透徹理解關節的模擬代碼:
public class OurOwnJoint : MonoBehaviour {
// Here is our configuration params
public Rigidbody connectedBody;
public Vector3 anchor;
public bool autoConfigureConnectedAnchor;
public Vector3 connectedAnchor;
// Here we're performing some initialization.
void Start() {
var ourBody = GetComponent<Rigidbody>();
// If autoConfigureConnectedAnchor is true,
// we should ignore value of connectedAnchor.
if ( this.autoConfigureConnectedAnchor ) {
// Anchor is relative to our body - let's calculate it position in the world.
var anchorWorldPosition = ourBody.transform.TransformPoint( this.anchor );
// If we have a connectedBody then let's store position relative to it.
// Otherwise we can just use world position of this.anchor.
this.connectedAnchor = this.connectedBody != null ?
this.connectedBody.transform.InverseTransformPoint( anchorWorldPosition ) :
anchorWorldPosition;
}
}
// Here we're fixing a position error
void FixedUpdate() {
var ourBody = GetComponent<Rigidbody>();
// Finding a world position of this.anchor.
var anchorWorldPosition = ourBody.transform.TransformPoint( this.anchor );
// Finding a world position of this.connectedAnchor. Note, that it's
// already stored in this.connectedAnchor if this.connectedBody is null.
var connectedAnchorWorldPosition = this.connectedBody != null ?
this.connectedBody.transform.TransformPoint( this.connectedAnchor ) :
this.connectedAnchor;
// Here is how far we should move our body in order to co-locate
// this.anchor and this.connectedAnchor
var positionError = connectedAnchorWorldPosition - anchorWorldPosition;
// This is a simplification - real joint will move connectedBody as well
ourBody.position += positionError;
}
}
Here’s our own joint code to make things crystal clear:
這是最基本最簡單的部分,下面我們來看一些吊炸天的東西。
線性運動(Linear Motion)
這里,我們涵蓋了三個相似的移動屬性:xMotion,yMotion,zMotion.想象一下,連接錨點的世界坐標是(1, 2, 3),錨點的世界坐標是(-1, -2, -3)。任何關節(Joint)的主要目標就是使錨點和連接錨點處于同一位置,這坐標的位置關系明顯與我們的目標不符。想要使它們符合預期,關節就要去改變錨點(anchor)或者連接錨點(connectedAnchor)的位置。我們要么使本體(body,就是綁定Configurable Joint的對象)移動(-2,-4,-6),要么使連接體(connected body)移動(2,4,6),又或者兩者都往它們之間移動。當然,你也可以通過微調移動屬性(motion properties)來糾正這個偏差(這里的偏差就是指錨點和連接錨點的差值)。
每個移動屬性(Motion)都可以設置成下面這些值:
Locked:鎖定,偏差會被完全糾正。例如,上面這個例子,你把yMotion設置成鎖定,會使其中一個物體在Y方向移動4個單位,或者分別向對方移動兩個單位,總之,會讓它們在一起。具體怎么移動,視當時情況而定。
Limited:受限制的,如果你想在超過一定閾值時再糾正這個偏差,可以設置成受限制的。我們接下來還會詳細討論它。
Free:放任自由的。你對對應軸上的偏差感到滿意,并不想糾正它時使用。(譯者注:意思就是在對應軸上,你不作任何限制。受咋咋地,像極了受盡996摧殘后放假的你~)
下面演示鎖定和自由參數設置:
xMotion設置成自由,yMotion設置成鎖定,只在y軸方向上糾正了偏差(xMotion is free and yMotion is locked — only error along Y axis was corrected)
就像我們在Update里加入了這一段代碼:
// ... skipping almost everything for brevity
if ( this.xMotion == ConfigurableJointMotion.Free ) {
positionError.x = 0;
}
if ( this.yMotion == ConfigurableJointMotion.Free ) {
positionError.y = 0;
}
if ( this.zMotion == ConfigurableJointMotion.Free ) {
positionError.z = 0;
}
ourBody.position += positionError;
And what it looks like when we update our code:
如你所見,如果你直接復制上面的代碼,你會得到完全不一樣的結果——物體會旋轉,而不是上升。
為什么會這樣呢?你會在下面的內容中找到答案。
坐標軸(Axes)
這里有一個非常重要的細節,線性限制(linear limits)使用的是本地坐標系,而不是世界坐標系。這就意味著,我上面寫的代碼只有在所有坐標軸上旋轉都為0時才有效。這里還有一種更為重要的情況:讓我們來看看前面的場景實際上會發生什么!
再試一遍:xMotion設置成自由,yMotion設置成鎖定,(Once again: xMotion is free and yMotion is locked)
(譯者注:這種情況,在一個物體時,即連接體為空時,是不會出現的。只有當連接體不為空,且滿足一定條件時,才會出現。條件就是連接體與本體x坐標不一致時,且沒有鎖定繞z軸的旋轉,沒有鎖定y軸方向的移動(可以通過Rigidbody的Freeze Position和Frezee Rotation鎖定)。當連接體放到(0,0,0)位置時,把它的y軸移動和繞z軸旋轉鎖定之后,就和連接體為空表現完全一樣了。這也驗證了我上面的觀點,連接體為空,就相當于連接體位于世界坐標的(0,0,0)點。然后,通過這里的測試,可以看出,這個“空的連接體”是位于原點,不能移動,不能旋轉的連接體。)
盡管我們的立方體沒有移動一點,但是繞著z軸發生了旋轉。當你只能移動y軸本身時,為什么沿著y軸向上移動立方體去糾正偏差?(譯者注:這一段原文可能表述有誤,我覺得應該是“當你沿y軸移動立方體就能糾正偏差,為什么要繞z軸旋轉來糾正誤差?”放上原文,高手指點一下。Although our cube hasn’t moved a bit, it rotated aroundZinstead. Why move the cube up to correct the error along theYaxis when you can just move theYaxis itself?)
如果你把圖片拉近來看,你會發現這個偏差其實已經完全被糾正了——把錨點和連接錨點放到我們新的坐標系中,y的值已經一樣了(x軸是自由的,不會糾錯,只有y軸鎖定會完全糾錯)。
但是,為什么關節(Joint)會這么做?再擴展一下,為什么關節們(Joints)要這么做?
還記得上面我提過的例子嗎?我們在(1,2,3)有一個連接錨點,以及在(-1,-2,-3)有一個錨點。
是的,一個解決這種情況的合理方式是:我們的本體(body)移動(2,4,6)。但其它的解決方案也可能是本體移動(1002,1004,1006),連接體(connectedBody)移動(1000,1000,1000)。很明顯,這樣也可以使錨點和連接錨點最終停在相同的位置,使偏差得到完全糾正。這是一個正確的結果,但這樣合理嗎?明顯不合理。那么在關節世界里,怎么才是合理的?
答案很簡單——花費最小的能量去做你需要做的事情。例如,移動(2,4,6)就比移動(1002,1004,1006)節能得多。同理,你稍微轉個身,比起移動更節能得多,不是嗎?(譯者注:好像挺有道理的哈。)就是這么簡單,如果可以,關節會選擇旋轉它,而不是移動它。
等等,還沒完,還有兩個屬性需要討論一下:第一軸向(axis)和第二軸向(secondaryAxis)。它們定義了本地空間的x軸、y軸方向。默認情況下axis = (1,0,0),secondaryAxis = (0,1,0) 。z軸方向通過x軸,y軸方向向量叉乘得到:(1, 0, 0) × (0, 1, 0) = (0, 0, 1)。所以,一般使用默認值就能符合你的期望。你可以修改軸向,但xMotion始終會控制移動沿著第一軸向(axis)進行,yMotion始終會控制移動沿著第二軸向(secondaryAxis)進行。
為了鞏固知識,我們再一次更新代碼
// ... skipping almost everything for brevity
// Calculating world direction of our axes
var xAxisDirection = ourBody.transform.TransformDirection(
this.axis
);
var yAxisDirection = ourBody.transform.TransformDirection(
this.secondaryAxis
);
var zAxisDirection = Vector3.Cross(
xAxisDirection,
yAxisDirection
);
// Removing error components along axes marked as "Free"
if ( this.xMotion == ConfigurableJointMotion.Free ) {
positionError -= xAxisDirection *
Vector3.Dot( positionError, xAxisDirection );
}
if ( this.yMotion == ConfigurableJointMotion.Free ) {
positionError -= yAxisDirection *
Vector3.Dot( positionError, yAxisDirection );
}
if ( this.zMotion == ConfigurableJointMotion.Free ) {
positionError -= zAxisDirection *
Vector3.Dot( positionError, zAxisDirection );
}
ourBody.position += positionError;
Armed with that knowledge, let’s update our code once again:
線性限制(LinearLimits)
在這一部分將討論移動屬性(Motion)設置成受限制(Limited)的情況。這個選項告訴關節在對應的軸上允許一定的偏差存在。這個最大偏差的絕對值可以通過線性限制(linearLimit)屬性指定。
這個屬性結構體包含三個字段:
limit:限制,允許最大的偏差值。移動限制的邊界,可以理解為可移動偏離關節原點的最大距離。
bounciness:彈跳強度,決定當位置超出限制時是否反彈。值的大小確定反彈的力度。
contactDistance:碰觸距離,可以防止逼近極限時突然停止。(譯者注:Unity官方解釋為——確定解算器可“看到”關節限制的空間前方的距離。)
它會應用于所有受限制的(Limited)的軸向上。想象一下,我們把限制設置成3。如果只有一個軸向是受限制的,其它軸向是鎖定的(Locked),那么對象將在受限制的軸向上來回不超過3個單位的移動。同理,兩個軸向受限將提供一個圓形移動區域;而三個軸向都設置成受限制的話,將在一個半徑為3的球形區域移動。
移動屬性在x軸、y軸上受限制而在z軸方向鎖定,則錨點可以在黃色圓形區域內的任意位置(Movement is limited along X and Y and locked along Z. Anchor can be anywhere within the neon yellow circle.)
默認情況下,到達線性限制(linear limit)閾值會立即停止物體移動。如果你想它有彈跳效果,你可以使用反彈(bounciness)屬性。設置后,本體(body)到達極限時會以一定的速度(velocity =velocityAlongAxis * bounciness)反彈。當設置反彈強度為1時,幾乎不會有能量損耗。(但經過測試,每次撞擊極限都會有一定的能損。)
你也可以通過線性限制彈力(linearLimitSpring)屬性給本體添加一個虛擬彈力。通常,你可以通過彈力(spring)屬性設置彈力的剛度,通過阻尼器(damper)屬性設置阻尼比。
注意:一旦設置了線性限制彈力(即 LinearLimitSpring下的spring!= 0)之后,線性限制(LinearLimit)下的反彈屬性將完全失效。因為它們無法一同運作。
從左到右,常規設置線性限制,線性限制+反彈,線性限制+彈力, 線性限制+彈力+阻尼(From left to right — regular limit, limit with bounciness, limit with spring, limit with spring and damper)
在Unity中實現彈簧很乏味,所以我們略過它們直接跳到角度部分。(It’ll be quite tedious to implement springs in Unity, so we’ll skip that part and move to angular stuff.)
角度偏移(Angular movement)
可配置關節(ConfigurableJoint)也提供了很多參數讓你可以控制物體的旋轉。
在初始化期間,Unity關節會嘗試保留本體與連接體之間的旋轉差異。這意味著,你旋轉其中一個物體,另一個物體也會跟著旋轉——保持相同的旋轉差異。另外,如果連接體為空,關節會認為旋轉差異為0,可以有效地防止本體旋轉。
對于錨點(anchor),連接錨點(connectedAnchor),自動配置連接錨點(autoConfigureConnectedAnchor),你可以暫時放下。它們對角度偏移沒有影響。而不管本體在連接體的上方、下方還是其它方向——最最重要的東西是它們之前的旋轉差異。
和線性移動一樣,角度偏移也分為三個部分——圍繞著第一軸向(axis),第二軸向(secondaryAxis),以及它們的叉積作旋轉偏移。
角度的偏移通過x軸角度偏移(angularXMotion),y軸角度偏移(angularYMotion),以及z軸角度偏移(angularZMotion)屬性控制。你可以決定每一個軸向上出現偏差時的行為——設置成Free可以完全忽略偏差;設置成Locked可以完全糾正偏差;設置成Limited對偏差作一定限制。
另外,線性限制(linearLimit)、線性彈力限制(linearLimitSpring)屬性控制著所有軸向的線性偏移。而角度偏移的限制更靈活一些,它有6個屬性來控制:
lowAngularXLimit
highAngularXLimit
angularYLimit
angularZLimit
angularXLimitSpring
angularYZLimitSpring
雖然這些屬性已經是不言自明的了,我們還是稍作說明:
x軸是最高級的——你可以控制它的最大和最小旋轉范圍,還可以對這些限制添加彈力。
看一眼下圖,為了方便,我把第一軸向(axis)設置成(0,0,1),這樣就把本地的x軸指向變換到世界空間的z軸方向上。把最高x軸角度偏移限制(highAngularXLimit.limit)設置成30——允許本體(body)順時針旋轉30度;把最低x軸角度偏移限制(lowAngularXLimit.limit)設置成-90——允許本體(body)逆時針方向旋轉90度;
有點搖晃,慶幸的是,你可以看到逆時針旋轉的角度是要大一些(It’s a little shaky, but hopefully, you can see that you can rotate the body much further in a counterclockwise direction)
這對于相對初始旋轉角度的旋轉非常有價值。假設初始角度是45度,最高限制是30度,最低限制是-90度,那么,你的本體(body)將可以圍繞軸向(axis)在[15, 135]度的區間內任意旋轉。
x軸的旋轉限制都有彈跳(bounciness)屬性。當然,最低x軸角度偏移限制的彈跳(lowAngularXLimit.bounciness)會同時作用于兩個限制屬性,而最高x軸角度偏移限制(highAngularXLimit.bounciness)會被忽略。x軸角度限制彈力(angularXLimitSpring)會非常好的符合你的期望。但要注意,它比所有的彈跳(bounciness)屬性的優先級都要高,也就是設置了角度限制彈力后,彈跳屬性就無效了。
從左到右,依次設置bounce = 0,bounce = 1,設置限制彈力(From left to right — limit with bounce = 0, limit with bounce = 1, limit with spring)
其它軸的自由度沒那么高,你不能分別控制最高、最低限制。
例如,設置y軸角度偏移限制(angularYLimit.limit)為45度,等價于最高限制為45度,最低限制為-45度。
你對每個軸都可以使用彈力(spring)屬性。再次說明,一旦使用了彈力屬性。彈跳屬性(angularYLimit.bounciness / angularZLimit.bounciness)將會被忽略。
線性驅動器(Linear drives)
你也可以通過驅動屬性—— 目標位置(targetPosition), x軸驅動(xDrive),y軸驅動(yDrive),z軸驅動(zDrive)——添加三個方向的彈力。
彈力將作用于本地軸向(axis、)方向。并通過目標位置屬性(secondaryAxis以及它們的叉積targetPosition)定義彈力作用的目標位置。
也就是說,你把目標位置設置成(0,10,0),那么你的物體將會沿著y軸向下偏移,并在(0,-10,0)附近落腳。如果你想要在某個方向添加彈力,你需要同時設置對應的目標位置(targetPosition)以及對應驅動器的彈力屬性(positionSpring)。(譯者注:這里為什么會向下偏移呢?我的理解是,這個目標位置其實是錨點(anchor)的偏移向量,而錨點和物體的偏移方向是相反的。也就是說,錨點偏移量為(0,10,0)就相當于物體的偏移量為(0,-10,0)。)
例如,你需要在第二軸向(secondaryAxis)上添加一個彈力,你需要把目標位置的y軸(targetPosition.y)和y軸驅動器的彈力(yDrive.positionSpring)都設置成非零值。
驅動阻尼(drive.positionDamper)和其它的阻尼原理差不多。驅動最大彈力限制(drive.maximumForce)允許你定義把你的物體拉到目標位置的彈力的最大值。
注意:驅動屬性(drives)會配合運動屬性(motion)工作,但不會覆蓋運動屬性。即運動屬性的優先級高于驅動屬性。例如,你設置目標位置x軸(targetPosition.x)的值為-10,以及x軸驅動的彈力(xDrive.positionSpring)為50 。但你把x軸的運動屬性(xMotion)設置成了鎖定的(Locked)。那么,你的這個驅動就不會生效。
你可以通過目標速度(targetVelocity)屬性,給關節添加一個想要的速度。
與目標位置(targetPosition)相似,設置目標速度為(0, -5, 0)將會使速度結果為(0,5,0)。目標速度應用在驅動彈力(drive.positionSpring)和阻尼(drive.positionDamper)不同時為0的情況。當驅動彈力不為0時,彈力會嘗試把物體拉回目標位置,最終會達到平衡而停止移動。
如果你想要驅動作為一個動力源(motor),可以把驅動彈力設置為0,阻尼比設置成非0值(實際值是多少可以根據需要設置)。這樣,彈力失效,物體將獲得目標速度且不會受到阻力(譯者注:這里忽略了重力,實際上會受到重力的影響)。
角度驅動器(Angular drives)
角度驅動會有些棘手。我們從設置關節旋轉的四元數目標旋轉(targetRotation)屬性開始。與目標位置相似,實際上你會獲取一個相反的值。也就是說你的物體會嘗試-targetRotation的變換。如果你想要你的對象擺動(0°, 30°, 0°),你需要一個相反的角度(0°, -30°, 0°),并把它轉成四元數,約為(0, -0.258819, 0, 0.9659258)。
有兩種方式可以實現旋轉:第一是應用一個彈力直接把本體(body)旋轉到目標旋轉屬性位置;第二是應用兩個彈力,其中一個沿著x軸,另外一個去調整剩下的旋轉差異。下圖顯示了兩個粗略近似的結果,你只需要選擇你認為合適的旋轉驅動模式(rotationDriveMode)。如果選擇XYAndZ,則會創建兩個彈力(分別由angularXDrive 、angularYZDrive屬性定義)。如果你選擇Slerp,則只會創建一個彈力(由slerpDrive定義)。
左邊使用XYAndZ模式,右邊使用Slerp模式。可以得到幾乎一樣的結果 (XYAndZ (left) vs Slerp (right) drive modes. The outcome is mostly the same)
和線性驅動類似,目標旋轉角速度(targetAngularVelocity)可以給角度驅動添加動力。
驚喜!目標旋轉角速度實現上就是對象的旋轉角速度,這和目標速度(targetVelocity)屬性情況相反。
到這,基本上把神奇的可配置骨骼的屬性總結完了。在結束之前,我們還會把余下的實踐中比較少用但有價值的東西討論一下。
剩余的其它屬性(All the remaining properties)
我們從投影模式(projectionMode), 投影距離(projectionDistance),和投影角度(projectionAngle)開始。你可以通過它們處理無法解決的關節——你可以點擊PhysX guide去了解更多信息。(譯者注:原文:You can use them to deal with unsolvable joints — check out this PhysX guide to learn more. 有點迷~)。
?。ㄗg者注:Unity官方對這些屬性的解釋是
projectionMode:此屬性定義了當關節意外地超過自身的約束(由于物理引擎無法協調模擬中當前的作用力組合)時如何快速恢復約束。選項為None和Position and Rotation。
Projection Distance:關節超過約束的距離,必須超過此距離才能讓物理引擎嘗試將關節拉回可接受位置。
Projection Angle:關節超過約束的旋轉角度,必須超過此角度才能讓物理引擎嘗試將關節拉回可接受位置。
并且在關節和布娃娃穩定性一節有這樣一句描述:
在極端情況下(例如在墻內不完整生成布娃娃或用大力推動布娃娃),關節解算器無法將布娃娃的剛體組件保持在一起。這種情況下可能導致拉伸。要解決此問題,請使用ConfigurableJoint.projectionMode或CharacterJoint.enableProjection在關節上啟用投影。)
然后,有一個在世界空間中配置(configuredInWorldSpace)的屬性。根據Unity文檔,“如果激活該屬性,所有的目標值將在世界空間計算,而不是對象空間?!保ā癷f enabled, all target values will be calculated in world space instead of the object’s local space.”)然而,在我的測試場景中得到的結果卻是:這個參數只會影響第一軸向(axis)和第二軸向(secondaryAxis)屬性。
最后,還有一個交換本體(swapBodies)屬性。如果啟用此屬性,則會使關節表現得好像組件已附加到連接的剛體(Connected Body)。(If enabled, the two connected rigidbodies will be swapped, as if the joint was attached to the other body.)但是這里,我遇到的唯一情況是驅動的目標位置(targetPosition)和目標旋轉(targetRotation)屬性使用負值(-targetPosition/-targetRotation)代替。
鄉親們!就這樣了!希望對你有用,可配置關節可以讓你的游戲更具機械性。
你可以隨心所欲地在評論區提出你的問題,建議及要求。也不要忘了在Medium, Twitter, Facebook,和Instagram關注Luna Labs。否則,你會錯過我們之后在Unity方面發表見解的文章。
——————————————————————————————————————————————————————————
啊啊??!終于翻譯完了。原來翻譯技術文章這么難??!
總結
以上是生活随笔為你收集整理的深入了解 Unity 可配置关节 Configurable Joints)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Some exceptional cas
- 下一篇: 前端数据脱敏处理方法