建立圖騰破壞者的關卡
現在你有能力建立你的第一個遊戲原型,我們將從建立圖騰破壞者的級別開始。
為了展示我們所做事情的真實性,我們將流行的Flash遊戲圖騰破壞者的一關作為 我們模仿的物件。請看下面的截圖:
這是圖騰破壞者遊戲原型的第一關,如果你仔細觀察你會發現磚塊的尺寸是30的倍 數。你知道這是什麼原因嗎?如果在你認真學習了前面的章節,你就會知道這是將米 和畫素的轉換所致。
作者建立遊戲可能是直接使用米作為度量單位的,但是我們將堅持自己的選擇使用畫素作為度量單位。
目前,我們無需去擔心怎樣設定褐色和黑色的圖騰顏色,我們只要想著再現圖騰即可。
在我們開始編碼之前,我將建議你建立幾個方法,這樣可以幫我們重用部分程式碼。別擔心,沒什麼新的東西,我們只是稍微組織一下我們的指令碼。
1、因此,我們將建立一個debugDraw()方法,它將處理所有與除錯繪圖
function debugDraw(){ 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); }
2、然後,由於我們要建立很多的磚塊,所以我強烈推薦用一個方法通過磚 塊的位置和尺寸來建立磚塊,所以下面的brick()方法的引數為:磚塊原點 的水平和垂直座標,寬度和高度。
function brick(px, py, w, h){ var bodyDef = new b2BodyDef(); bodyDef.position.Set(px/worldScale, py/worldScale); //bodyDef.type = b2Body.b2_dynamicBody; var polygonShape = new b2PolygonShape(); polygonShape.SetAsBox(w/2/worldScale, h/2/worldScale); var fixtureDef = new b2FixtureDef(); fixtureDef.shape = polygonShape; fixtureDef.density = 2; fixtureDef.restitution = .4; fixtureDef.friction = .5; var theBrick = world.CreateBody(bodyDef); theBrick.CreateFixture(fixtureDef); }
如果你看看程式碼就會發現沒有什麼新東西,但是我希望你能看看突出顯示的那幾行程式碼:
° 首先,被註釋了的那行設定型別屬性的程式碼, 因此目前我們建立的都 是static型別的剛體。當你在設定關卡的時候,使用static型別的剛體比 較好,一旦你滿意你所設計的關卡時,可以將它們轉變為dynamic型別 的剛體。static型別的剛體將幫助你檢視每一個剛體的精確位置,避免 重疊或其它的設計錯誤。
° 其次,在SetAsBox()方法中, 我們將磚塊的寬高分別除以2再除以之前 的worldScale。無論它要的是磚塊的半寬和半高,我這麼做是想要用真 實的寬高畫素來呼叫brick()方法,
3、 你的Main類的方法應該看起來向下面這樣:
function main(){ world = new b2World(gravity, sleep); debugDraw(); // level design goes here setInterval(updateWorld, 1000 / 60); }
現在Main()方法看起來簡單清晰,那麼我們可以通過呼叫brick()方法,一次一 塊的來搭建我們的關卡。
並且,請注意我將重力(gravity)設定為(0,5)而不是作為小球下落時的真 實世界的重力。較弱的重力將會使圖騰破壞和下落變得慢一些,從而產生更 好的效果。總之,這只是個與遊戲設計的建議,而且你可以自由設定你自己 的重力。
4、現在,讓我們回到Main()方法中:
function main(){ world = new b2World(gravity, sleep); debugDraw(); brick(275,435,30,30); brick(365,435,30,30); brick(320,405,120,30); brick(320,375,60,30); brick(305,345,90,30); brick(320,300,120,60); setInterval(updateWorld, 1000 / 60); }
我們先搭建左邊的基石磚塊和右邊的基石磚塊,然後剩下的磚塊從下向上一次堆放。
5、測試你的影片
完整原始碼在demo2-4.html中檢視
我想你會為你的測試成功而感到非常的開心。正如你學習搭建你的第一個圖騰那 樣,我們準備將黃金神像放到圖騰的頂部。在我們新增地面和改變圖騰剛體為dynamic型別之前,我希望你思考一下黃金神 像。 假使我們想要給黃金神像一個形狀,而這個形狀是不同於矩形和圓形的形狀,這將會怎樣呢?
建立複合剛體
黃金神像在圖騰破壞者中是主要角色,我們不能用一個矩形來代表它或者否則詛咒的圖騰將會永遠困擾著我們。
我想象出了下面的圖形:
這就我說的黃金神像,就是我們將要在Box2D中建立的物體。
上圖的左邊是神像剛體的輪廓,然後右邊是完成的神像輪廓。
首先你看到的神像是由不止一個剛體組合而成的一個複雜剛體。記住Box2D只能接受 凸多邊形。和圖騰磚塊只是單個的堆疊不同,我們需要通過某種方法合併所有的神像 組成物件。
首先,我們將從建立一個垂直的矩形開始,這個我們已經知道怎樣建立了,我們將把 它的程式碼放在Main()方法中最後一塊圖騰磚塊之後。
function main(){ world = new b2World(gravity, sleep); debugDraw(); brick(275,435,30,30); brick(365,435,30,30); brick(320,405,120,30); brick(320,375,60,30); brick(305,345,90,30); brick(320,300,120,60); idol(320, 242); setInterval(updateWorld, 1000 / 60); }
下面的idol()方法是我們建立神像的方法:
function idol(px, py){ var bodyDef = new b2BodyDef(); bodyDef.position.Set(px/worldScale, py/worldScale); //bodyDef.type = b2Body.b2_dynamicBody; var polygonShape = new b2PolygonShape(); polygonShape.SetAsBox(5/worldScale, 20/worldScale); var fixtureDef = new b2FixtureDef(); fixtureDef.shape = polygonShape; fixtureDef.density = 1; fixtureDef.restitution = .4; fixtureDef.friction = .5; var theIdol = world.CreateBody(bodyDef); theIdol.CreateFixture(fixtureDef); }
目前,我們只新增了一個矩形形狀,就和磚塊一樣,所以這些程式碼無需說明。
測試影片,你將會看到神像的剛體。
完整原始碼在demo2-5.html中檢視
第二部分,我們將建立一個交叉的形狀。這個交叉的形狀是由兩個矩形組成,但是 這次他們分別要順時針和逆時針旋轉45度。
你將在下面學到建立定向的矩形形狀。
建立定向的矩形
建立定向矩形形狀,我將使用b2PolygonShape類中和SetAsBox()方法相似的 SetAsOrientedBox()增強方法。 引數是矩形的寬和高,矩形的中心定義是一個b2Vec2物件和旋轉的弧度。
1、應用上面的方法,在idol()方法中按照下面的步驟操作:
var bW = 5/worldScale; var bH = 20/worldScale; var boxPos = new b2Vec2(0, 10/worldScale);// 可以使用矩形內的相對位置 var boxAngle = -Math.PI / 4;
前兩行程式碼,我們定義了矩形的尺寸,和我們剛剛建立的矩形一樣。
讓我們來看看第三行,在這裡我們定義了位置,你可能會有疑惑。第一個神 像的元件矩形的中心為(320,242)畫素,所以為什麼我要將第二個神像矩形 的位置設定為(0,10)?不是應該放置在第一塊神像元件矩形的附近嗎? 這是你需要學習複合物件神奇的地方。現在,我們不需要定義絕對位置,而是定義一個相對第一個神像元件矩形的位置。所以,這將意味著,矩形將放置在剛體中心偏下的位置。最後一行就是指定旋轉的弧度,逆時針45度。
2、你可以將這四個變數作為引數傳入SetAsOrientedBox()方法:
polygonShape.SetAsOrientedBox(bW, bH, boxPos, boxAngle);
3、然後,照例我們要更新夾具的shape屬性:
fixtureDef.shape = polygonShape;
4、那麼,神奇的神奇的事情要發生了,我不用去建立一個新b2Body物件,我們 將夾具附加到已經存在的theIdol剛體上:
theIdol.CreateFixture(fixtureDef);
5、如果我們為交叉形狀的另一個矩形應用相同的引數,我們需要改變一下boxAngle變數:
boxAngle=Math.PI/4;
6、然後我們可以建立定向矩形,更新夾具的shape屬性,然後新增它到theIdol剛 體上:
polygonShape.SetAsOrientedBox(bW, bH, boxPos, boxAngle); fixtureDef.shape = polygonShape; theIdol.CreateFixture(fixtureDef);
7、最後,idol()方法將看起來和下面的程式碼片段一樣:
function idol(px, py){ var bodyDef = new b2BodyDef(); bodyDef.position.Set(px/worldScale, py/worldScale); //bodyDef.type = b2Body.b2_dynamicBody; var polygonShape = new b2PolygonShape(); polygonShape.SetAsBox(5/worldScale, 20/worldScale); var fixtureDef = new b2FixtureDef(); fixtureDef.shape = polygonShape; fixtureDef.density = 1; fixtureDef.restitution = .4; fixtureDef.friction = .5; var theIdol = world.CreateBody(bodyDef); theIdol.CreateFixture(fixtureDef); // 建立定向矩形 var bW = 5/worldScale; var bH = 20/worldScale; var boxPos = new b2Vec2(0, 10/worldScale);// 可以使用矩形內的相對位置 var boxAngle = -Math.PI / 4; // 左傾矩形 polygonShape.SetAsOrientedBox(bW, bH, boxPos, boxAngle); fixtureDef.shape = polygonShape; theIdol.CreateFixture(fixtureDef); // 右傾矩形 boxAngle = Math.PI / 4; polygonShape.SetAsOrientedBox(bW, bH, boxPos, boxAngle); fixtureDef.shape = polygonShape; theIdol.CreateFixture(fixtureDef); }
8、測試影片,我們將看到神像與之前的圖案比較相似了:
完整程式碼在demo2-6.html
現在,我們仍需要建立它的頭部。
這次,你應該可以自己去建立它了吧。總而言之,它只是另一個複合剛體的定向矩形部件。我將向你展示另外一種方式,使用更加強大的方法建立一個多邊形形狀。
建立各種型別的凸多邊形
Box2D允許你建立任何種類的多邊形形狀,只要多邊形是凸多邊形,這將意味著它擁 有的所有內角要小於180度,所以所有的頂點要遠離中心,而且你要按順時針順序排 列它們。
1、首先,讓我們建立一個向量(Vector)來儲存所有的頂點:
// 用多邊形方式建立凸多邊形 var vertices = new b2Vec2();
2、然後,我們將所有頂點作為b2Vec2物件並順時針順序推入向量(vertices)中, 併為b2Vec2物件指派頂點相對神像剛體的中心的座標位置。
var vertices = new Box2D.NVector(); vertices.push(new b2Vec2(-15/worldScale,-25/worldScale)); vertices.push(new b2Vec2(0,-90/worldScale)); vertices.push(new b2Vec2(15/worldScale, -25/worldScale)); vertices.push(new b2Vec2(0,-10/worldScale));
3、之前的幾行程式碼表示的是神像的頭部的四個頂點。現在讓我們將向(vector) 變成多邊形形狀
polygonShape.SetAsVector(vertices, 4);
SetAsVector方法將任何順時針方向的頂點向量(vector)變成一個多邊形形 狀。第二個引數只是代表需要的頂點數。
4、最後,和通常一樣,你需要更新夾具的shape屬性並將它新增到theIdol剛 體上
fixtureDef.shape = polygonShape;
theIdol.CreateFixture(fixtureDef);
5、下面就是idol()方法看起來的樣子:
function idol(px, py){ var bodyDef = new b2BodyDef(); bodyDef.position.Set(px/worldScale, py/worldScale); bodyDef.type = b2Body.b2_dynamicBody; var polygonShape = new b2PolygonShape(); polygonShape.SetAsBox(5/worldScale, 20/worldScale); var fixtureDef = new b2FixtureDef(); fixtureDef.shape = polygonShape; fixtureDef.density = 1; fixtureDef.restitution = .4; fixtureDef.friction = .5; var theIdol = world.CreateBody(bodyDef); theIdol.CreateFixture(fixtureDef); // 建立定向矩形 var bW = 5/worldScale; var bH = 20/worldScale; var boxPos = new b2Vec2(0, 10/worldScale);// 可以使用矩形內的相對位置 var boxAngle = -Math.PI / 4; // 左傾矩形 polygonShape.SetAsOrientedBox(bW, bH, boxPos, boxAngle); fixtureDef.shape = polygonShape; theIdol.CreateFixture(fixtureDef); // 右傾矩形 boxAngle = Math.PI / 4; polygonShape.SetAsOrientedBox(bW, bH, boxPos, boxAngle); fixtureDef.shape = polygonShape; theIdol.CreateFixture(fixtureDef); // 用多邊形方式建立凸多邊形 var vertices = new Box2D.NVector(); vertices.push(new b2Vec2(-15/worldScale,-25/worldScale)); vertices.push(new b2Vec2(0,-90/worldScale)); vertices.push(new b2Vec2(15/worldScale, -25/worldScale)); vertices.push(new b2Vec2(0,-10/worldScale)); polygonShape.SetAsVector(vertices, 4); fixtureDef.shape = polygonShape; theIdol.CreateFixture(fixtureDef); }
6、測試影片然後你將在最後看到你完成的在圖騰頂部的神像
7、這時,你只需要建立地面,它是一個static型別的矩形剛體
function main(){ world = new b2World(gravity, sleep); debugDraw(); brick(275,435,30,30); brick(365,435,30,30); brick(320,405,120,30); brick(320,375,60,30); brick(305,345,90,30); brick(320,300,120,60); idol(320, 242); floor(); setInterval(updateWorld, 1000 / 60); }
8、下面是floor()方法的定義:
function floor(){ var bodyDef = new b2BodyDef(); bodyDef.position.Set(320/worldScale, 465/worldScale); var polygonShape = new b2PolygonShape(); polygonShape.SetAsBox(320/worldScale, 15/worldScale); var fixtureDef = new b2FixtureDef(); fixtureDef.shape = polygonShape; fixtureDef.restitution = .4; fixtureDef.friction = .5; var theFloor = world.CreateBody(bodyDef); theFloor.CreateFixture(fixtureDef); }
9、然後刪除brick()方法內的註釋行,將磚塊設定為dynamic型別:
bodyDef.type = b2Body.b2_dynamicBody;
10、最後,我們將神像的也設定為dynamic型別 idol()方法內
bodyDef.type = b2Body.b2_dynamicBody;
正如本章開始時所說的,你在Box2D中建立一個真實的圖騰破壞者關卡已經完成
完整程式碼請檢視demo2-7
概述
你剛剛學習的是本書中最重要的章節之一,在這裡你學習了怎樣建立剛體然後
使用它們去設計成功遊戲的關卡,例如圖騰破壞者。
為了習慣使用Box2D的剛體,我建議你建立更多的圖騰破壞者(Totem Destroyer)的關卡或者一些紅磚移除(Red Remover)或者憤怒的小鳥(Angry Birds)的關卡。總之,它只是一個簡單的形狀。
本文相關程式碼請在
https://github.com/willian12345/Box2D-for-Javascript-Games
注:轉載請註明出處部落格園:sheldon-二狗-偷飯貓(willian12345@126.com)
https://github.com/willian12345