Box2D v2.1.0用戶手冊翻譯 - 第02章 Hello Box2D

轉自Complex_Ok

內容很多摘自
Aman JIANG(江超宇)翻譯的Box2D v2.0.1 用戶手冊

第02章 Hello Box2D

Box2D的發佈包中有個Hello World程序。程序創建了一個大大的地面盒(ground box)和一個小小的動態盒(dynamic box)。盒子的位置隨着時間的變化而變化。代碼沒有涉及到圖形界面,你只能在控制檯中看到文字輸出

這是個很好的例子, 展示了怎麼使用Box2D。

2.1 創建世界(Creating a World)

每個Box2D程序開始時都會創建一個b2World對象。b2World是物理樞紐(physics hub), 用於管理內存、對象和模擬。根據自己的實際情況, 你可以在棧, 堆或數據區中創建出world。

創建Box2D的world很簡單。首先, 我們要定義重力矢量,另外還要告訴world是否允許body在靜止時休眠。休眠中的body不需要任何模擬。

b2Vec2 gravity(0.0f, -10.0f);

bool doSleep = true;

現在可以創建world對象了。注意,在這裏我們是在棧中創建world, 所以world不能離開它的作用域。

b2World world(gravity, doSleep);

我們已經有了自己的物理世界, 開始向裏面加東西了。

2.2 創建地面盒(Creating a Ground Box)

body用以下步驟來創建:

1.                   用位置(position), 阻尼(damping)等來定義body

2.                   通過world對象來創建body

3.                   用形狀(shape), 摩擦(friction), 密度(density)等來定義fixture

4.                   在body上來創建fixture

第一步,創建ground body。我們需要一個body定義。在定義中,我們指定ground body的初始位置。

b2BodyDef groundBodyDef;

groundBodyDef.position.Set(0.0f,-10.0f);

第二步, 將body定義傳給world對象, 創建ground body。world對象並不保留body定義的引用。ground body是作爲靜態物體(static body)創建的。靜態物體和其它靜態物體之間並沒有碰撞, 它們是固定的。當body的質量爲零時, Box2D就認爲它是靜態的。物體質量的默認值就爲零, 所以它們默認就是靜態的。

b2Body* groundBody =world.CreateBody(&groundBodyDef);

第三步, 創建地面多邊形。我們用簡便函數SetAsBox使得地面多邊形構成一個盒子形狀,盒子的中心點就是父body的原點。

b2PolygonShape groundBox;

groundBox.SetAsBox(50.0f, 10.0f);

SetAsBox函數接收半個寬度和半個高度作爲參數。因此在這種情況下,地面盒就是100個單位寬(x軸),20個單位高(y軸)。Box2D已被調諧到使用米,千克和秒做單位。你可以認爲長度單位就是米。當物體的大小跟真實世界一樣時,Box2D通常工作良好。例如,一個桶約1米高。由於浮點算法的侷限性,使用Box2D模擬冰川或沙塵的運動並不是一個好主意。

第四步, 我們創建shape fixture, 以完成ground body。這步中,我們有個簡便方法。我們並不需要修改fixture默認的材質屬性, 可以直接將形狀傳給body而不需要創建fixture的定義。隨後的教程中, 我們將會看到如何使用fixture定義來定製材質屬性。

groundBody->CreateFixture(&groundBox);

Box2D並不保存shape的引用。它把數據複製到一個新的b2Shape對象中。

注意,每個fixture都必須有一個父body,即使fixture是靜態的。然而,你可以把所有靜態fixture都依附於單個靜態body之上。之所以需要這個靜態body, 是爲了保證Box2D內部的代碼更具一致性,以減少潛在的bug數量。

可能你已經注意到, 多數Box2D類型都有b2前綴。這是爲了降低它和你的代碼之間名字衝突的機會。

2.3 創建動態物體(Creating a Dynamic Body)

現在我們已經有了一個地面body,我們可以使用同樣的方法來創建一個動態body。除尺寸之外的主要區別是, 我們必須爲動態body設置質量屬性。

首先我們用CreateBody創建body。默認情況下,body是靜態的, 所以在構造時候應該設置b2BodyType使得body成爲動態

b2BodyDef bodyDef;

bodyDef.type = b2_dynamicBody;

bodyDef.position.Set(0.0f, 4.0f);

b2Body* body =world.CreateBody(&bodyDef);

注意

如果你想body受力的影響而運動, 你必須將body的類型設爲b2_dynamicBody。

然後,我們創建一個多邊形shapde, 並將它附加到fixture定義上。我們先創建一個box shape:

b2PolygonShape dynamicBox;

dynamicBox.SetAsBox(1.0f, 1.0f);

接下來我們使用box創建一個fixture定義。注意, 我們把密度值設置爲1,而密度值默認是0。並且,fixture的摩擦係數設置爲0.3。

b2FixtureDef fixtureDef;

fixtureDef.shape = &dynamicBox;

fixtureDef.density = 1.0f;

fixtureDef.friction = 0.3f;

使用fixture定義, 我們現在就可以創建fixture。這會自動更新body的質量。要是你喜歡, 你可以爲body添加許多不同的fixture。每個fixture都會增加物體的總質量。

body->CreateFixture(&fixtureDef);

這就是初始化過程。現在我們已經做好準備,可以開始模擬了。

2.4 模擬(Box2D的)世界

我們已經初始化好了地面box和一個動態box。該讓牛頓來接手了。我們只有少數幾個問題需要考慮。

Box2D使用了一個叫積分器(integrator)的數值算法。 積分器在離散的時間點上模擬連續的物理方程。 它與傳統的遊戲動畫循環一同運行。我們需要爲Box2D選取一個時間步。通常來說用於遊戲的物理引擎需要至少 60Hz 的速度,也就是 1/60 的時間步。你可以使用更大的時間步,但是你必須更加小心地爲你的世界調整定義。我們也不喜歡時間步變化得太大,所以不要把時間步關聯到幀頻(除非你真的必須這樣做)。直截了當地,這個就是時間步:

float32 timeStep = 1.0f / 60.0f;

除積分器外,Box2D代碼還使用了約束求解器(constraint solver)。約束求解器用於解決模擬中的所有約束,一次一個。單個的約束會被完美的求解,然而當我們求解一個約束的時候,我們就會稍微耽誤另一個。要得到良好的解,我們需要多次迭代所有約束。

約束求解有兩個階段:速度、位置。在速度階段,求解器會計算必要的衝量,使得物體正確運動。而在位置階段,求解器會調整物體的位置,減少物體之間的重疊。每個階段都有自己的迭代計數。此外,如果誤差已足夠小的話,位置階段的迭代可能會提前退出。

對於速度和位置,建議的Box2D迭代次數都是10次。你可以按自己的喜好去調整這個數字,但要記得它是性能與精度之間的折中。更少的迭代會增加性能但降低精度,同樣地,更多的迭代會降低性能但能提高模擬質量。對於這個簡單示例,我們不需要多次迭代。這是我們選擇的迭代次數。

int32 velocityIterations = 6;

int32 positionIterations = 2;

時間步和迭代數是完全無關的。一個迭代並不是一個子步。一次迭代就是在時間步之中的單次遍歷所有約束,你可以在單個時間步內多次遍歷約束。

現在我們可以開始模擬循環了, 在你的遊戲中, 模擬循環和遊戲循環可以合併起來。每次遊戲循環你都應該調用b2World::Step, 通常調用一次就夠了, 這取決於幀頻以及物理時間步。步進後,你應當調用b2World::ClearForces清除你施加到body上的任何力。

這個Hello World程序設計得非常簡單, 它沒有圖形輸出。代碼會打印出動態body的位置以及旋轉角, 有文字輸出總比完全沒有輸出好。這就是模擬 1 秒鐘內 60 個時間步的循環:

for (int32 i = 0; i < 60; ++i)

{

    world.Step(timeStep,velocityIterations, positionIterations);

    world.ClearForces();

    b2Vec2 position =body->GetPosition();

    float32 angle =body->GetAngle();

    printf("%4.2f %4.2f%4.2f\n", position.x, position.y, angle);

}

輸出展示了動態box降落到地面的情況。你的輸出看起來應當是這樣:

0.00 4.00 0.00

0.00 3.99 0.00

0.00 3.98 0.00

...

0.00 1.25 0.00

0.00 1.13 0.00

0.00 1.01 0.00

2.5 清理

當world對象超出它的作用域,或通過指針將其 delete 時, 分配給body, fixture, joint使用的內存都會被釋放。這能使你的生活變得更簡單。然而,你應該將body, fixture或joint的指針都清零,因爲它們已經無效了。

2.6 Testbed例子

一旦你征服了 HelloWorld 例子,你應該開始看 Box2D 的 testbed 了。testbed 是個單元測試框架,也是個演示環境, 這是它的一些特點:

•                    可移動和縮放的攝像機

•                    可用鼠標選中依附在動態物體上的形狀

•                    可擴展的測試集

•                    通過圖形界面選擇測試,調整參數,以及設置調試繪圖

•                    暫停和單步模擬

•                    文字渲染


在 testbed 中有許多 Box2D 的測試用例,以及框架本身的實例。我鼓勵你通過研究和修改它來學習 Box2D。

注意:testbed 是使用 freeglut 和 GLUI 寫成的,testbed 本身並不是 Box2D 庫的一部分。Box2D本身不知道如何渲染,就像 HelloWorld 例子一樣,使用 Box2D 並不一定需要渲染。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章