天天看點

box2d關節

8.1  關于(About)

連接配接器起到限制世界當中物體自身或物體之間的作用。典型的例子是遊戲中的木偶,跷跷闆,滑輪。連接配接器可以用很多不同的方式建立有趣的運動。

有些連接配接器提供了限制,你可以以此來控制運動的範圍。有的連接配接器提供了用指定速度驅動物體的馬達,直到有一個更大的力或扭矩來進行抵消。很多地方可以使用馬達。可以使用馬達來控制位置,隻要通過指定連接配接器中目标和目前位置成正比例的速度值即可。還可以使用馬達模拟連接配接器摩擦:設定連接配接器速度為零然後提供一個小的但是有效的最大力或扭矩的馬達。然後馬達将會嘗試着讓連接配接器靜止直到負載變的足夠大。

8.2  連接配接器定義(The Joint Definition)

每一個連接配接器都有一個定義派生自b2JointDef。所有連接配接器在不同的物體之間進行連接配接。一個物體可能是靜态的。連接配接器也可以在靜态物體以及(或者)運動學物體之間進行連接配接,但是不會有任何效果隻會占用處理時間。

你可以指定使用者資料(user data)為任何連接配接器類型,并且可以提供一個标志來阻止所附加的物體彼此碰撞。事實上這是預設屬性你必須設定collideConnected的布爾值才能實作連接配接着的兩個物體碰撞。

很多連接配接器的定義需要提供一些幾何資料。通常一個連接配接器通過錨定來定義。這些點附加在物體上。Box2D需要這些點在本地坐标中設定。即便目前物體自身的變換違反了限制條件還是可以指定連接配接器–普遍的情況是遊戲的存儲和載入。另外,有些連接配接器的定義需要知道兩個物體之間預設的相對角度。這才能正确的限制旋轉。

初始化幾何資料很乏味,是以很多連接配接器提供了初始化方法來減少大部分工作。然而,這些初始化方法通常隻是用來做原型。産品代碼需要直接定義幾何體。這将使得連接配接器的行為更具健壯性。

接下來的連接配接器的定義取決于連接配接器的類型。現在我們開始了解。

8.3  連接配接器工廠(Joint Factory)

使用世界工廠方法來建立和銷毀連接配接器。這又讓人想起一個老問題:

警告

不要使用new或者malloc關鍵字在棧或堆裡建立連接配接器。你必須要像建立和銷毀物體一樣,對連接配接器操作也要使用b2World類型中建立和銷毀方法。

這裡是一個旋轉連接配接器生命周期的例子:

  1. b2RevoluteJointDef jointDef;
  2. jointDef.bodyA = myBodyA;
  3. jointDef.bodyB = myBodyB;
  4. jointDef.anchorPoint = myBodyA->GetCenterPosition();
  5. b2RevoluteJoint* joint = (b2RevoluteJoint*)myWorld->CreateJoint(&jointDef);
  6. ...do stuff...
  7. myWorld->DestroyJoint(joint);
  8. joint = NULL;

複制代碼

當指針指向的對象被銷毀的時候指派為空無論何時都是一個好習慣。這使得如果你重用這個指針,程式會以一種可控的方式崩潰。

連接配接器的生命周期并不簡單。最好注意下面這條:

注意

當附加的物體銷毀時,連接配接器才會被銷毀。

上面的注意并非每次都必要。你可以管理遊戲引擎來保證附加的物體銷毀之前連接配接先銷毀就可以了。在這個例子中你不要實作監聽器類型(listener class)。更多細節請看11.1小節隐式銷毀(Implicit Destruction)。

8.4  使用連接配接器(Using Joints)

在很多模拟場景中,建立完連接配接器之後直到銷毀再也沒有被通路過。然而,連接配接器中包含了很多有用的資料可以使模拟更加豐富多彩。

首先,你可以擷取物體,錨定點,和連接配接器中的使用者資料(user data)。

  1. b2Body* GetBodyA();
  2. b2Body* GetBodyB();
  3. b2Vec2 GetAnchorA();
  4. b2Vec2 GetAnchorB();
  5. void* GetUserData();

複制代碼

所有連接配接器都有反作用力和反扭矩。這個反作用力應用在錨定點的body 2上。你可以使用反作用力來觸發其它連接配接器或者遊戲事件中的觸發器。這些方法可以做一些計算,那麼如果你不需要這些結果你可以不用調用它們。

  1. b2Vec2 GetReactionForce();
  2. float32 GetReactionTorque();

複制代碼

8.5  距離連接配接器(Distance Joint)

其中最簡單的連接配接器,是通常所說的在兩個物體上兩點之間保持一定距離的距離連接配接器。當你指定一個距離連接配接器時,相應的兩個物體應該已經在應有的位置上了。然後在世界坐标系中指定兩個錨定點。第一個錨定點連接配接body1,第二個錨定點連接配接body2。這些點代表着應該保持的距離的常量。

box2d關節

這裡給出一個距離連接配接器的定義。在這個例子中我們決定允許物體之間有碰撞。

  1. b2DistanceJointDef jointDef;
  2. jointDef.Initialize(myBodyA, myBodyB, worldAnchorOnBodyA, worldAnchorOnBodyB);
  3. jointDef.collideConnected = true;

複制代碼

距離連接配接器可以變成軟的,就像連接配接一個彈簧一樣。可以在testbed中通過Web例子看看到底是什麼樣的行為。

在定義中通過調節:頻率(frequency)和阻尼率(damping ratio)兩個常量來取得柔軟的效果。想象一下諧波震蕩器中的頻率(像琴弦一樣)。頻率以赫茲(Hertz)為機關。通常頻率應該比半個時間步長要短。是以如果你的時間步長是60Hz,那麼距離連接配接器的頻率應該小于30Hz。原因是由奈奎斯特頻率(Nyquist frequency)有關。

阻尼率屬于無機關量綱(non-dimensional)并且通常在0到1之間,但是可以更大。1是阻尼的臨界值(此時無震蕩)。

  1. jointDef.frequencyHz = 4.0f;
  2. jointDef.dampingRatio = 0.5f;

複制代碼

8.6  旋轉連接配接器(Revolute Joint)

旋轉連接配接器同時作用于兩個物體,并使兩個物體共享同一個錨定點,經常稱之為鉸鍊點(hinge point)。旋轉連接配接器有一個自由度:相對于兩個物體的旋轉來說。這個角稱為連接配接角(joint angle)。

box2d關節

為了定制一個旋轉連接配接,你需要在世界中提供兩個物體和一個簡單的錨定點。初始化方法假設物體已經在正确的位置。

在這個例子中,兩個物體通過旋轉連接配接器以第一個物體的質心作為鉸鍊點(hinge Point)連接配接在一起。

  1. b2RevoluteJointDef jointDef;
  2. jointDef.Initialize(myBodyA, myBodyB, myBodyA->GetWorldCenter());

複制代碼

當bodyB逆時針旋轉的時候,轉動連接配接器的角度為正值。就像Box2D中的所有其它角度一樣,旋轉角度以弧度為基準。一般來說,旋轉連接配接器使用Initialize()方法建立完成之後,旋轉連接配接器的角度為零,和兩個物體目前的角度無關。

在一些場合下你可能希望控制連接配接角(joint angle)。為此,旋轉連接配接器可選的模拟連接配接限制和(或)馬達。

連接配接限制作用力可将連接配接角限制在一定範圍内。為此将會盡可能的應用扭矩來達到效果。限制最小值應該包括零,否則在開始模拟的時候連接配接将會比較困難。

連接配接器馬達允許你指定角速度(角的時間導數)。速度可以是正值或負值。雖然馬達可以産生無限大的力,但是通常這麼做是不可取的。想想那個永恒的問題:

當一個無窮大的力碰到一個不可移動的物體上,會發生什麼呢?

我可以告訴你這并不好笑。是以你應該為連接配接馬達提供一個最大值的力。連接配接馬達将會維持特定的速度除非扭矩超過了指定的最大值。當最大的扭矩值被超過時,連接配接器将會慢下來或者反向運動。

你可以使用連接配接馬達來模拟連接配接摩擦力。隻要設定連接配接速度為零,然後設定最大的扭矩為某個很小的值,隻要能顯示出效果就可以。這樣馬達将會嘗試阻止連接配接旋轉,除非産生明顯的過載。

這裡對上面旋轉連接配接器做一個修訂;此時,連接配接器既有限制也有馬達。馬達用來模拟連接配接摩擦。

  1. b2RevoluteJointDef jointDef;
  2. jointDef.Initialize(bodyA, bodyB, myBodyA->GetWorldCenter());
  3. jointDef.lowerAngle = -0.5f * b2_pi;  //-90 degrees
  4. jointDef.upperAngle = 0.25f* b2_pi;  // 45 degrees
  5. jointDef.enableLimit = true;
  6. jointDef.maxMotorTorque = 10.0f;
  7. jointDef.motorSpeed = 0.0f;
  8. jointDef.enableMotor = true;

複制代碼

你可以擷取一個旋轉連接配接器的角度,速度以及馬達扭矩。

  1. float32 GetJointAngle() const;
  2. float32 GetJointSpeed() const;
  3. float32 GetMotorTorque() const;

複制代碼

你可以在每一步對馬達控制器參數進行更新。

  1. void SetMotorSpeed(float32 speed);void SetMaxMotorTorque(float32 torque);

複制代碼

連接配接馬達有一些有趣的功能。你可以在每個時間步長進行設定更新,然後連接配接就會像正弦波那樣前後擺動或者根據你自定義的方法做一些改變。

  1. ...Game Loop Begin...
  2. float32 angleError = myJoint->GetJointAngle() - angleTarget;
  3. float32 gain = 0.1f;
  4. myJoint->SetMotorSpeed(-gain*angleError);
  5. ...Game Loop End...

複制代碼

通常你的增益參數不能太大。否則連接配接或許會變的不穩定。

8.7  平移連接配接(Prismatic Joint)

平移連接配接允許相關聯的兩個物體沿着特定的坐标軸進行平移。平移連接配接阻止相對旋轉。是以平移連接配接隻有一個自由度。

box2d關節

平移連接配接定義的描述類似于旋轉連接配接;隻是把角度替換成平移以及把扭矩替換成力矩。以此類推讓我們看一個平移連接配接器中的連接配接限制和摩擦馬達如何定義的:

  1. b2PrismaticJointDef jointDef;
  2. b2Vec2 worldAxis(1.0f, 0.0f);
  3. jointDef.Initialize(myBodyA, myBodyB, myBodyA->GetWorldCenter(), worldAxis);
  4. jointDef.lowerTranslation = -5.0f;
  5. jointDef.upperTranslation = 2.5f;
  6. jointDef.enableLimit = true;
  7. jointDef.maxMotorForce = 1.0f;
  8. jointDef.motorSpeed = 0.0f;
  9. jointDef.enableMotor = true;

複制代碼

旋轉連接配接器有一個從螢幕射出的隐藏的軸。平移連接配接器需要一個明确的和螢幕平行的軸。并且這個軸固定在兩個物體上并且跟随它們一起移動。就像旋轉連接配接一樣,平移連接配接當使用Initilialize()方法建立時初始平移為零。是以確定零在你的最低和最高平移範圍内。

使用平移連接配接器就像使用旋轉連接配接器一樣。這裡給出一些相關方法:

  1. float32 GetJointTranslation() const;
  2. float32 GetJointSpeed() const;
  3. float32 GetMotorForce() const;
  4. void SetMotorSpeed(float32 speed);
  5. void SetMotorForce(float32 force);

複制代碼

8.8  滑輪連接配接器(Pulley Joint)

滑輪連接配接器用來建立一個理想的滑輪。滑輪連接配接器将兩個物體之間進行連接配接并同時連接配接到地面上。當一個物體上升,另一個物體就下降。滑輪連接配接器繩子的長度取決于初始化的配置。

  1. length1 + length2 == constant

複制代碼

你可以提供一個比值(ratio)來模拟木塊和滑車(block and tackle)。這會出現滑輪一側繩子比另一側拉長的速度快。于此同時,這一側的力也會比另一側的力要較小。你可以用這種方法來建立機械杠杆(mechanical leverage)。

  1. length1 + ratio*length2 == constant

複制代碼

比如說,如果比值(ratio)是2,那麼length1的變化将是length2的兩倍。同樣附加在body1上的力将是附加在body2上力的一半兒。

box2d關節

當滑輪一側的繩子被完全展開時可能會出現麻煩。另一側的繩子長度将會是零。此時恒等式将會變的奇特(糟糕)。你應該設定形狀碰撞來阻止這種情況的發生。

這裡給出一個滑輪連接配接器的定義:

  1. b2Vec2 anchor1 = myBody1->GetWorldCenter();
  2. b2Vec2 anchor2 = myBody2->GetWorldCenter();
  3. b2Vec2 groundAnchor1(p1.x, p1.y + 10.0f);
  4. b2Vec2 groundAnchor2(p2.x, p2.y + 12.0f);
  5. float32 ratio = 1.0f;
  6. b2PulleyJointDef jointDef;
  7. jointDef.Initialize(myBody1, myBody2, groundAnchor1, goundAnchor2, anch

複制代碼

滑輪連接配接器提供了擷取目前繩子長度的方法。

  1. float32 GetLengthA() const;
  2. float32 GetLengthB() const;

複制代碼

8.9  齒輪連接配接器(Gear Joint)

如果你想建立一個精密的機械東東你也許會想起使用齒輪。在Box2D中,原則上來說你可以使用幾何重疊來模拟帶牙齒的齒輪。這并不高效而且還很乏味。你還要認真的考慮排列齒輪的牙齒以保證其平滑。Box2D有一個簡單的方法來建立齒輪:齒輪連接配接器(gear joint)。

box2d關節

齒輪連接配接器隻能連接配接旋轉和(或者)平移連接配接器。

就像滑輪比值(pulley ratio)一樣,你可以指定一個齒輪比值(gear ratio),此時齒輪比值可以設定成負值。仍然要注意的是當其中一個連接配接器是旋轉連接配接器(角度)和另一個是平移連接配接器(平移)的時候,這時齒輪比将會有機關長度或者機關長度的倒數(one over length)。

  1. coordinate1 + ratio * coordinate2 == constant

複制代碼

這裡有一個齒輪連接配接器的例子。物體myBodyA和物體myBodyB是任意兩個連接配接器,隻要它們不是附加的同一個物體就行。

  1. b2GearJointDef jointDef;
  2. jointDef.bodyA = myBodyA;
  3. jointDef.bodyB = myBodyB;
  4. jointDef.joint1 = myRevoluteJoint;
  5. jointDef.joint2 = myPrismaticJoint;
  6. jointDef.ratio = 2.0f * b2_pi / myLength;

複制代碼

注意齒輪連接配接器依賴其它兩個連接配接器。這裡是薄弱的地方。如果删除其它連接配接器将會發生什麼?

警告

在删除旋轉/平移連接配接器之前總是先删除齒輪連接配接器。否則你的代碼将會由于齒輪指針變為無效指針以糟糕的方式崩潰。是以在你删除任何其它連接配接器之前應該先删除齒輪連接配接器。

8.10  滑鼠連接配接器(Mouse Joint)

在testbed例子中,使用滑鼠連接配接器操縱物體。它嘗試在物體上驅動一個點,拖向目前滑鼠位置。在旋轉方面沒有限制。

滑鼠連接配接器定義有目标點(target point),最大力矩(maximum force),頻率(frequency)以及阻尼率(damping ratio)。目标點最初與物體的錨定點重合。最大力矩防止多個動态物體作用時的激烈反應。你可以把它設定的足夠大。頻率和阻尼率是用來對距離連接配接器(distance joint)建立彈簧/減震器類似效果的。

許多使用者為了遊戲的可玩性想嘗試修改滑鼠連接配接器。使用者通常希望能夠實作精确定位和及時相應。在這方面滑鼠連接配接器工作的并不是很好。你可以考慮一下動力學物體(kinematic bodies)進行代替。

8.11  滾輪連接配接器(Wheel Joint)

滾輪連接配接器限制了bodyB上的一個點系一個繩子,繩子另一邊系在bodyA上。滾輪連接配接器也提供了一個懸挂彈簧(suspension spring)。詳細檢視b2WheelJoint.h和Car.h檔案。

box2d關節

8.12  焊接連接配接器(Weld Joint)

焊接連接配接器嘗試限制兩個物體之間所有相對運動。可以參看testbed例子當中Cantilever.h檔案關于焊接連接配接器的具體行為。

使用焊接連接配接器定義一個易碎的物體結構很有吸引力。即便如此,Box2D的求解器是進行疊代的,是以焊接連接配接器不是很穩定。是以使用焊接連接配接器連接配接起來的物體鍊看起來将會很柔軟。

是以用附有多個定制器的單個物體來建立易碎物體更合适。當物體摔碎時,你可以銷毀一個定制器,在一個新物體上重新建立它。詳情參見testbed中的Breakable例子。

8.13  繩索連接配接器(Rope Joint)

繩索連接配接器限制了兩個點之間最大距離。即便是高負載,繩索連接配接器也防止了物體鍊的過度拉伸。詳情參見b2RopeJoint.h和RopeJoint.h檔案。

8.14  摩擦連接配接器(Friction Joint)

摩擦連接配接器用來進行自上而下摩擦(top-down friction)。摩擦連接配接器提供了2D的平移摩擦和角摩擦。詳情參見b2FrictionJoint.h和ApplyForce.h檔案。

摘自:http://www.ohcoder.com/post/2012-04-25/17254389

繼續閱讀