本文的主要目的是給大家介紹如何使用Box2DJS庫在瀏覽器中創建一個物理世界,本文爲基本入門教程,如果你想更深入的瞭解Box2D的功能,請看本文結尾“相關資源”中的“Box2D v2.0.1 用戶手冊”
一、主函數:
- 主函數包括了Box2D的基本流程
- 簡單來說,一個Box2D程序的基本流程是由以下三個基本步驟構成的:
5 |
setInterval(step, 1000/60); |
二、創建世界:
- 設定世界有效區域的大小:超過有效區域的物體將不參與計算。
- 定義重力:重力是一個二維矢量,矢量在Box2D中用b2Vec2來定義。
- 設定是否允許物體休眠:當物體靜止下來,它就會被判定爲休眠,如果打開這個開關,對於休眠的物體將停止模擬。直到它被其它物體解除,它纔會醒來。
01 |
function setupWorld(){ |
04 |
worldAABB = new b2AABB(); |
05 |
worldAABB.minVertex.Set(-1000, -1000); |
06 |
worldAABB.maxVertex.Set(1000, 1000); |
09 |
gravity = new b2Vec2(0, 300); |
15 |
var world = new b2World(worldAABB, gravity, doSleep); |
三、創建物體:
- 形狀定義:Box2D中有三種基本形狀,圓形(Circle)、矩形(Box)、多邊形(Poly)。每個形狀可以單獨定義摩擦力、彈性、密度、相對位置等參數。形狀是組成物體的基本材料。(當物體的密度設定爲0時,物體變爲牆類物體,不可移動)
- 物體定義:物體可由多個形狀組成。形狀由其定義相對位置(localPosition)決定其在物體中的位置,形狀添加到物體後,其相對位置始終保持不變。
- 物體:物體只有使用世界的CreateBody()來生成,物體是物體定義的實例。只有使用這個函數生成的物體,纔會在世界中被模擬。
在下面這個例子中,我們創建了一個上圖的物體和一個地面。
04 |
var Shape1 = new b2CircleDef(); |
06 |
Shape1.localPosition.Set(0, 0); |
08 |
Shape1.restitution = .3; |
11 |
var Shape2 = new b2PolyDef(); |
12 |
Shape2.vertexCount = 3; |
13 |
Shape2.vertices[0] = new b2Vec2(0,-20); |
14 |
Shape2.vertices[1] = new b2Vec2(23.10,20); |
15 |
Shape2.vertices[2] = new b2Vec2(-23.10,20); |
16 |
Shape2.localPosition.Set(0, 30); |
18 |
Shape2.restitution = .3; |
22 |
var BodyDef1 = new b2BodyDef(); |
23 |
BodyDef1.position.Set(100, 100); |
24 |
BodyDef1.AddShape(Shape1); |
25 |
BodyDef1.AddShape(Shape2); |
28 |
Body = World.CreateBody(BodyDef1); |
31 |
var Shape3 = new b2BoxDef(); |
33 |
Shape3.extents.Set(200, 5); |
35 |
Shape2.restitution = .3; |
37 |
var BodyDef2 = new b2BodyDef(); |
38 |
BodyDef2.position.Set(220, 500); |
39 |
BodyDef2.AddShape(Shape3); |
40 |
Body2 = World.CreateBody(BodyDef2); |
四、讓世界運動起來:
- step()函數的作用是計算某段時間後,世界中物體的位置和角度,並將其繪製到瀏覽器中。
- 計算機中的動畫,是一幀一幀構成的,每一幀表現了動畫中某一時刻的一個場景。所以我們使用定時器函數setInterval(step, 1000/60),來每1/60秒執行一次計算和重繪工作,也就是上述的step()函數。
- step()中的dt參數,告訴了計算機要計算當前時間多少秒以後的世界,Box2D官方推薦爲1/60秒,當然,如果你的計算機足夠快,縮小這個時間間隔。另外dt應該與setInterval()函數中的第二個參數對應起來,這樣纔不會導致物體看起來運動的比你想像的要快或者慢。還有一點,dt不宜過大,否則模擬會不太精確,可能出現物體穿過另一個物體之類的bug。
- step()中的iterations參數,是多個物體同時發生碰撞時的模擬精度,越高的值會使模擬越精確,但同時也會讓運算速度大幅下降,推薦值爲10。
- step()中的World.step()函數是用來計算世界中物體的位置,執行後,物體的位置、角度、速度等信息更新。;
- step()中的drawWorld()函數會將物體繪製在瀏覽器中。
10 |
World.step(dt,iterations); |
五、繪製世界:
- 當前的demo的drawWorld()函數是通過HTML5的canvas標籤來繪製物體的。當然,你也可以使用圖片和CSS3來進行模擬。
- 繪製最新的一幀前,需要清除上一幀繪製的內容。
05 |
context.clearRect(0, 0, canvasWidth, canvasHeight); |
07 |
for ( var b = World.m_bodyList; b; b = b.m_next) { |
09 |
for ( var s = b.GetShapeList(); s != null ; s = s.GetNext()) |
18 |
function drawShape(shape){ |
20 |
context.strokeStyle = '#000' ; |
22 |
switch (shape.m_type) { |
23 |
case b2Shape.e_circleShape:{ |
25 |
var r = circle.m_radius; |
26 |
var pos = circle.m_position; |
27 |
var pos2 = circle.m_R.col1.clone().scale(r).add(pos); |
28 |
context.arc(pos.x, pos.y, r, 0, Math.PI * 2, false ); |
29 |
context.moveTo(pos.x, pos.y); |
30 |
context.lineTo(pos2.x, pos2.y); |
33 |
case b2Shape.e_polyShape:{ |
35 |
var tV = b2Math.AddVV(poly.m_position, |
36 |
b2Math.b2MulMV(poly.m_R, poly.m_vertices[0])); |
37 |
context.moveTo(tV.x, tV.y); |
38 |
for ( var i = 0; i < poly.m_vertexCount; i++) { |
39 |
var v = b2Math.AddVV(poly.m_position, |
40 |
b2Math.b2MulMV(poly.m_R, poly.m_vertices[i])); |
41 |
context.lineTo(v.x, v.y); |
43 |
context.lineTo(tV.x, tV.y); |
六、結語:
- 本文只是介紹瞭如何使用基於JS的Box2D物理引擎創建最簡單的程序。對於更復雜的應用,Box2D還有衆多的如關節(Joint)、接觸(contact)等本文沒有提及內容和雖然本文提及了,但並沒有詳細闡述的內容。這裏有一份“Aman JIANG(江超宇)”翻譯的“Box2D v2.0.1 用戶手冊”,雖然其內容是基於c++的Box2D引擎,但其基本的API與JS版無太大差異,可能對你會有很大的幫助。
- 另外,目前無問題的JS版是由Box2DFlashAS3_1.4.3.1轉換而來,而網上Box2D教程的主流版本爲2.x系列,部分api與1.4不太一樣,功能也有增強,所以請大家研究的時候注意兩者差別。在HTML5被衆人關注的今天,希望作者能儘快發佈2.0版的Box2DJS。