HTML5之2D物理引擎 Box2D for javascript Games 系列 第二部分

 

這是系列第二部分,以前部分在本博客中找html

源碼demo存放在https://github.com/willian12345/Box2D-for-Javascript-Gamesgit

 

向世界添加剛體


 

剛體(Bodies)是咱們用Box2D建立物理遊戲的重要對象。任何你能夠移動的或交互 的對象都是剛體(Bodies)。github

憤怒的小鳥(Angry Birds)中建立的小鳥和小豬是剛 體,一樣在圖騰破壞者(Totem Destroyer)中的黃金神像和圖騰磚塊也是剛體。canvas

本章將帶你學習建立各類類型的Box2D剛體,此外還有一些其它重要的特性,以下表所列函數

• 建立圓形剛體
學習

• 建立矩形剛體
測試

• 建立任意多邊形剛體spa

• 使用DebugDraw()方法測試模擬debug

• 定義剛體的類型:static,dynamic或kinimatic
調試

• 設置材質屬性:密度(density),摩擦係數(friction)和恢復係數(resitution)

• 度量單位


• 建立合成對象

經過本章的學習,你將會建立一個你的第一個圖騰破壞者類型的遊戲。本章有較

多的知識點,那麼咱們廢話少說,直接開始本章的學習吧!

 

你的第一個模擬—一個球落地

 


咱們先從簡單的任務開始,最簡單的物理模擬:一個球落到地面。總之,雖然

這是一個簡單小球落地的模擬,可是它將是你的第一個模擬,而且易於很快實

現它。

讓咱們看看在此次模擬中咱們要作些什麼:

• 世界的重力(gravity)


• 一個受到做用力(例如:重力(gravity))的球

• 一個不受任何做用力的地面


• 某種材質,正如咱們但願小球在地面彈起的材質

在以前的學習中,你已經可以配置世界的重力了,因此咱們從建立小球開始本章的代

碼編寫。

1. 不管咱們是建立球形仍是多邊形,第一步都是建立一個剛體: 

  var bodyDef =new b2BodyDef();

  b2BodyDef類是一個剛體的定義類,它將持有建立咱們剛體所須要的全部數 據。

2. 如今能夠將剛體添加到世界中。由於咱們採用的舞臺尺寸是640X480,咱們將 把球放置在舞臺的頂部的中心位置,該位置爲(320,30),以下所示: 

  bodyDef.position.Set(10.66,1); 

  經過position屬性顯示的設置了剛體在世界中的位置,可是我確信你會對我以前 所說的位置爲(320,30)的設置而變成(10.66,1)而感到困惑。

  這緣由要關係 到度量單位上。雖然Flash是以像素(pixels)爲度量單位,可是在Box2D中嘗試 模擬真實的世界並採用米(meters)做爲度量單位。

  對於米(meters)和像素 (pixels)之間的轉換沒有通用的標準,可是咱們採用下面的轉換標準能夠有很 好的運行效果: 

  1米 = 30像素

  因此,若是咱們定義一個變量來幫助咱們將米(meters)轉換成像素 (pixels),咱們即可以在Box2D世界(world)中進行操做時使用像素 (pixels)而不用使用米(meters)來做爲度量單位。

  這樣將使咱們在製做 Flash遊戲時,使用像素來思考,從而變得更加直觀。

3. 打開你在第一章中建立的demo1-1.html,並像下面那樣修改它: 

  

<script>
         function init(){
            var b2Vec2 = Box2D.Common.Math.b2Vec2
            ,b2AABB = Box2D.Collision.b2AABB
            ,b2BodyDef = Box2D.Dynamics.b2BodyDef
            ,b2Body = Box2D.Dynamics.b2Body
            ,b2FixtureDef = Box2D.Dynamics.b2FixtureDef
            ,b2Fixture = Box2D.Dynamics.b2Fixture
            ,b2World = Box2D.Dynamics.b2World
            ,b2MassData = Box2D.Collision.Shapes.b2MassData
            ,b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape
            ,b2CircleShape = Box2D.Collision.Shapes.b2CircleShape
            ,b2DebugDraw = Box2D.Dynamics.b2DebugDraw
            ,b2MouseJointDef =  Box2D.Dynamics.Joints.b2MouseJointDef
            ;

            
            var world;
            var worldScale = 30;
            function main(){
               world = new b2World(new b2Vec2(0, 9.81), true);
               var bodyDef = new b2BodyDef(); 
               bodyDef.position.Set(320/worldScale,30/worldScale);

               setInterval(updateWorld, 1000 / 60);
            }
            function updateWorld() {
               world.Step(1/30,10,10);
               world.ClearForces(); // 清除做用力
            }

            main();
         }
         init();
</script>

而且,注意我是怎樣建立世界和調用step方法的。這比以前少用了幾行代碼。

一旦你建立了剛體定義,那麼是時候給它一個形狀了。

 

建立一個圓形形狀


 

形狀(shape)是一個2D幾何對象,例如一個圓形或者多邊形,在這裏必須是凸多邊

形(每個內角小於180度)。記住,Box2D只能處理凸多邊形

如今,咱們從小球開始,因此咱們建立一個圓形:

var circleShape =new b2CircleShape(25/worldScale);

 

b2CircleShape是用來建立圓形形狀,而且它的構造函數須要一個半徑(radius)做爲 參數。

在以前的代碼中,咱們建立了一個圓形,它的半徑爲25像素(pixels),因爲設 置了worldScale變量。

從如今起,每次你想要使用像素進行操做時,你只要將它們除以 worldScale便可。你也能夠定義一個方法名爲pixelsToMeters的方法,在每次你須要將像 素(pixels)轉換成米(meters)時調用。

當咱們有了剛體定義和形狀時,咱們將使用夾具(fixture)來將它們粘合起來。

建立夾具


夾具(fixture)用於將形狀綁定到剛體上,而後定義它的材質,設置密度 (density),摩擦係數(friction)以及恢復係數(restitution)。

此刻咱們無需去 擔憂材質,讓咱們把注意力集中到夾具(fixture)上: 

1.首先,咱們建立夾具(fixture):


  

var fixtureDef = new b2FixtureDef();
fixtureDef.shape=circleShape;

 

  一旦咱們經過構造函數建立了夾具(fixture),咱們將分配以前建立 的形狀給它的shape屬性。

2.最後,咱們準備將球添加到世界中:


  

var theBall =world.CreateBody(bodyDef);
theBall.CreateFixture(fixtureDef);

b2Body是剛體的實體:是物質,是經過使用bodyDef屬性建立的具 體剛體。

3.再次說明一下,使用如下步驟將剛體添加到世界中:

  I 建立一個剛體定義,它將持有剛體信息,例如剛體的位置信息。

  II 建立一個形狀,它將決定剛體的顯示形狀

  III. 建立一個夾具,將形狀附加到剛體定義上。

  IV. 建立剛體在世界中的實體,使用夾具。

一旦你知道了每一步的重要性,添加剛體到你的Box2D世界中將會 很容易和有趣

回到咱們的項目。如今的main函數內應該看起來和下面同樣: 

function main(){
               world = new b2World(new b2Vec2(0, 9.81), true);
               var bodyDef = new b2BodyDef(); 
               bodyDef.position.Set(320/worldScale,30/worldScale);
               var circleShape = new b2CircleShape(25/worldScale);
               var fixtureDef = new b2FixtureDef();
               fixtureDef.shape = circleShape;
               fixtureDef.density = 1;
               fixtureDef.restitution = .6;
               fixtureDef.friction = .1;
               var theBall = world.CreateBody(bodyDef);
               theBall.CreateFixture(fixtureDef);

               setInterval(updateWorld, 1000 / 60);
}

定時保存項目並測試它。準備好看看你的第一個Box2D剛體的活動?運行影片!

額…,然而你如今運行時仍是看不到任何東西。。讓我告訴你緣由,Box2D只負責模擬物理世界,而不負責顯示任何東西。

 

這意味着,你的剛體正活躍在你的Box2D世界中,只是你看不到而已。

 

使用調試繪圖測試你的模擬


幸運的是,Box2D有一個特性,調試繪圖(debug draw),它將幫助你顯示出模擬的狀況:

在網頁中首先要添加一個canvas如

<canvas id="canvas" width="640" height="480" style="" ></canvas>

 

1.調試繪圖(debug draw)將Box2D世界中發生的事情顯示出來,在

updateWorld方法中,咱們能夠在Step()方法以後調用世界(world)的 DrawDebugData()方法:

       world.DrawDebugData();

2. 一旦咱們告知世界在每次遍歷以後顯示調試繪圖(debug draw),咱們須要通 過調試繪圖(debug draw)定義視覺設置。以下添加代碼到你的main函數內: 

var debugDraw = new b2DebugDraw();
debugDraw.SetSprite(document.getElementById("canvas").getContext("2d"));
debugDraw.SetDrawScale(worldScale);
debugDraw.SetFillAlpha(0.5);
debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);
world.SetDebugDraw(debugDraw);

3.這裏有不少代碼,因此讓咱們來解釋一下發生了什麼。你已經知道 DrawDebugData()方法表明什麼,因此咱們將解釋其它行代碼表明的意思:

  

var debugDraw = new b2DebugDraw();

 

b2DebugDraw是一個類,它支持調試繪圖(debug draw)出你的遊戲中的物理 實體。

  

 var debugSprite:Sprite = new Sprite();

 

debugSprite被添加到顯示列表(Display List),準備顯示在canvas上。

debugDraw.SetSprite(debugSprite);

 

SetSprite()方法告知debugSprite將要被用來顯示調試繪圖 (debug draw)。

debugDraw.SetDrawScale(worldScale);

 

由於咱們要將米(meters)轉變爲像素(pixels),咱們須要通知調試繪 圖(debug draw)咱們使用的換算比例。 debugDraw.SetFlags(b2DebugDraw.e_shapeBit);

SetFlag()方法容許咱們決定咱們將在調試繪圖(debug draw)中描繪的物 理實體的類型。此刻,咱們只須要繪製形狀。

補充說明:

setFlag()方法選擇性的繪製Box2D對象的內容。這樣能夠節省CPU開支。setFlag()方法有一個16進制的參數,這參數的取值只能是b2DebugDraw中定義的下面幾個常量

另外,咱們還能夠用」或」運算符,同時使用多個Flag

debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);

debugDraw.SetFillAlpha(0.5);

SetFillAlpha()方法是爲了便於觀看而設置的。形狀的輪廓是不透明的,填 充的顏色是半透明的。這將使得調試繪圖輸出更加易於理解。

world.SetDebugDraw(debugDraw);

最後,咱們將指派調試繪圖(Debug draw)到咱們剛剛建立的世界(world)

4.如今是時候來測試一下你影片了,而後你應該會看到下圖所示的樣子: 

 

就這樣!你設法看到了你放置在Box2D世界中的剛體。

 

目前,球體還沒法在重力的做用下下落,可是不要擔憂,咱們將在稍後修改它。

 

如今,讓咱們來建立一些能夠做爲地面的東西,例如一個放置在舞臺底部邊緣的大矩

 

形。從如今開始一切將更加簡單,做爲新的剛體將會很快的自動顯示在它所添加的世界中。

 

完整源碼在demo2-1.html中查看

建立矩形形狀


 

 

讓我執行下面的步驟:

  1.首先,剛體和夾具的定義能夠重指定到咱們定義的新的剛體上。這樣,我 們無需再去定義bodyDef變量,可是咱們要改變原先在建立球時使用的坐 標:


bodyDef.position.Set(320/worldScale,470/worldScale); 

  2.咱們將用b2PolygonShape類建立一個多邊形:  

var polygonShape = new b2PolygonShape(); 

  這樣,咱們以以前建立圓形形狀時,相同的方法建立了一個多邊形形狀。

  3.多邊形形狀必須遵照一些限制,可是目前,由於咱們只須要一個軸對稱的矩 形,SetAsBox()方法便能知足咱們的須要: 
 

polygonShape.SetAsBox(320/worldScale,10/worldScale);

  這個方法須要兩個參數:矩形的半寬長和半高長。最後,咱們的新多邊形形狀 的中心在像素(320,470),它的寬度爲640像素和高度爲20像素——這是咱們 剛剛建立的地面的尺寸。 


  4.如今,咱們改變定義的夾具的shape屬性,附加新的多邊形形狀: 

fixtureDef.shape = polygonShape; 

  5.最後,咱們能夠建立剛體並將夾具附加上去,就像咱們在球形上作的那樣。

var theFloor = world.CreateBody(bodyDef); 
theFloor.CreateFixture(fixtureDef); 

  6.你的main方法應該向下面這樣:

function main(){
               world = new b2World(new b2Vec2(0, 9.81), true);
               var bodyDef = new b2BodyDef(); 
               bodyDef.position.Set(320/worldScale,30/worldScale);
               var circleShape = new b2CircleShape(25/worldScale);
               var fixtureDef = new b2FixtureDef();
               fixtureDef.shape = circleShape;
               fixtureDef.density = 1;
               fixtureDef.restitution = .6;
               fixtureDef.friction = .1;
               var theBall = world.CreateBody(bodyDef);
               theBall.CreateFixture(fixtureDef);

               // 定義矩形地面
               bodyDef.position.Set(320/worldScale, 470/worldScale); 
               bodyDef.type = b2Body.b2_staticBody;
               var polygonShape = new b2PolygonShape();
               polygonShape.SetAsBox(320/worldScale, 10/worldScale);
               fixtureDef.shape = polygonShape; // 複用夾具

               var theFloor = world.CreateBody(bodyDef);
               theFloor.CreateFixture(fixtureDef);

                //setup debug draw
               var debugDraw = new b2DebugDraw();
               debugDraw.SetSprite(document.getElementById("canvas").getContext("2d"));
               debugDraw.SetDrawScale(worldScale);
               debugDraw.SetFillAlpha(0.5);
               debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);
               world.SetDebugDraw(debugDraw);

               setInterval(updateWorld, 1000 / 60);
            }

  7.測試影片,你將會看到地面: 

  

完整源碼在demo2-2.html中查看

你看是否是很簡單?咱們花了將近一章半去防止咱們的第一個剛體,而後只花了很

少的幾行代碼添加另外一個剛體。

 

 

不一樣的剛體類型——static,dynamic和 kinematic 


 

有三種Box2D剛體的類型:staitc,dynamic和kinematic。

 

一個static類型的剛體不受任何力,衝量或撞擊的影響而且不會移動。它只能經過 用戶手動移動。默認狀況下,全部的Box2D剛體都是static類型的剛體,這就是爲什 麼球不移動的緣由。一個static類型的剛體不會和別的static或kinematic類型的剛體發 生碰撞。

一個dynamic類型的剛體受力,衝量,撞擊以及任何世界事件的影響。它能夠經過 手動移動,雖然我建議讓它們經過世界的重力,和任何類型剛體的碰撞來移動。

一個kinematic類型的剛體是一個介於static和dynamic剛體之間的混合剛體。它不 受理的影響,可是能夠經過手動和設置它們的速率來移動。它不能和static和 kinematic類型的剛體碰撞。

如今回到咱們的 模擬鍾來。那種類型是咱們要指派給球和地面的呢?

地面是static類型的剛體,它無需移動,然而經過世界重力球要移動,因此是 dynamic類型的剛體。

你只須要設置剛體定義的type屬性就能告知Box2D每個剛體的類型,屬性值能夠是

b2Body.b2_staticBody, b2Body.b2_dynamicBody或b2Body.b2_kinematicBody分別對應 static,dynamic或kinematic剛體。

爲球添加上bodyDef.type=b2Body.b2_dynamicBody;

地面添加上bodyDef.type=b2Body.b2_staticBody;

你的新main方法向下面這樣: 

function main(){
               world = new b2World(new b2Vec2(0, 9.81), true);
               var bodyDef = new b2BodyDef(); 
               bodyDef.position.Set(320/worldScale,30/worldScale);
               bodyDef.type = b2Body.b2_dynamicBody;
               var circleShape = new b2CircleShape(25/worldScale);
               var fixtureDef = new b2FixtureDef();
               fixtureDef.shape = circleShape;
               var theBall = world.CreateBody(bodyDef);
               theBall.CreateFixture(fixtureDef);

               // 定義矩形地面
               bodyDef.position.Set(320/worldScale, 470/worldScale); // 複用定義剛體
               bodyDef.type = b2Body.b2_staticBody;
               var polygonShape = new b2PolygonShape();
               polygonShape.SetAsBox(320/worldScale, 10/worldScale);
               fixtureDef.shape = polygonShape; // 複用夾具

               var theFloor = world.CreateBody(bodyDef);
               theFloor.CreateFixture(fixtureDef);

                //setup debug draw
               var debugDraw = new b2DebugDraw();
               debugDraw.SetSprite(document.getElementById("canvas").getContext("2d"));
               debugDraw.SetDrawScale(worldScale);
               debugDraw.SetFillAlpha(0.5);
               debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);
               world.SetDebugDraw(debugDraw);

               setInterval(updateWorld, 1000 / 60);
}

在恭喜你運行成功你的第一個模擬以前,讓咱們花點時間來講一下關於當使用調試 繪圖(debug draw)時的不一樣顏色。

static類型的剛體將會繪製成綠色。dynamic類型的剛體,當它們沒有在睡眠狀態 時將會繪製成紅色,在睡眠狀態時將會繪製成灰色。

kinematic類型的剛體,在之 前的屏幕截圖中沒有顯示,它將會被顯示爲藍色。

如今,咱們知道當剛體進入睡眠狀態並節約CPU資源這個概念。正如你所見, 當球撞擊地面,沒有別的裏影響它時,因此求能夠進入睡眠狀態,知道有什麼 發生爲止。

如今,有一個新的問題。球在落地後沒有彈起。若是咱們想要運行一個完美的模

擬,咱們須要給咱們的剛體一些更多的屬性。

密度,摩擦和恢復


正如你已經知道怎樣向世界添加剛體,那麼我想向你介紹三種將會改變剛體行爲

的屬性:密度,摩擦和恢復。

密度(density)用來設置剛體的質量,按照公斤沒平方米。越高的密度意味着越 重的剛體,而且該值不能爲負。

摩擦(friction)在兩個剛體在彼此的表面上移動時產生,它是經過一個係數來定 義的,一般它的範圍在0(沒有摩擦)-1(最大摩擦)之間。它不能爲負數。

恢復(restitution)決定剛體在發生碰撞時反彈的程度。與密度(density)和摩擦 (friction)同樣,它不能爲負數而且它是一個介於0-1的係數來定義的。

一個小球 在恢復爲0時落向地面,不發生反彈(無彈性碰撞),反之恢復爲1時小球將會以此刻撞擊時相同的速率彈起(徹底彈性碰撞)。

密度(density),摩擦(friction)和恢復(restitution)必須添加到夾具上,因此在main方法中添加如下幾行代碼:


fixtureDef.density=1;

fixtureDef.restitution=0.6;

fixtureDef.friction=0.1;

在你的main函數內看起來應該這樣

function main(){
               world = new b2World(new b2Vec2(0, 9.81), true);
               var bodyDef = new b2BodyDef(); 
               bodyDef.position.Set(320/worldScale,30/worldScale);
               bodyDef.type = b2Body.b2_dynamicBody;
               var circleShape = new b2CircleShape(25/worldScale);
               var fixtureDef = new b2FixtureDef();
               fixtureDef.shape = circleShape;
               fixtureDef.density = 1;
               fixtureDef.restitution = .6;
               fixtureDef.friction = .1;
               var theBall = world.CreateBody(bodyDef);
               theBall.CreateFixture(fixtureDef);

               // 定義矩形地面
               bodyDef.position.Set(320/worldScale, 470/worldScale); // 複用定義剛體
               bodyDef.type = b2Body.b2_staticBody;
               var polygonShape = new b2PolygonShape();
               polygonShape.SetAsBox(320/worldScale, 10/worldScale);
               fixtureDef.shape = polygonShape; // 複用夾具

               var theFloor = world.CreateBody(bodyDef);
               theFloor.CreateFixture(fixtureDef);

                //setup debug draw
               var debugDraw = new b2DebugDraw();
               debugDraw.SetSprite(document.getElementById("canvas").getContext("2d"));
               debugDraw.SetDrawScale(worldScale);
               debugDraw.SetFillAlpha(0.5);
               debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);
               world.SetDebugDraw(debugDraw);

               setInterval(updateWorld, 1000 / 60);
            }

我向夾具指派一次屬性,而全部的剛體都將使用這個相同的夾具。在本書的整個

講解過程當中,咱們將要處理不少夾具的屬性,可是目前讓咱們只須要設置小球彈跳便可。

測試demo2-3.html,你就會發現小球在彈跳

祝賀你!你剛剛完成了你的第一個真實的Box2D項目,那麼如今你有能力去建立 基礎的形狀和爲它們分配特性和屬性。

接下去讓我開始來建立一個準遊戲吧…

 

 


注:轉載請註明出處博客園:sheldon-二狗-偷飯貓(willian12345@126.com)

https://github.com/willian12345

相關文章
相關標籤/搜索