上次已實現箭矢的飛行效果,但是箭在碰到牆壁時直接反彈回來,像鋼棍而不是箭。在Box2d中,可以利用關節(Joint)將箭和靶子連接起來,組成一個整體,實現射擊效果。使用關節要在文件開頭添加新的變量,方便後面使用:
var b2Joints = Box2D.Dynamics.Joints;
var b2Contacts = Box2D.Dynamics.Contacts;
var b2Listener =Box2D.Dynamics.b2ContactListener;
箭與靶子的起始位置並不相同,要檢測箭是否射中靶子就要實時進行碰撞檢測,幸好Box2d可以自動進行這部分工作,不過檢測到碰撞時如何處理需要自己定義。所以有下面的聲明,聲明一個b2Listener,兩個物體碰撞時會調用Listener指定的方法。
var arrowContactListener = new b2Listener;
arrowContactListener.PreSolve = arrowPreSolve;
world.SetContactListener(arrowContactListener);
arrowPreSolve就是檢測到碰撞時要執行的方法,具體實現如下:
function arrowPreSolve(contact,oldManifold){
var contactPoint = new b2Vec2;
var weldJointDef = new b2Joints.b2WeldJointDef;
if(contact.IsTouching()){
var bodyA =contact.GetFixtureA().GetBody();
var bodyB = contact.GetFixtureB().GetBody();
var objA = bodyA.GetUserData();
var objB = bodyB.GetUserData();
if (objA.name=="arrow" &&objB.name=="arrow") {
for (var j = bodyA.GetJointList(); j; j=j.next) {
bodyA.GetWorld().DestroyJoint(j.joint);
}
for (j=bodyB.GetJointList(); j; j=j.next) {
bodyB.GetWorld().DestroyJoint(j.joint);
}
}
if (objA.name=="arrow" &&objB.name=="target") {
if (objA.isflying) {
weldJointDef = newb2Joints.b2WeldJointDef;
weldJointDef.Initialize(bodyB,bodyA,bodyA.GetWorldCenter());
bodyB.GetWorld().CreateJoint(weldJointDef);
}
console.log("Hit Target!");
}
if (objB.name=="wall"&&objA.name=="arrow") {
if (objA.isflying) {
weldJointDef = newb2Joints.b2WeldJointDef;
weldJointDef.Initialize(bodyA,bodyB,bodyB.GetWorldCenter());
bodyA.GetWorld().CreateJoint(weldJointDef);
}
}
if (objB.name=="arrow") {
objB.isflying=false;
}
if (objA.name=="arrow") {
objA.isflying=false;
}
}
}
幾個條件語句分別處理箭與箭碰撞,箭與靶子碰撞和箭與牆壁碰撞這三種情況,最後更新箭的飛行狀態。設置箭飛行狀態的主要目的就是保證當前世界中只有一支活動的箭。
平時一些小遊戲在射箭或者發生炮彈的時候都會有一個能量槽來表示力度的大小,接下來就向這個小遊戲中添加一個能量槽,按鼠標時間越長,射箭的力度越大。
首先向arrow.html文件中添加下面的div,表示能量槽,在後面的JS代碼中動態更新它的長度。
<divid="power_bar"></div>
然後在JS文件中修改鼠標消息響應方法,並添加一個定時修改能量槽長度的方法calculateStrength:
functiononMouseDown(e){
if(arrowGame.state == arrowGame.STATE_PLAY){
if(allowCreateArrow()) {
arrowGame.chargeTaskID =setInterval(calculateStrength, 25);
}
}
}
functiononMouseUp(e) {
if(arrowGame.state == arrowGame.STATE_PLAY){
// Only one arrow in gaming.
if(allowCreateArrow()) {
createArrow(e.clientX-canvasPosition.x,e.clientY-canvasPosition.y,arrowGame.power);
clearInterval(arrowGame.chargeTaskID);
arrowGame.power = 0;
}
}
}
functioncalculateStrength() {
arrowGame.power++;
arrowGame.powerBar.style.width =arrowGame.power*10 + "px";
if(arrowGame.powerBar.style.width =="300px"){
clearInterval(arrowGame.chargeTaskID);
}
}
在鼠標按下時,開啓定時器,增加能量槽的長度,當鼠標彈起時,根據當前的鼠標位置和能量槽長度創建一支箭。
效果如下圖:
爲方便後面編程,對整個JS文件的結構進行了修改,使用一個全局結構存儲遊戲狀態,並修改整個流程,細節部分可以參照源代碼。