上次已实现箭矢的飞行效果,但是箭在碰到墙壁时直接反弹回来,像钢棍而不是箭。在Box2d中,可以利用关节(Joint)将箭和靶子连接起来,组成一个整体,实现射击效果。使用关节要在文件开头添加新的变量,方便后面使用:
[javascript]
var b2Joints = Box2D.Dynamics.Joints;
var b2Contacts = Box2D.Dynamics.Contacts;
var b2Listener =Box2D.Dynamics.b2ContactListener;
箭与靶子的起始位置并不相同,要检测箭是否射中靶子就要实时进行碰撞检测,幸好Box2d可以自动进行这部分工作,不过检测到碰撞时如何处理需要自己定义。所以有下面的声明,声明一个b2Listener,两个物体碰撞时会调用Listener指定的方法。
[javascript]
var arrowContactListener = new b2Listener;
arrowContactListener.PreSolve = arrowPreSolve;
world.SetContactListener(arrowContactListener);
arrowPreSolve就是检测到碰撞时要执行的方法,具体实现如下:
[javascript]
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代码中动态更新它的长度。
[html]
<dividdivid="power_bar"></div>
然后在JS文件中修改鼠标消息响应方法,
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);
}
}
在鼠标按下时,开启定时器,增加能量槽的长度,当鼠标弹起时,根据当前的鼠标位置和能量槽长度创建一支箭。
效果如下图: