然后是更多的Box2D元素?
创建矩形物体
和创建地面大同小异,修改坐标长宽即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| function createRectangularBody(){ var bodyDef = new b2BodyDef; bodyDef.type = b2Body.b2_dynamicBody; bodyDef.position.x = 40/scale; bodyDef.position.y = 100/scale; var fixtureDef = new b2FixtureDef; fixtureDef.density = 1.0; fixtureDef.friction = 0.5; fixtureDef.restitution = 0.3; fixtureDef.shape = new b2PolygonShape; fixtureDef.shape.SetAsBox(30/scale, 50/scale); var body = world.CreateBody(bodyDef); var fixture = body.CreateFixture(fixtureDef); }
|
创建圆形物体
在特性中形状修改为圆,赋予半径即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| function createCircularBody(){ var bodyDef = new b2BodyDef; bodyDef.type = b2Body.b2_dynamicBody; bodyDef.position.x = 130/scale; bodyDef.position.y = 130/scale; var fixtureDef = new b2FixtureDef; fixtureDef.density = 1.0; fixtureDef.friction = 0.5; fixtureDef.restitution = 0.7; fixtureDef.shape = new b2CircleShape(30/scale); var body = world.CreateBody(bodyDef); var fixture = body.CreateFixture(fixtureDef); }
|
创建多边形物体
利用向量数组来提供多边形顶点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| function createSimplePolygonBody(){ var bodyDef = new b2BodyDef; bodyDef.type = b2Body.b2_dynamicBody; bodyDef.position.x = 230/scale; bodyDef.position.y = 50/scale; var fixtureDef = new b2FixtureDef; fixtureDef.density = 1.0; fixtureDef.friction = 0.5; fixtureDef.restitution = 0.2; fixtureDef.shape = new b2PolygonShape(); var points = [ new b2Vec2(0, 0), new b2Vec2(40/scale, 50/scale), new b2Vec2(50/scale, 100/scale), new b2Vec2(-50/scale, 100/scale), new b2Vec2(-40/scale, 50/scale), ]; fixtureDef.shape.SetAsArray(points, points.length); var body = world.CreateBody(bodyDef); var fixture = body.CreateFixture(fixtureDef); }
|
创建多种形状的复杂物体
注意当由多个物体拼接时,将添加入世界的物体直接赋予多个特性即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| function createComplexBody(){ var bodyDef = new b2BodyDef; bodyDef.type = b2Body.b2_dynamicBody; bodyDef.position.x = 350/scale; bodyDef.position.y = 50/scale; var body = world.CreateBody(bodyDef); var fixtureDef = new b2FixtureDef; fixtureDef.density = 1.0; fixtureDef.friction = 0.5; fixtureDef.restitution = 0.7; fixtureDef.shape = new b2CircleShape(40/scale); body.CreateFixture(fixtureDef); fixtureDef.shape = new b2PolygonShape(); var points = [ new b2Vec2(0, 0), new b2Vec2(40/scale, 50/scale), new b2Vec2(50/scale, 100/scale), new b2Vec2(-50/scale, 100/scale), new b2Vec2(-40/scale, 50/scale), ]; fixtureDef.shape.SetAsArray(points, points.length); body.CreateFixture(fixtureDef); }
|
连接物体的接合点
类似于风车的转轴,需要指定两个物体和结合点的坐标。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| function createRevoluteJoint(){ var bodyDef1 = new b2BodyDef; bodyDef1.type = b2Body.b2_dynamicBody; bodyDef1.position.x = 480/scale; bodyDef1.position.y = 50/scale; var body1 = world.CreateBody(bodyDef1); var fixtureDef1 = new b2FixtureDef; fixtureDef1.density = 1.0; fixtureDef1.friction = 0.5; fixtureDef1.restitution = 0.5; fixtureDef1.shape = new b2PolygonShape(); fixtureDef1.shape.SetAsBox(50/scale, 10/scale); body1.CreateFixture(fixtureDef1); var bodyDef2 = new b2BodyDef; bodyDef2.type = b2Body.b2_dynamicBody; bodyDef2.position.x = 480/scale; bodyDef2.position.y = 50/scale; var body2 = world.CreateBody(bodyDef2); var fixtureDef2 = new b2FixtureDef; fixtureDef2.shape = new b2PolygonShape(); var points = [ new b2Vec2(0, 0), new b2Vec2(40/scale, 50/scale), new b2Vec2(50/scale, 100/scale), new b2Vec2(-50/scale, 100/scale), new b2Vec2(-40/scale, 50/scale), ]; fixtureDef2.shape.SetAsArray(points, points.length); body2.CreateFixture(fixtureDef2); var jointDef = new b2RevoluteJointDef; var jointCenter = new b2Vec2(470/scale, 50/scale); jointDef.Initialize(body1, body2, jointCenter); world.CreateJoint(jointDef); }
|
创建具有自定义属性的物体
注意第9行代码,我们利用特有的SetUserData方法可以给物体赋予自定义属性,通过GetUserData获取它。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| var specialBody; function createSpecialBody(){ var bodyDef = new b2BodyDef; bodyDef.type = b2Body.b2_dynamicBody; bodyDef.position.x = 450/scale; bodyDef.position.y = 0/scale; specialBody = world.CreateBody(bodyDef); specialBody.SetUserData({ name:"special",life:250 }) var fixtureDef = new b2FixtureDef; fixtureDef.density = 1.0; fixtureDef.friction = 0.5; fixtureDef.restitution = 0.5; fixtureDef.shape = new b2CircleShape(30/scale); var fixture = specialBody.CreateFixture(fixtureDef); }
|
很有用的追踪碰撞与损坏!
接触监听器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| function listenForContact(){ var listener = new Box2D.Dynamics.b2ContactListener; listener.PostSolve = function(contact, impulse){ var body1 = contact.GetFixtureA().GetBody(); var body2 = contact.GetFixtureB().GetBody(); if (body1 == specialBody || body2 == specialBody){ var impulseAlongNormal = impulse.normalImpulses[0]; specialBody.GetUserData().life -= impulseAlongNormal; console.log("The special body was in a collision with impulse", impulseAlongNormal, "and its life has now become ", ~~specialBody.GetUserData().life); } }; world.SetContactListener(listener); }
|
绘制角色
这里我们使用canvas原生绘图,获取暴露的自定义物体的坐标和偏移角度,然后通过“覆盖”的方法将自定义图形通过生命值来绘制特有的表情。
有一个调试技巧,就是在实际的canvas旁边放置一个testCanvas,将world世界里的图像绘制到testCanvas里。然后通过获取到每个图形信息,把对应的图片或者新绘制的图形放置到真canvas里,也就是要实际使用的canvas中,可以方便地实现调试。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| function drawSpecialBody(){ var position = specialBody.GetPosition(); var angle = specialBody.GetAngle(); context.translate(position.x*scale, position.y*scale); context.rotate(angle); context.fillStyle = "rgb(200,150,250)"; context.beginPath(); context.arc(0, 0, 30, 0, 2*Math.PI, false); context.fill(); context.fillStyle = "rgb(255,255,255)"; context.fillRect(-15, -15, 10, 5); context.fillRect(5, -15, 10, 5); context.strokeStyle = "rgb(255,255,255)"; context.beginPath(); if (specialBody.GetUserData().life > 100) { context.arc(0, 0, 10, Math.PI, 2*Math.PI, true); } else { context.arc(0, 10, 10, Math.PI, 2*Math.PI, false); } context.stroke(); context.rotate(-angle); context.translate(-position.x*scale, -position.y*scale); }
|
小结
XANA工作室以前做游戏的时候碰撞我使用的是向量模型,来代替简单的x,y的四则运算,通过一系列定义的向量方法造的轮子来做碰撞的检测。当然缺点很明显,第一个就是碰撞的处理不够完善,物体的密度、弹性、摩擦均没有考虑在内。然而Box2D提供了相当成熟的API,虽然它的学习成本不低,理论相当复杂,总之在2D的物体计算方面,Box2D库是相当不错的。