第二章又拖到現在才發佈,話說本次更新離上次已經很久了。不知道大家還記得上一章講的內容否?
在上一章中,我們創建了各式各樣的地形,今天我們就在這個地形之上,創建一輛自行車,並讓它受到我們的控制。首先放上一張截圖:
沒看過上一章的同學可以先移步到上一章。在瞭解上一章的內容之後,讀起本章方可容易一些。
HTML5物理遊戲開發 - 越野山地自行車(一)建立各式各樣的地形
http://blog.csdn.net/yorhomwang/article/details/19710537
※再次聲明,本次開發用到了lufylegend.js開源html5遊戲引擎和box2dweb物理引擎,請到官方網站下載使用。官方網站地址已在第一章中說過了。
一,基礎知識
首先我們來科普幾個知識,只有瞭解這些知識之後,讀起下面的內容纔不會感覺頭疼。
1,如何使剛體移動
在box2dweb中,要想使剛體移動,不能單純地改變x、y座標。其一,這樣會使你的遊戲失去了物理運動的效果;其二,在box2dweb中,直接調整剛體的位置是一個十分不好的方法,會違揹物理運動原理,只有在剛體創建前的時候才用這個方法。所以在我們的自行車創建出來以後,想要移動剛體,最好的方法就是給剛體施加一個力。
在box2dweb中,施加力的方法有:ApplyForce、ApplyImpulse、SetLinearVelocity。本次使用的只有ApplyForce。其餘的幾種可以暫且不管。如果你確實想了解的話,可以看看ladeng6666的這篇文章:讓剛體聽我的——ApplyForce、ApplyImpulse、SetLinearVelocity
ApplyForce的用法如下:
ApplyForce(vec, pos)
這個函數的第個一個參數是給物體施加力的向量(b2Vec2);第二個pos是施加的位置,一般不確定位置的時候就取物體的重心(可以用b2Body的GetWorldCenter獲取)。
2,如何構造像自行車這樣形狀複雜的物體
如何構造像自行車這樣形狀複雜的物體確實是一個值得思考的話題。對我們而言,第一個出現在腦子裏的想法就是創建一個多邊形剛體。當然這樣實現起來極其麻煩,而且不僅僅是麻煩,做到最後更掃興的是使用這種方法弄出來的剛體碰撞檢測有問題,換言之,一個原本是凹凸多邊形的剛體,突然就成了柔體,碰在其他物體上就會滲入其他物體裏,so qipa~
那到底應該怎麼辦呢?我們不妨假設自己就真正地在製作一輛自行車。首先,我們要把材料和工具準備好。材料就是幾個木塊,工具就是一個錘子,一把鐵釘。接下來,我們要做的就是用錘子鐵釘把幾個木塊組裝起來就ok了。這樣看起來雖然so easy,但是有朋友會問,在box2dweb裏到哪裏去找錘子鐵釘呢?錘子鐵釘在box2dweb裏倒是真沒有,不過有一個更先進的東西——關節(joint)。
在這裏主要就是需要兩種關節:旋轉關節(用於把輪子和支架綁起來),焊接關節(用於把各個支架固定起來)。
這兩個關節在box2dweb裏的使用方法依然不是那麼簡單,因此同樣用到了lufylegend.js的封裝。接下來就對這幾個關節的用法進行說明。
■setRevoluteJoint(b2BodyA, b2BodyB, limits, motors)
b2BodyA:表示物體A (b2Body對象,可以用LSprite的box2dBody屬性獲取)
b2BodyB:表示物體B(b2Body對象,可以用LSprite的box2dBody屬性獲取)
limits:表示旋轉角度限制數組,這個數組的內容是:[最小角度,最大角度],它在這裏可以限制旋轉關節旋轉的角度(可以不傳)
motors:表示馬達數組,這個數組的內容是:[力度,速度],馬達可以有很多用途,在這裏,它可以是關節自動進行旋轉(可以不傳)
示例:
var backLayer,cLayer;
function main(){
LGlobal.setDebug(true);
backLayer = new LSprite();
addChild(backLayer);
LGlobal.box2d = new LBox2d();
cLayer = new LSprite();
cLayer.x = 300;
cLayer.y = 390;
backLayer.addChild(cLayer);
cLayer.addBodyPolygon(600,10,0,5,0.4,0.2);
//加入一個動態的圓形物體1
box01 = new LSprite();
box01.x = 250;
box01.y = 200;
backLayer.addChild(box01);
box01.addBodyCircle(100,0,0,1,1,0.4,0.2);
box01.setBodyMouseJoint(true);
//加入一個靜態的圓形物體2
box02 = new LSprite();
box02.x = 250;
box02.y = 150;
backLayer.addChild(box02);
box02.addBodyCircle(10,0,0,0,1,0.4,0.2);
//加入一個旋轉關節
LGlobal.box2d.setRevoluteJoint(box01.box2dBody, box02.box2dBody ,[-360,720*5],[450,2]);
}
■setWeldJoint (b2BodyA, b2BodyB)
b2BodyA:表示捆綁對象物體A(b2Body對象,可以用LSprite的box2dBody屬性獲取)
b2BodyB:表示捆綁對象物體B(b2Body對象,可以用LSprite的box2dBody屬性獲取)
示例:
var backLayer,cLayer;
function main(){
LGlobal.setDebug(true);
backLayer = new LSprite();
addChild(backLayer);
LGlobal.box2d = new LBox2d();
cLayer = new LSprite();
cLayer.x = 300;
cLayer.y = 390;
backLayer.addChild(cLayer);
cLayer.addBodyPolygon(600,10,0,5,0.4,0.2);
//加入一個動態的圓形物體1
box01 = new LSprite();
box01.x = 200;
box01.y = 100;
backLayer.addChild(box01);
box01.addBodyCircle(50,0,0,1,1,0.4,0.2);
box01.setBodyMouseJoint(true);
//加入一個動態的圓形物體2
box02 = new LSprite();
box02.x = 250;
box02.y = 100;
backLayer.addChild(box02);
box02.addBodyCircle(50,0,0,1,1,0.4,0.2);
box02.setBodyMouseJoint(true);
//加入一個焊接關節
LGlobal.box2d.setWeldJoint(box01.box2dBody, box02.box2dBody);
}
ok,基礎知識差不多講完了。進入正題吧。
二,修改Main類
上一章中的Main類大家還記得否?上次我沒有加入真正的自行車,而是拿一個圓形小球在那裏充當着,主要是給大家看看鏡頭跟隨效果和各式各樣的地形。這次既然要實現一個自行車,那麼就要先把小球換掉,於是更改addBicycle函數,更新後如下:
Main.prototype.addBicycle = function(){
var s = this;
//創建自行車對象
s.bicycleObj = new Bicycle(50,385);
s.addChild(s.bicycleObj);
};
這裏明顯用到了Bicycle這個類。那麼,我就立刻把筆尖指向Bicycle類吧,這個類是本章的重點內容,前面的基礎講解就是爲這個類作鋪墊的。三,自行車類(Bicycle)
先看看構造器:
function Bicycle(sx,sy){
var s = this;
base(s,LSprite,[]);
/**初始座標*/
s.sx = sx;
s.sy = sy;
/**剛體所屬LSprite對象列表*/
s.bodyList = new Array();
/**添加左右移動力度向量*/
s.moveVec = new LStage.box2d.b2Vec2();
/**添加拉壓操作力度向量*/
s.tcVec = new LStage.box2d.b2Vec2();
/**添加事件*/
//鍵盤按下事件
LEvent.addEventListener(window,LKeyboardEvent.KEY_DOWN,function(e){
s.onKeydown(e,s);
});
//鍵盤松開事件
LEvent.addEventListener(window,LKeyboardEvent.KEY_UP,function(e){
s.onKeyup(e,s);
});
//初始化
s.init();
}
構造器代碼加入了詳細的註釋,所以我們直接進入講解Bicycle的init函數。這是代碼:
Bicycle.prototype.init = function(){
var s = this;
var sx = s.sx;
var sy = s.sy;
/**輪子半徑*/
var wheelR = 20;
/**輪子之間的距離*/
var gapBetweenWheelAndWheel = 100;
/**車手柄到輪子的距離*/
var gapBetweenWheelAndHandlebar = 50;
/**車把尺寸*/
var handlebarWidth=20,handlebarHeight=5;
/**座椅到輪子支架的距離*/
var gapBetweenWheelFrameAndSeat = 30;
/**座椅尺寸*/
var seatWidth=30,seatHeight=5;
/**支架尺寸*/
var frameSize = 10;
/**加入支架*/
//輪子上的支架
var frameAObj = new LSprite();
frameAObj.x = sx+gapBetweenWheelAndWheel/2;
frameAObj.y = sy+frameSize/2;
frameAObj.addBodyPolygon(gapBetweenWheelAndWheel,frameSize,1,5);
world.addChild(frameAObj);
s.bodyList.push(frameAObj);
//車把到輪子的支架
var frameBObj = new LSprite();
frameBObj.x = sx+gapBetweenWheelAndWheel-frameSize/2;
frameBObj.y = sy-gapBetweenWheelAndHandlebar/2;
frameBObj.addBodyPolygon(frameSize,gapBetweenWheelAndHandlebar,1,2);
world.addChild(frameBObj);
s.bodyList.push(frameBObj);
/**加入車把*/
var handlebarObj = new LSprite();
handlebarObj.x = sx+gapBetweenWheelAndWheel-handlebarWidth/2-frameSize;
handlebarObj.y = sy-gapBetweenWheelAndHandlebar+handlebarHeight/2;
handlebarObj.addBodyPolygon(handlebarWidth,handlebarHeight,1,.5);
world.addChild(handlebarObj);
s.bodyList.push(handlebarObj);
/**加入座椅*/
//座椅到輪子支架的支架
var seatFrameObj = new LSprite();
seatFrameObj.x = sx+30;
seatFrameObj.y = sy-gapBetweenWheelFrameAndSeat/2;
seatFrameObj.addBodyPolygon(frameSize,gapBetweenWheelFrameAndSeat,1,1);
world.addChild(seatFrameObj);
s.bodyList.push(seatFrameObj);
//座椅
var seatObj = new LSprite();
seatObj.x = sx+30;
seatObj.y = sy-gapBetweenWheelFrameAndSeat-seatHeight/2;
seatObj.addBodyPolygon(seatWidth,seatHeight,1,.5);
world.addChild(seatObj);
s.bodyList.push(seatObj);
/**加入輪子*/
//左邊輪子A
var wheelAObj = new LSprite();
wheelAObj.x = sx-wheelR;
wheelAObj.y = sy;
wheelAObj.addBodyCircle(wheelR,wheelR,wheelR,1,2.5,.2,.4);
world.addChild(wheelAObj);
s.bodyList.push(wheelAObj);
//右邊輪子B
var wheelBObj = new LSprite();
wheelBObj.x = sx+gapBetweenWheelAndWheel-wheelR;
wheelBObj.y = sy;
wheelBObj.addBodyCircle(wheelR,wheelR,wheelR,1,2.5,.2,.4);
world.addChild(wheelBObj);
s.bodyList.push(wheelBObj);
/**添加關節*/
//輪子A和輪子支架的旋轉關節
LStage.box2d.setRevoluteJoint(frameAObj.box2dBody, wheelAObj.box2dBody);
//輪子B和輪子支架的旋轉關節
LStage.box2d.setRevoluteJoint(frameAObj.box2dBody, wheelBObj.box2dBody);
//車把到輪子的支架和輪子支架的焊接關節
LStage.box2d.setWeldJoint(frameAObj.box2dBody, frameBObj.box2dBody);
//車把到輪子的支架和車把的焊接關節
LStage.box2d.setWeldJoint(handlebarObj.box2dBody, frameBObj.box2dBody);
//輪子的支架和座椅的焊接關節
LStage.box2d.setWeldJoint(seatFrameObj.box2dBody, frameAObj.box2dBody);
//座椅的支架和座椅的焊接關節
LStage.box2d.setWeldJoint(seatFrameObj.box2dBody, seatObj.box2dBody);
/**遍歷所有自行車零件剛體*/
for(var key in s.bodyList){
var obj = s.bodyList[key];
//加入鼠標拖動
if(obj.box2dBody)obj.setBodyMouseJoint(true);
//設置對象名稱
obj.name = "bicycle";
}
/**設置主剛體*/
s.mainBody = frameAObj.box2dBody;
/**設置拉壓操作剛體*/
s.tcBody = wheelBObj.box2dBody;
};
這個函數看上去超長,但是邏輯卻異常簡單,大部分是重複的代碼,再通過上面的基礎講解外加註釋,看上去應該是無壓力了吧,當然如果有不懂的,歡迎在本文下方留言。其中,我爲了大家測試時的簡化操作,給每一塊剛體都設置了鼠標拖動,但是又由於剛體有很多,所以我把它們統統裝在bodyList這個數組了,然後最後用遍歷的方式給每一個剛體都設置了鼠標拖動。最後來看看如何實現鍵盤操作。不難發現,在構造器裏有這麼一段代碼:
/**添加事件*/
//鍵盤按下事件
LEvent.addEventListener(window,LKeyboardEvent.KEY_DOWN,function(e){
s.onKeydown(e,s);
});
//鍵盤松開事件
LEvent.addEventListener(window,LKeyboardEvent.KEY_UP,function(e){
s.onKeyup(e,s);
});
在段代碼裏,我們還用到了onKeydown和onKeyup這兩個函數。代碼如下:Bicycle.prototype.onKeydown = function(e,s){
var force = 50;
switch(e.keyCode){
//向右
case 39:
s.moveVec.x = force;
break;
//向左
case 37:
s.moveVec.x = -force;
break;
//向上
case 38:
s.tcVec.y = -force;
break;
//向下
case 40:
s.tcVec.y = force;
break;
default:
return;
}
/**施加移動的力*/
s.mainBody.ApplyForce(s.moveVec,s.mainBody.GetWorldCenter());
/**施加拉壓的力*/
s.tcBody.ApplyForce(s.tcVec,s.tcBody.GetWorldCenter());
};
Bicycle.prototype.onKeyup = function(e,s){
/**清空力度相量*/
s.moveVec.SetZero();
s.tcVec.SetZero();
};
在這裏我們用到了之前講過的ApplyForce。之前講的時候,沒有具體講第一個參數是因爲沒有實際用到講了等於徒勞。現在可以看到,這個b2Vec2有x,y屬性,這兩個屬性可以控制力的方向。x<0向左,x>0向右,y>0向下,y<0向上。
當然,tcVec和moveVec是兩個不同的向量,也就是說控制的不是同一個效果。moveVec是控制移動自行車的,這個不用多說。而tcVec是用於拉、壓自行車的,我們知道,當在遊戲中要開翻車的時候,往往可以用拉、壓的方式來操控自行車,使其重新平衡,這個tcVec就是幹這活兒的。
BTW,如果你不知道tcBody和mainBody是什麼,可以看看Bicycle裏init函數的代碼。
添加完鍵盤事件後,我們就可以通過方向鍵來控制自行車了。操作方式爲:上-拉,下-壓,左-後退,右-前進。大家可以打開本文最下方的測試鏈接進行測試。
ok,至此,運行一下代碼,得到的就是本文最上方圖片所示的效果了。(什麼?太醜了?你指的是我還是這個遊戲?這個遊戲啊,沒貼圖當然醜呢。)
奉上源代碼下載地址:http://files.cnblogs.com/yorhom/box2dBicycle%282%29.rar
測試地址:http://www.cnblogs.com/yorhom/articles/box2dweb_bike2.html
下一章預告:目前,咋們的自行車是金剛不壞之身,你怎麼摔它,它就是摔不碎(除非你把顯示器抱起來,然後使勁往地上摔)。但是這不科學對吧……所以在下一章,我們就來看看,如何把這個自行車摔得支離破碎,體無完膚。哈哈,盡情期待~
本章就先到這裏了。如果文章有任何疏漏之處,歡迎指正。當然,有不懂之處也歡迎各位在本文下方留言,我會盡力回覆大家的。
----------------------------------------------------------------
歡迎大家轉載我的文章。
轉載請註明:轉自Yorhom's Game Box
http://blog.csdn.net/yorhomwang
歡迎繼續關注我的博客