這是系列第二部分,以前部分在本博客中找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中查看
你看是否是很簡單?咱們花了將近一章半去防止咱們的第一個剛體,而後只花了很
少的幾行代碼添加另外一個剛體。
有三種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