Box2DJS入門教程

本文的主要目的是給大家介紹如何使用Box2DJS庫在瀏覽器中創建一個物理世界,本文爲基本入門教程,如果你想更深入的瞭解Box2D的功能,請看本文結尾“相關資源”中的“Box2D v2.0.1 用戶手冊

一、主函數:

  • 主函數包括了Box2D的基本流程
  • 簡單來說,一個Box2D程序的基本流程是由以下三個基本步驟構成的:

1 function box2dMain() {
2  
3     setupWorld();               //1. 創建一個世界
4     addBodys();                 //2. 爲世界創建物體
5     setInterval(step, 1000/60); //3. 讓世界動起來,反覆計算和繪製世界
6  
7 }

二、創建世界:

  • 設定世界有效區域的大小:超過有效區域的物體將不參與計算。
  • 定義重力:重力是一個二維矢量,矢量在Box2D中用b2Vec2來定義。
  • 設定是否允許物體休眠:當物體靜止下來,它就會被判定爲休眠,如果打開這個開關,對於休眠的物體將停止模擬。直到它被其它物體解除,它纔會醒來。
01 function setupWorld(){
02  
03     //1. 設置有效區域大小 - b2AABB 類 (左上角向量,右下角向量)
04     worldAABB = new b2AABB();
05     worldAABB.minVertex.Set(-1000, -1000);  //左上角
06     worldAABB.maxVertex.Set(1000, 1000);    //右下角
07  
08     //2. 定義重力 - 2D向量 - b2Vec2 類 (x,y)
09     gravity = new b2Vec2(0, 300);
10  
11     //3. 忽略休眠的物體
12     var doSleep = true;
13  
14     //4. 創建世界 - b2World
15     var world = new b2World(worldAABB, gravity, doSleep);
16 }

三、創建物體:

  • 形狀定義:Box2D中有三種基本形狀,圓形(Circle)、矩形(Box)、多邊形(Poly)。每個形狀可以單獨定義摩擦力、彈性、密度、相對位置等參數。形狀是組成物體的基本材料。(當物體的密度設定爲0時,物體變爲牆類物體,不可移動)
  • 物體定義:物體可由多個形狀組成。形狀由其定義相對位置(localPosition)決定其在物體中的位置,形狀添加到物體後,其相對位置始終保持不變。
  • 物體:物體只有使用世界的CreateBody()來生成,物體是物體定義的實例。只有使用這個函數生成的物體,纔會在世界中被模擬。

在下面這個例子中,我們創建了一個上圖的物體和一個地面。

01 function addBodys(){
02  
03     //1. 定義形狀   b2CircleDef,b2BoxDef,b2PolyDef 類
04     var Shape1 = new b2CircleDef(); //Shape1:圓形
05     Shape1.radius = 20;                 //半徑
06     Shape1.localPosition.Set(0, 0);     //偏移量
07     Shape1.density = 1.0;               //密度
08     Shape1.restitution = .3;            //彈性
09     Shape1.friction = 1;                //摩擦力
10  
11     var Shape2 = new b2PolyDef();   //Shape2:多邊形
12     Shape2.vertexCount = 3;                     //頂點數爲5
13     Shape2.vertices[0] = new b2Vec2(0,-20);     //頂點1
14     Shape2.vertices[1] = new b2Vec2(23.10,20);  //頂點2
15     Shape2.vertices[2] = new b2Vec2(-23.10,20); //頂點3
16     Shape2.localPosition.Set(0, 30);    //偏移量
17     Shape2.density = 1.0;               //密度
18     Shape2.restitution = .3;            //彈性
19     Shape2.friction = 1;                //摩擦力
20  
21     //2. 定義物體   b2BodyDef 類
22     var BodyDef1 = new b2BodyDef();
23     BodyDef1.position.Set(100, 100);    //設置物體的初始位置
24     BodyDef1.AddShape(Shape1);          //物體中加入Shape1
25     BodyDef1.AddShape(Shape2);          //物體中加入Shape2
26  
27     //3. 將物體添加至world
28     Body = World.CreateBody(BodyDef1);  //在世界中創建物體
29  
30     //...可用同樣流程繼續添加物體,再定義一塊地板
31     var Shape3 = new b2BoxDef();    //Shape3:矩形
32  
33     Shape3.extents.Set(200, 5);         //定義矩形高、寬
34     Shape2.density = 0;                 //牆體密度爲0
35     Shape2.restitution = .3;            //彈性
36     Shape2.friction = 1;                //摩擦力
37     var BodyDef2 = new b2BodyDef();
38     BodyDef2.position.Set(220, 500);    //設置物體的初始位置
39     BodyDef2.AddShape(Shape3);          //物體中加入Shape3
40     Body2 = World.CreateBody(BodyDef2); //在世界中創建物體
41  
42 }

四、讓世界運動起來:

  • 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()函數會將物體繪製在瀏覽器中。

01 function step(){
02  
03     //計算多少秒之後的世界
04     var dt = 1/60;
05  
06     //迭代次數,影響物體碰撞的計算精度,太高會導致速度過慢
07     var iterations = 10;
08  
09     //計算dt秒之後世界中物體的位置
10     World.step(dt,iterations);
11  
12     //繪製世界
13     drawWorld();
14  
15 }

五、繪製世界:

  • 當前的demo的drawWorld()函數是通過HTML5的canvas標籤來繪製物體的。當然,你也可以使用圖片和CSS3來進行模擬。
  • 繪製最新的一幀前,需要清除上一幀繪製的內容。
01 //繪製世界
02 function drawWorld(){
03  
04     //繪製之前將上一幀的內容清除
05     context.clearRect(0, 0, canvasWidth, canvasHeight);
06     //遍歷世界中的物體
07     for (var b = World.m_bodyList; b; b = b.m_next) {
08         //遍歷物體中的形狀
09         for (var s = b.GetShapeList(); s != null; s = s.GetNext())
10         {
11             this.drawShape(s);  //繪製一個形狀
12         }
13     }
14  
15 }
16  
17 //繪製一個形狀
18 function drawShape(shape){
19  
20     context.strokeStyle = '#000';       //線形
21     context.beginPath();
22     switch (shape.m_type) {
23         case b2Shape.e_circleShape:{    //如果是圓形,畫圓
24             var circle = shape;
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);
31             break;
32         }
33         case b2Shape.e_polyShape:{      //如果是多邊形,畫多邊形
34             var poly = shape;
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);
42             }
43             context.lineTo(tV.x, tV.y);
44             break;
45         }
46     }
47     context.stroke();   //繪製
48  
49 }

六、結語:

  • 本文只是介紹瞭如何使用基於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。
發佈了6 篇原創文章 · 獲贊 2 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章