1. 程式人生 > >(轉載)BOX2D V2.3.0 使用者手冊中文版(第8章)-關節

(轉載)BOX2D V2.3.0 使用者手冊中文版(第8章)-關節

Chapter 8 關節

8.1 關於

關節用於把物體約束到世界,或約束到其它物體上。在遊戲中,典型例子有木偶,蹺蹺板和滑輪。用不同的方式將關節結合起來使用,可以創造出有趣的運動。
有些關節提供了限制(limit),使你可以控制運動的範圍。有些關節還提供了馬達(motor),它可以以指定的速度驅動關節一直運動,直到你指定了更大的力或扭矩來抵消這種運動。
關節馬達有許多不同的用途。你可以使用關節來控制位置,只要提供一個與目標之距離成正比例的關節速度即可。你還可以模擬關節摩擦:將關節速度置零,並且提供一個小的、但有效的最大力或扭矩;那麼馬達就會努力保持關節不動,直到負載變得過大為止。

8.2 關節定義

每種關節型別都有各自的定義(definition),它們都派生自b2JointDef。所有的關節都連線兩個不同的物體,其中一個物體有可能是靜態的。關節也可以連線兩個static或者kinematic型別的物體,但這沒有任何實際用途,只會浪費處理器時間。
你可以為任何一種關節型別指定使用者資料。你還可以提供一個標記,用於防止用關節相連的物體之間發生碰撞。實際上, 這是預設行為。你也可以通過設定 collideConnected,來允許相連的物體之間發生碰撞。
很多關節定義需要你提供一些幾何資料。一個關節常常需要一個錨點(anchor point)來定義,這是固定於相接物體中的點。Box2D要求這些點在區域性座標系中指定,這樣,即便當前物體的變化違反了關節約束(joint constraint),關節還是可以被指定——這通常會發生在遊戲儲存或載入進度時。另外,有些關節定義需要知道物體之間預設的相對角度。這樣才能正確地約束旋轉。
初始化幾何資料可能有些乏味。所以很多關節提供了初始化函式,使用當前的物體的形狀,來消除大部分工作。然而,這些初始化函式通常只應用於原型,在產品程式碼中應該直接地定義幾何資料。這能使關節行為更具健壯性。
其餘的關節定義資料依賴於關節的型別。下面我們來介紹它們。

8.3 關節工廠

關節使用world的工廠方法來建立和摧毀。這引入一個老問題:

注意
不要使用new或malloc在棧(stack)或堆(heap)中建立關節。你想建立或摧毀物體和關節,必須使用b2World類中對應的建立或摧毀函式。

這裡有個例子,展示了旋轉關節(revolute joint)的生命週期:

b2RevoluteJointDef jointDef;
jointDef.bodyA = myBodyA;
jointDef.bodyB = myBodyB;
jointDef.anchorPoint = myBodyA->GetCenterPosition();
b2RevoluteJoint* joint = (b2RevoluteJoint*)myWorld->CreateJoint(&jointDef);
...
do stuff ... myWorld->DestroyJoint(joint); joint = NULL;

一個很好的習慣:當物件摧毀後,就將對應的指標清零。這樣的話,當你試圖再次使用這個指標時,程式會以一種可控的方式崩潰。

(譯註:野指標雖然不一定會使程式崩潰,但它所帶來的問題非常難以除錯。空指標雖然一定會讓程式崩潰,但是除錯起來很簡單。)

關節的生命週期並不簡單,要特別留心下面的警告。

注意
物體被摧毀時,依附其上的關節也會被摧毀。

上面的注意並非時時必要。你可以組織好自己的遊戲引擎,保證物體被摧毀前,依附其上的關節已經先被摧毀。在這種情況下,你沒有必要實現監聽類 (listener class),去監聽物體被摧毀的事件。更多細節請看隱式摧毀(Implicit Destruction)那小節。

8.4 使用關節

在許多模擬中,關節被建立之後,直到摧毀也不會再被訪問。然而,關節中包含著很多有用的資料,使你可以創建出豐富的模擬。
首先,你可以在關節上得到物體、錨點和使用者資料。

b2Body* GetBodyA();
b2Body* GetBodyB();
b2Vec2 GetAnchorA();
b2Vec2 GetAnchorB();
void* GetUserData();

所有的關節都有反作用力和反扭矩,這個反作用力應用於 body2 的錨點之上。你可以用反作用力來折斷關節(break joints),或引發其它遊戲事件。這些函式可能需要做一些計算,所以沒有必要就不要去呼叫它們。

b2Vec2 GetReactionForce();
float32 GetReactionTorque();

8.5 距離關節

距離關節是最簡單的關節之一, 它是說, 兩個物體上面各自有一點,兩點之間的距離必須固定不變。當你指定一個距離關節時,兩個物體必須已在應有的位置上。之後,你指定世界座標中的兩個錨點。第一個錨點連線 到物體1,第二個錨點連線到物體2。這兩點隱含了距離約束的長度。

這是一個距離關節定義的例子。這種情況下, 我們允許物體碰撞。

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

距離關節也可以是軟的,就像用橡皮筋來連線。看看testbed中的Web例子,就可以知道它有什麼樣的行為。
要使關節有彈性,可以調節一下定義中的兩個常數:頻率(frequency)和阻尼率(damping ratio)。將頻率想象成諧振子(harmonic oscillator,比如吉他弦)振動的快慢。頻率使用單位赫茲(Hertz)來指定。典型情況下,關節頻率要小於時間步(time step)頻率的一半。比如每秒執行60次時間步, 距離關節的頻率就要小於30赫茲。這樣做的理由可以參考Nyquist頻率理論。
阻尼率無單位,典型是在0到1之間,也可以更大。1是阻尼率的臨界值,當阻尼率為1時,沒有振動。

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

8.6 旋轉關節

旋轉關節會強制兩個物體共享一個錨點,即所謂鉸接點。旋轉關節只有一個自由度:兩個物體的相對旋轉。這稱之為關節角。

要指定一個旋轉關節,你需要提供兩個物體以及世界座標的一個錨點。初始化函式會假定物體已經在正確的位置了。
在此例中,兩個物體被旋轉關節連線起來,其中鉸接點為第一個物體的質心。

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

在 body2 逆時針旋轉時,關節角為正。像所有 Box2D 中的角度一樣,旋轉角也是弧度制的。按規定,使用Initialize() 建立關節時,無論兩個物體當前的角度怎樣,旋轉關節角都為0。
有時候,你可能需要控制關節角。為此,旋轉關節可以隨意地模擬關節限制和馬達。
關節限制(joint limit)會強制關節角度保持在一定範圍內,它會應用足夠的扭矩來保持這個範圍。0應該在範圍內,否則在開始模擬時關節會有點傾斜。
關節馬達允許你指定關節的角速度(角度的時間導數),速度可正可負。馬達可以有產生無限大的力,但這通常是沒有必要的。想想那個經典問題:
“當一個不可抵抗的力作用在一個不可移動的物體上,會發生什麼?”
我可以告訴你這並不有趣。所以你應該為關節馬達提供一個最大扭矩。關節馬達會維持在指定的速度,除非其所需的扭矩超出了最大扭矩。當超出最大扭矩時,關節會慢下來,甚至會反向運動。
你還可以使用關節馬達來模擬關節摩擦。只要把關節速度設為0,並將最大扭矩設為很小但有效的值。這樣馬達會試圖阻止關節旋轉,除非有過大的負載。
這裡是對上面旋轉關節定義的修訂; 這次,關節擁有一個限制以及一個馬達,後者用於模擬摩擦。

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

你可以訪問旋轉關節的角度,速度和馬達扭矩。

float32 GetJointAngle() const;
float32 GetJointSpeed() const;
float32 GetMotorTorque() const;

每次執行step後,你也可以更新馬達的引數。

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

關節馬達有些有趣的功能。你可以在每個時間步中更新關節的速度,使得它像正弦波或者任意一個你想要的函式那樣前後擺動。

... Game Loop Begin ...
myJoint->SetMotorSpeed(cosf(0.5f * time));
... Game Loop End ...

你也可以用關節馬達來跟蹤你想要的關節角。比如:

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

通常你的增益引數不應太大,不然關節會變得不穩定。

8.7 移動關節

移動關節(prismatic joint)允許兩個物體沿指定軸相對移動,它會阻止相對旋轉。因此,移動關節只有一個自由度。

移動關節的定義有些類似於旋轉關節;只是轉動角度換成了平移,扭矩換成了力。以這樣的類比,我們來看一個帶有關節限制以及馬達摩擦的移動關節定義:

b2PrismaticJointDef jointDef;
b2Vec2 worldAxis(1.0f, 0.0f);
jointDef.Initialize(myBodyA, myBodyB, myBodyA->GetWorldCenter(), worldAxis);
jointDef.lowerTranslation = -5.0f;
jointDef.upperTranslation = 2.5f;
jointDef.enableLimit = true;
jointDef.maxMotorForce = 1.0f;
jointDef.motorSpeed = 0.0f;
jointDef.enableMotor = true;

旋轉關節隱含著一個從螢幕射出的軸,而移動關節明確地需要一個平行於螢幕的軸。這個軸會固定於兩個物體之上,沿著它們的運動方向。
就像旋轉關節一樣,當使用 Initialize() 建立移動關節時,移動為0。所以要確保0在你的移動限制範圍內。
移動關節的用法跟旋轉關節類似。這是相應的成員函式:

float32 GetJointTranslation() const;
float32 GetJointSpeed() const;
float32 GetMotorForce() const;
void SetMotorSpeed(float32 speed);
void SetMotorForce(float32 force);

8.8 滑輪關節

滑輪關節用於建立理想的滑輪,它將兩個物體接地(ground)並彼此連線。這樣,當一個物體上升,另一個物體就會下降。滑輪的繩子長度取決於初始配置。

length1 + length2 == constant

你還可以提供一個係數(ratio)來模擬滑輪組,這會使滑輪一側的運動比另一側要快。同時,一側的約束力也比另一側要小。你也可以用這個來建立機械槓桿。

length1 + ratio * length2 == constant

舉個例子,如果係數是2,那麼 length1 的變化會是 length2 的兩倍。另外連線body1的繩子的約束力將會是連線body2繩子的一半。

滑輪的一側完全展開時,另一側的繩子長度為零,這可能會出問題。此時,約束方程將變得奇異(糟糕)。你應該配置碰撞形狀以避免這種情況。
這是一個滑輪定義的例子:

b2Vec2 anchor1 = myBody1->GetWorldCenter();
b2Vec2 anchor2 = myBody2->GetWorldCenter();
b2Vec2 groundAnchor1(p1.x, p1.y + 10.0f);
b2Vec2 groundAnchor2(p2.x, p2.y + 12.0f);
float32 ratio = 1.0f;
b2PulleyJointDef jointDef;
jointDef.Initialize(myBody1, myBody2, groundAnchor1, groundAnchor2, anchor1, anchor2, ratio);

滑輪關節提供函式得到當前長度。

float32 GetLengthA() const;
float32 GetLengthB() const;

8.9 齒輪關節

如果你想建立複雜的機械裝置,可能需要齒輪。原則上,在 Box2D 中你可以用複雜的形狀來模擬輪齒,但這並不十分高效,而且可能有些乏味。另外,你還得小心地排列齒輪,保證輪齒能平穩地齧合。Box2D 提供了一個建立齒輪的更簡單的方法:齒輪關節。

齒輪關節只能連線旋轉關節和移動關節。
類似於滑輪系數,你可以指定一個齒輪系數(ratio),齒輪系數可以為負。另外值得注意的是,當一個是旋轉關節(有角度的)而另一個是移動關節(平移)時,齒輪系數有長度單位,或者是長度單位的倒數。

coordinate1 + ratio * coordinate2 == constant

這是一個齒輪關節的例子。物體myBodyA和myBodyB來自於兩個關節,並且它們不是同一個物體。

b2GearJointDef jointDef;
jointDef.bodyA = myBodyA;
jointDef.bodyB = myBodyB;
jointDef.joint1 = myRevoluteJoint;
jointDef.joint2 = myPrismaticJoint;
jointDef.ratio = 2.0f * b2_pi / myLength;

注意,齒輪關節依賴於兩個其它關節,這是脆弱的:當其它關節被刪除了會發生什麼?

注意
齒輪關節總應該先於旋轉或移動關節被刪除。否則你的程式碼將會因訪問齒輪關節的孤兒關節指標而導致崩潰。另外齒輪關節也應該在任何相關物體被刪除之前刪除。

8.10 滑鼠關節

在testbed例子中,滑鼠關節用於通過滑鼠來操控物體。它試圖將物體拖向當前滑鼠游標的位置。而在旋轉方面就沒有限制。
滑鼠關節的定義需要一個目標點(target point),最大力(maximum force),頻率(frequency),阻尼率(damping ratio)。目標點最開始與物體的錨點重合。最大力用於防止在多個動態物體相互作用時的激烈反應。你想將最大力設為多大就多大。頻率和阻尼率用於創造一種彈性效果,就跟距離關節類似。
許多使用者為了遊戲的可玩性,會試圖修改滑鼠關節。使用者常常希望滑鼠關節有即時反應,精確的去到某個點。這情況下,滑鼠關節表現並不好。你可以考慮一下用kinematic物體來替代。

8.11 輪子關節

輪子關節限制bodyB上的一個點到bodyA的一條線上。輪子關節也提供懸置彈簧的效果。細節參見b2WheelJoint.h和Car.h。

8.12 焊接關節

焊接關節的用途是使兩個物體不能相對運動。看看testbed中的Cantilever例子,可以知道焊接關節有怎麼樣的表現。
用焊接關節來定義一個可分裂物體,這想法很誘人。但是,由於Box2D的迭代求解,關節焊得有點不穩。因此用焊接關節連線起來的物體會有所擺動。
建立可裂物體的更好方法是使用單個的物體,上面有很多fixture。 當物體分裂時,你可以刪掉原物體的一個fixture,並重新在一個新的物體上建立它。參考一下testbed中的Breakable例子。

8.13 繩子關節

繩子關機限制了兩個點之間的最大距離。它能夠阻止連線的物體之間的拉伸,即使在很大的負載下。細節參見b2RopeJoint.h和RopeJoint.h。

8.14 摩擦關節

摩擦關節被用於模擬上下摩擦。關節提供2D的傳統摩擦和角度摩擦。細節參見b2FrictionJoint.h 和ApplyForce.h。

8.15 馬達關節

馬達關節通過指定目標位置和旋轉偏移來控制物體的移動。你能設定最大的馬達力和力矩來到達目標位置和旋轉。如果物體被阻塞,它將停下來,接觸力與最大的馬達力和力矩成正比。細節參見b2MotorJoint和MotorJoint.h。

相關推薦

(轉載)BOX2D V2.3.0 使用者手冊中文版(8)-關節

Chapter 8 關節 8.1 關於 關節用於把物體約束到世界,或約束到其它物體上。在遊戲中,典型例子有木偶,蹺蹺板和滑輪。用不同的方式將關節結合起來使用,可以創造出有趣的運動。 有些關節提供了限制(limit),使你可以控制運動的範圍。有些關節還提

(轉載)Box2D v2.3.0 使用者手冊中文版(1)-導言

Chapter 1 導言 1.1 關於 Box2D 是一個用於遊戲的 2D 剛體模擬庫。程式設計師可以在他們的遊戲裡使用它,它可以使物體的運動更加真實,並讓遊戲世界看起來更具互動性。從遊戲引擎的視角來看,物理引擎就是一個程式性動畫 (procedural

(轉載)BOX2D V2.3.0 使用者手冊中文版(12)-除錯繪圖

Chapter 12 除錯繪圖 實現 b2DebugDraw 可得到物理世界的細部圖,這裡是可用的實體: • 形狀輪廓 • 關節連通性 • broad-phase axis-aligned bounding boxes (AABBs) •

(轉載)BOX2D V2.3.0 使用者手冊中文版(11)-雜項

Chapter 11 雜項 11.1 使用者資料 b2Fixture, b2Body 和 b2Joint 類都允許你通過一個 void 指標來附加使用者資料。當你測試Box2D資料結構,並使其跟自己遊戲引擎中的物件結合起來時,這樣做是比較方便的。 舉個

《MySQL 8.0 參考手冊 14 MySQL 資料字典

文章目錄 14.1 資料字典模式 資料字典升級過程 使用 MySQL 除錯版本檢視資料字典表 14.2 刪除基於檔案的元資料 14.3 事務型資料字典 14.4 資料字典快取 14.5 I

C Primer Plus (五版)中文版—— 8 字元輸入/輸出和輸入確認

8.1  單字元 I/O:getchar() 和 putchar() getchar() 和 putchar() 每次輸入和輸出一個字元。一個輸入回顯例子: /*使用一個while迴圈,該迴圈在遇到#時終止*/ int main(void) { char ch; while

C Primer Plus (五版)中文版—— 8 字元輸入/輸出和輸入確認

8.1  單字元 I/O:getchar() 和 putchar() getchar() 和 putchar() 每次輸入和輸出一個字元。一個輸入回顯例子: /*使用一個while迴圈,該迴圈在遇到#時終止*/ int main(void) { char ch; w

Mongodb 3.0+操作手冊 純手打

許可權控制 使用者概念 Mongodb的使用者是由 使用者名稱+所屬庫名組成 例如: 登入mongo testdb1 ,建立使用者testuser 登入mongo testdb2

Protocol Buffers 3.0 技術手冊

簡介 Protocol Buffers 是 google 的一種資料交換的格式,它獨立於語言,獨立於平臺。google 提供了多種語言的實現:java、c#、c++、go 和 python,每一種實現都包含了相應語言的編譯器以及庫檔案。由於它

Android實現新浪微博SSO授權登入分享文字圖片等功能(WEIBO_ANDROID_SDK V2.3.0

新浪開發平臺:http://open.weibo.com 新浪微博分享目前分為兩種途徑: 1,直接在自己的APP,彈出類似Dialog(sina整合)來完成授權,授權成功後可直接分享內容,全程都是在自己APP裡完成分享。老版本的微博SDK中集成了彈出分享Dialog(

Maatwebsite / Laravel-Excel 3.0教程手冊 連結地址

功能: 1.輕鬆將集合匯出到Excel。為Laravel系列增壓並將其直接匯出到Excel或CSV文件。出口從未如此簡單。 2.增壓出口。使用自動分塊匯出查詢以獲得更好的效能。您向我們提供查詢,我們處理效能。匯出更大的資料集?不用擔心,Laravel Excel有你的背。您

【OpenCV入門教程之一】 安裝OpenCV:OpenCV 3.0、OpenCV 2.4.8、OpenCV 2.4.9 +VS 開發環境配置

毛星雲,網路ID「淺墨」,90後,熱愛遊戲開發、遊戲引擎、計算機圖形、實時渲染等技術,就職於騰訊互娛。 微軟最有價值專家 著作《Windows遊戲程式設計之從零開始》、《OpenCV3程式設計入門》 碩士就讀於南京航空航天大學航天學院(2013級碩士研究生),已於2016年三月畢業。本科

【轉】【OpenCV入門教程之一】 安裝OpenCV:OpenCV 3.0、OpenCV 2.4.8、OpenCV 2.4.9 +VS 開發環境配置

本系列文章由出品,轉載請註明出處。  寫作當前博文時配套使用的OpenCV版本: 2.4.8、2.4.9、3.0 ( 2014年4月28更新OpenCV 2.4.9的配置。 2014年9月12更新OpenCV 3.0的配置 2014年9月12日本文第

MyBatis 3(中文版) 使用註解配置SQL對映器

例如,看下面的findStudentById()和findAllStudents()方法: @Select("SELECT * FROM STUDENTS WHERE STUD_ID=#{studId}")   @Results(   {       @Result(id = true

Opencv(3.0.0beta)+Python(2.7.8 64bit) 簡單詳細,一遍成功

看到很多配置的文章,都沒法正常走完流程 使用到的資源,都是今天為止最新的: 步驟: 1.下載最新Python3.4.2後安裝opencv時說python版本不對,所以解除安裝了Python3.4.2重新下載了python-2.7.8安裝,注意是64位的,32位的我沒

Python基礎教程(3版)中文版 7 類和物件(筆記)

                                  7 類和物件 1.物件魔法  1.多型  2.多型和方法   3.封裝  4.繼承 2.類  1.類是什麼 類 型別 2.建立自定義類  class Person:      def set_

15,MySQL 8.0參考手冊 4.6.8 mysqlbinlog

4.6.8.1 mysqlbinlog十六進位制轉儲格式4.6.8.2 mysqlbinlog行事件顯示4.6.8.3使用mysqlbinlog備份二進位制日誌檔案4.6.8.4指定mysqlbinlog伺服器ID伺服器的二進位制日誌由包含描述資料庫內容修改的“ 事件 ”的檔

Netty In Action中文版 - :Transports(傳輸)

duplicate pipeline 客戶 下列表 bytes 線程安全 get 工具 jsb 本章內容 Transports(傳輸)NIO(non-blocking IO,New IO), OIO(Old IO,blocking IO), Local(本地),

《python學習手冊34 異常對象

indexer app interrupt 類定義 ase num pac chan 捕捉 基於字符串的異常 python在2.6之前可以使用字符串來定義異常,並且是通過對象標識符來匹配的(即通過is 而不是==) myexc = "My excetion string"

Learning Spark中文版----RDD編程(2)

翻譯 瓶頸 並集 ria multi guide 第六章 rabl 函數式 Common Transformations and Actions ??本章中,我們瀏覽了Spark中大多數常見的transformation(轉換)和action(動作)。在包含特定數據類型的R