(轉載)BOX2D V2.3.0 使用者手冊中文版(第11章)-雜項
Chapter 11 雜項
11.1 使用者資料
b2Fixture, b2Body 和 b2Joint 類都允許你通過一個 void 指標來附加使用者資料。當你測試Box2D資料結構,並使其跟自己遊戲引擎中的物件結合起來時,這樣做是比較方便的。
舉個典型的例子,角色上附有物體,並在物體中附加角色的指標,這就構成了一個迴圈引用。如果你有角色(actor),你就能得到物體。如果你有物體,你也能得到角色。
GameActor* actor = GameCreateActor();
b2BodyDef bodyDef;
bodyDef.userData = actor;
actor->body = box2Dworld->CreateBody (&bodyDef);
一些需要使用者資料的例子:
• 使用碰撞結果給角色施加傷害效果。
• 當玩家進入一個包圍盒(axis-aligned box)時,觸發指令碼事件。
• 當Box2D通知你關節將要被摧毀時,去訪問某個遊戲結構。
記住,使用者資料是可選的,並且能放入任何東西。然而,你需要確保一致性。例如,如果你想在body中儲存actor的指標,那你就應該在所有的 body中都儲存actor指標。不要在一個body中儲存actor指標,卻在另一個body中儲存foo指標。將一個actor指標強制轉成foo指 針,可能會導致程式崩潰。
使用者資料指標預設為NULL。
對於fixture來說,你可以定義一個使用者資料結構來儲存遊戲特定的資訊。例如材料型別、特效鉤子、音效鉤子,等等。
struct FixtureUserData
{
int materialIndex;
…
};
FixtureUserData myData = new FixtureUserData;
myData->materialIndex = 2;
b2FixtureDef fixtureDef;
fixtureDef.shape = &someShape;
fixtureDef.userData = myData;
b2Fixture* fixture = body->CreateFixture(&fixtureDef);
…
delete fixture-> GetUserData();
fixture->SetUserData(NULL);
body->DestroyFixture(fixture);
11.2 隱式摧毀
Box2D沒有使用引用計數。因此你摧毀了body後,它就確實不存在了。訪問指向已摧毀body的指標,會導致未定義的行為。 也就是說,你的程式可能會崩潰。以debug方式編譯出的程式,Box2D的記憶體管理器會將已被摧毀實體佔用的記憶體,都填上FDFDFDFD。在某些時候, 這樣做可以使你更容易的找到問題的所在,並進而修復問題。
如果你摧毀了Box2D實體,你要確保所有指向這實體的引用都被移除。如果只有實體的單個引用,處理起來就很簡單了。但如果有多個引用,你需要考慮是否去實現一個控制代碼類(handle class),將原始指標封裝起來。
當你使用Box2D時,會很頻繁的建立並摧毀很多的物體(bodies)、形狀(shapes)和關節(joints)。某種程度上,這些實體是由Box2D自動管理的。當你摧毀了一個body,所有跟它有關聯的形狀、關節和接觸都會自動被摧毀。這稱為隱式摧毀。
連線到這些關節或接觸上的物體會被喚醒。這個過程是很簡便的,但是你必須小心一個關鍵問題:
注意
當body被摧毀時,所有依附其上的形狀和關節也會被自動摧毀。你應該將任何指向這些形狀和關節的指標清零。否則如果你之後試圖訪問或者再次摧毀這些形狀或關節,你的程式會死得很慘。
為了幫助你清空關節指標,Box2D提供了一個名叫b2DestructionListener的監聽類。你可以實現這個類,並將world物件傳給它。這樣當關節被隱式摧毀時,world物件會通知你。
注意,關節或fixture被顯式摧毀時,並沒有通知。這種情況下,所有者是清楚的,你可以在合適的地方執行必要的清理工作。如果你喜歡,你也可以呼叫你自己的b2DestructionListener的實現來保持清理程式碼的集中。
大多數情況下,隱式摧毀還是很方便的,但也很容易使你的程式崩潰。你可能會將指向shape或關節的指標儲存起來,當有關聯的body摧毀時,這些指標就變得無效了。當關節是由那些與相關的物體的管理程式碼無關的程式碼片段建立時,事情會變得更糟糕。比如,testbed就建立了一個 b2MouseJoint用來互動式操作螢幕上的body。
Box2D提供了一種回撥機制,用於在隱式摧毀發生時,通知你的應用程式。這給了你的程式一個機會,將無效指標清零。這個回撥機制將在稍後詳述。
你可以實現一個b2DestructionListener,這樣當一個形狀或關節隱式摧毀時,b2World 就能通知你。這將防止你的程式碼訪問野指標。
class MyDestructionListener : public b2DestructionListener
{
void SayGoodbye(b2Joint* joint)
{
// remove all references to joint.
}
};
你可以將摧毀監聽器(destruction listener)註冊到world物件中。在world物件初始化時,你就應該這樣做了。
myWorld->SetListener(myDestructionListener);
11.3 畫素和座標系統
重申一下Box2D使用MKS(米、千克和秒)單位制,角度使用弧度為單位。你可能對使用米感到困惑,因為你的遊戲是以畫素的形式表示的。在testbed中為了解決這個問題,我將整個遊戲都使用米,並使用OpenGL的檢視轉換,將world調整到螢幕空間中。
float lowerX = -25.0f, upperX = 25.0f, lowerY = -5.0f, upperY = 25.0f;
gluOrtho2D(lowerX, upperX, lowerY, upperY);
如果你的遊戲必須使用畫素為單位,在向Box2D傳送值的時候,你應該將你的長度單位由畫素轉換成米。反之,當你接收Box2D傳來的值時,應該將之由米轉換成畫素。這會提升物理模擬的穩定性。
你必須設定一個合理的轉換因子。我建議可以根據你的角色的尺寸來做出選擇。假設你每米使用50個畫素(因為你的角色有75個畫素的高度),則你可以使用下面這些公式將畫素轉換成米:
xMeters = 0.02f * xPixels;
yMeters = 0.02f * yPixels;
相反的:
xPixels = 50.0f * xMeters;
yPixels = 50.0f * yMeters;
你應該在你的程式碼中使用MKS單位制,只在你渲染的時候,將之轉換成畫素。這會簡化你的遊戲邏輯,並減少錯誤的可能。因為渲染時的變換,能被限定在很小的程式碼中。
如果你使用轉換因子,你應該在全域性範圍內調整它,確保沒有錯誤發生。你也可以調整它來改善穩定度。