- 精通Cocos2d-x游戲開發(進階卷)
- 王永寶
- 1813字
- 2020-11-28 22:37:07
9.4 Body和Shape
Body表示在物理世界中的一個對象,往往也對應游戲世界中的一個對象,物體具有位置、角度、質量、速度等屬性,每個剛體都包含一個形狀列表,這意味著一個剛體可以由很多個形狀組成,如由兩個圓形加一個長方形組成一個啞鈴,剛體總共可以分為靜態、動態以及動力學3種。
? 靜態剛體永遠不會移動,不管其他剛體以多大的力量推動它,它總是巋然不動,也不會與其他靜態剛體碰撞。
? 動態剛體表示場景中可以運動的對象,可以和任何剛體發生碰撞。撞到動力學剛體和撞到靜態剛體都會被彈開,撞到動態剛體,會互相作用,也就是可能互相彈開,或者其中一個撞飛另外一個。
? 動力學剛體可以移動,但不受任何力的影響,動態剛體對其施加的力,或者ApplyForce對其施加的力都無效,和靜態剛體不會發生碰撞,動力學剛體之間,也不會發生碰撞,但是動力學剛體的運動,可以對動態剛體產生力的作用。
9.4.1 剛體的碰撞
在筆者的測試代碼中,一個向上移動的動力學剛體,碰到動態剛體,會將動態剛體往上頂起,碰到其他的動力學剛體,會穿過,而碰到靜態剛體,也是直接穿過!動力學剛體并不會和靜態剛體發生碰撞!當地面上有個動態剛體時,這時候一個動力學剛體從正上方往下運動,會將動態剛體緩緩壓入地面。
由于動力學剛體不受力或沖量的作用,所以只能通過Body->SetLinearVelocity設置線性速度或者關節的馬達驅動,來使其移動。如圖9-2~圖9-4演示了動力學剛體與各種剛體碰撞時的表現。

圖9-2 動力學剛體和動力學剛體互相穿透

圖9-3 動力學剛體和動態剛體互相碰撞

圖9-4 動力學剛體和靜態剛體互相穿透
如表9-1總結了各種剛體的碰撞情況,如果讀者覺得動力學剛體難以理解,可以想一想在超級瑪麗等游戲中可以跳上去的那些來回移動的平臺,它可以托著游戲者移動,但卻可以不受游戲者的影響。現實生活中的電梯也勉強可以理解為動力學剛體。
表9-1 各種剛體的碰撞情況

在游戲中經常會碰到一些單面墻壁,例如,是男人就上100層這樣的游戲中,游戲者從下往上跳時可以穿過平臺,而從上往下掉落時卻會被平臺擋住,這就需要使用到Box2d的碰撞監聽了。當監聽到碰撞事件時,判斷角色運動的方向是向上還是向下,根據判斷的結果來決定是否禁用此次碰撞。
9.4.2 創建剛體
要創建一個Body需要傳入一個物體的定義,b2BodyDef是一個內容豐富的結構,其默認構造函數如下,一般在填充完剛體描述對象的type和position之后,就會調用World的CreateBody接口來創建一個新的Body。
b2BodyDef() { userData = NULL; //默認沒有額外數據 position.Set(0.0f, 0.0f); //默認的位置和旋轉角度 angle = 0.0f; linearVelocity.Set(0.0f, 0.0f); //線性加速度和角速度等 angularVelocity = 0.0f; linearDamping = 0.0f; angularDamping = 0.0f; allowSleep = true; awake = true; //一開始就是醒著的狀態 fixedRotation = false; bullet = false; //當物體需要在高速運動中進行碰撞,開啟它 type = b2_staticBody; //默認是一個靜態物體 active = true; gravityScale = 1.0f; }
剛體的大致結構如圖9-5所示,每個剛體都會有其對應的一些形狀,在Box2d中通過Fixture來描述這些形狀,以及它們的密度、摩擦、彈性等。

圖9-5 剛體的組成
使用Body的CreateFixture可以為剛體設置一個新的形狀,也可以為剛體設置很多形狀,通過Body的SetGravityScale還可以設置剛體的重力縮放,某些物體可以讓其不受重力的影響。
物體的摩擦系數是一個0~1之間的數,0表示非常光滑,1表示非常粗糙。物體的彈性系數(恢復系數)也是一個0~1之間的數,0表示沒有彈性,1表示沒有能量損失的反彈。密度也是一個必須要設置的數據,假設將密度設置為0,物體仍然會往下掉落,但是沒有任何重量的物體堆疊起來就會變成如圖9-6所示。

圖9-6 密度為0的堆箱子
在Box2d里可以創建的形狀包含Circle圓形、Edge邊界線、Polygon多邊形以及Chain鏈條。多邊形是一個閉合的多邊形,而Chain是一系列點,可以理解為一個不閉合的多邊形。形狀主要是用于物理模擬中的碰撞檢測,在創建完Body之后,對Body進行設置。下面看一下如何創建多邊形。
b2CircleShape circle; //圓形 circle.m_radius = 1.0f; //半徑為1 b2EdgeShape edge; edge.Set(b2Vec2(-100.0f, 0.0f), //創建一條平行于x軸的邊 b2Vec2(100.0f, 0.0f)) b2PolygonShape polygon; //創建一個封閉的多邊形 polygon.SetAsBox(5.0f, 5.0f); //創建一個長和寬都為10的正方形 b2ChainShape shape; //鏈形 b2Vec2* arr = new b2Vec2[5]; arr[0] = b2Vec2(0.0f, 0.0f); arr[1] = b2Vec2(10.0f, 10.0f); arr[2] = b2Vec2(20.0f, 0.0f); arr[3] = b2Vec2(30.0f, 10.0f); arr[4] = b2Vec2(40.0f, 0.0f); //數組描述了一個’W’形狀 shape.CreateChain(arr, 5); //傳入一系列點來確定形狀 delete[] arr;
在創建閉合多邊形的時候,也可以使用Set()函數,像Chain一樣,傳入一個頂點數組來創建形狀,在為Body創建Fixture時,將shape對象的指針賦值給b2FixtureDef對象的shape變量即可。
上面的Edge常用于創建世界的邊界,其確實很適合做這個工作,而且做得要比Polygon要好。至于Chain,感覺像是一系列連續的Edge。
SetAsBox是個很好用的接口,可以節省代碼,需要注意的一點是,傳入的參數是寬和高的一半,函數會以當前位置為中心,創建一個四邊形。
填充好Shape數據之后,需要把它賦給對應的Fixture,同時為Fixture填充彈性、摩擦力、質量之類的屬性。最后調用Body的CreateFixture,綁定到剛體之上。