创建图腾破坏者的关卡
现在你有能力创建你的第一个游戏原型,我们将从创建图腾破坏者的级别开始。
为了展示我们所做事情的真实性,我们将流行的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-二狗-偷饭猫([email protected])
https://github.com/willian12345