这是系列第二部分,之前部分在本博客中找

源码demo存放在https://github.com/willian12345/Box2D-for-Javascript-Games

向世界添加刚体


刚体(Bodies)是我们用Box2D创建物理游戏的重要对象。任何你可以移动的或交互 的对象都是刚体(Bodies)。

愤怒的小鸟(Angry Birds)中创建的小鸟和小猪是刚 体,同样在图腾破坏者(Totem Destroyer)中的黄金神像和图腾砖块也是刚体。

本章将带你学习创建各种类型的Box2D刚体,此外还有一些其它重要的特性,如下表所列

• 创建圆形刚体


• 创建矩形刚体


• 创建任意多边形刚体

• 使用DebugDraw()方法测试模拟

• 定义刚体的类型:static,dynamic或kinimatic


• 设置材质属性:密度(density),摩擦系数(friction)和恢复系数(resitution)

• 度量单位


• 创建合成对象

通过本章的学习,你将会创建一个你的第一个图腾破坏者类型的游戏。本章有较

多的知识点,那么我们废话少说,直接开始本章的学习吧!

你的第一个模拟—一个球落地


我们先从简单的任务开始,最简单的物理模拟:一个球落到地面。总之,虽然

这是一个简单小球落地的模拟,但是它将是你的第一个模拟,并且易于很快实

现它。

让我们看看在这次模拟中我们要做些什么:

• 世界的重力(gravity)


• 一个受到作用力(例如:重力(gravity))的球

• 一个不受任何作用力的地面


• 某种材质,正如我们希望小球在地面弹起的材质

在之前的学习中,你已经能够配置世界的重力了,所以我们从创建小球开始本章的代

码编写。

1. 无论我们是创建球形还是多边形,第一步都是创建一个刚体:

  var bodyDef =new b2BodyDef();

  b2BodyDef类是一个刚体的定义类,它将持有创建我们刚体所需要的所有数 据。

2. 现在可以将刚体添加到世界中。因为我们采用的舞台尺寸是640X480,我们将 把球放置在舞台的顶部的中心位置,该位置为(320,30),如下所示:

  bodyDef.position.Set(10.66,1);

  通过position属性显示的设置了刚体在世界中的位置,但是我确信你会对我之前 所说的位置为(320,30)的设置而变成(10.66,1)而感到困惑。

  这原因要关系 到度量单位上。虽然Flash是以像素(pixels)为度量单位,但是在Box2D中尝试 模拟真实的世界并采用米(meters)作为度量单位。

  对于米(meters)和像素 (pixels)之间的转换没有通用的标准,但是我们采用下面的转换标准可以有很 好的运行效果:

  1米 = 30像素

  所以,如果我们定义一个变量来帮助我们将米(meters)转换成像素 (pixels),我们便可以在Box2D世界(world)中进行操作时使用像素 (pixels)而不用使用米(meters)来作为度量单位。

  这样将使我们在制作 Flash游戏时,使用像素来思考,从而变得更加直观。

3. 打开你在第一章中创建的demo1-1.html,并像下面那样修改它:

  

<script>function init(){var b2Vec2 = Box2D.Common.Math.b2Vec2,b2AABB = Box2D.Collision.b2AABB,b2BodyDef = Box2D.Dynamics.b2BodyDef,b2Body = Box2D.Dynamics.b2Body,b2FixtureDef = Box2D.Dynamics.b2FixtureDef,b2Fixture = Box2D.Dynamics.b2Fixture,b2World = Box2D.Dynamics.b2World,b2MassData = Box2D.Collision.Shapes.b2MassData,b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape,b2CircleShape = Box2D.Collision.Shapes.b2CircleShape,b2DebugDraw = Box2D.Dynamics.b2DebugDraw,b2MouseJointDef =  Box2D.Dynamics.Joints.b2MouseJointDef;var world;var worldScale = 30;function main(){world = new b2World(new b2Vec2(0, 9.81), true);var bodyDef = new b2BodyDef(); bodyDef.position.Set(320/worldScale,30/worldScale);setInterval(updateWorld, 1000 / 60);}function updateWorld() {world.Step(1/30,10,10);world.ClearForces(); // 清除作用力
            }main();}init();
</script>

并且,注意我是怎样创建世界和调用step方法的。这比之前少用了几行代码。

一旦你创建了刚体定义,那么是时候给它一个形状了。

创建一个圆形形状


形状(shape)是一个2D几何对象,例如一个圆形或者多边形,在这里必须是凸多边

形(每一个内角小于180度)。记住,Box2D只能处理凸多边形

现在,我们从小球开始,所以我们创建一个圆形:

var circleShape =new b2CircleShape(25/worldScale);

b2CircleShape是用来创建圆形形状,并且它的构造函数需要一个半径(radius)作为 参数。

在之前的代码中,我们创建了一个圆形,它的半径为25像素(pixels),由于设 置了worldScale变量。

从现在起,每次你想要使用像素进行操作时,你只要将它们除以 worldScale即可。你也可以定义一个方法名为pixelsToMeters的方法,在每次你需要将像 素(pixels)转换成米(meters)时调用。

当我们有了刚体定义和形状时,我们将使用夹具(fixture)来将它们粘合起来。

创建夹具


夹具(fixture)用于将形状绑定到刚体上,然后定义它的材质,设置密度 (density),摩擦系数(friction)以及恢复系数(restitution)。

此刻我们无需去 担心材质,让我们把注意力集中到夹具(fixture)上:

1.首先,我们创建夹具(fixture):


  

var fixtureDef = new b2FixtureDef();
fixtureDef.shape=circleShape;

  一旦我们通过构造函数创建了夹具(fixture),我们将分配之前创建 的形状给它的shape属性。

2.最后,我们准备将球添加到世界中:


  

var theBall =world.CreateBody(bodyDef);
theBall.CreateFixture(fixtureDef);

b2Body是刚体的实体:是物质,是通过使用bodyDef属性创建的具 体刚体。

3.再次说明一下,使用以下步骤将刚体添加到世界中:

  I 创建一个刚体定义,它将持有刚体信息,例如刚体的位置信息。

  II 创建一个形状,它将决定刚体的显示形状

  III. 创建一个夹具,将形状附加到刚体定义上。

  IV. 创建刚体在世界中的实体,使用夹具。

一旦你知道了每一步的重要性,添加刚体到你的Box2D世界中将会 很容易和有趣

回到我们的项目。现在的main函数内应该看起来和下面一样:

function main(){world = new b2World(new b2Vec2(0, 9.81), true);var bodyDef = new b2BodyDef(); bodyDef.position.Set(320/worldScale,30/worldScale);var circleShape = new b2CircleShape(25/worldScale);var fixtureDef = new b2FixtureDef();fixtureDef.shape = circleShape;fixtureDef.density = 1;fixtureDef.restitution = .6;fixtureDef.friction = .1;var theBall = world.CreateBody(bodyDef);theBall.CreateFixture(fixtureDef);setInterval(updateWorld, 1000 / 60);
}

定时保存项目并测试它。准备好看看你的第一个Box2D刚体的活动?运行影片!

额…,然而你现在运行时还是看不到任何东西。。让我告诉你原因,Box2D只负责模拟物理世界,而不负责显示任何东西。

这意味着,你的刚体正活跃在你的Box2D世界中,只是你看不到而已。

使用调试绘图测试你的模拟


幸运的是,Box2D有一个特性,调试绘图(debug draw),它将帮助你显示出模拟的情况:

在网页中首先要添加一个canvas如

<canvas id="canvas" width="640" height="480" style="" ></canvas>

1.调试绘图(debug draw)将Box2D世界中发生的事情显示出来,在

updateWorld方法中,我们可以在Step()方法之后调用世界(world)的 DrawDebugData()方法:

world.DrawDebugData();

2. 一旦我们告知世界在每次遍历之后显示调试绘图(debug draw),我们需要通 过调试绘图(debug draw)定义视觉设置。如下添加代码到你的main函数内:

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);

3.这里有很多代码,所以让我们来解释一下发生了什么。你已经知道 DrawDebugData()方法代表什么,所以我们将解释其它行代码代表的意思:

  

var debugDraw = new b2DebugDraw();

b2DebugDraw是一个类,它支持调试绘图(debug draw)出你的游戏中的物理 实体。

 var debugSprite:Sprite = new Sprite();

debugSprite被添加到显示列表(Display List),准备显示在canvas上。

debugDraw.SetSprite(debugSprite);

SetSprite()方法告知debugSprite将要被用来显示调试绘图 (debug draw)。

debugDraw.SetDrawScale(worldScale);

因为我们要将米(meters)转变为像素(pixels),我们需要通知调试绘 图(debug draw)我们使用的换算比例。 debugDraw.SetFlags(b2DebugDraw.e_shapeBit);

SetFlag()方法允许我们决定我们将在调试绘图(debug draw)中描绘的物 理实体的类型。此刻,我们只需要绘制形状。

补充说明:

setFlag()方法选择性的绘制Box2D对象的内容。这样可以节省CPU开支。setFlag()方法有一个16进制的参数,这参数的取值只能是b2DebugDraw中定义的下面几个常量

另外,我们还可以用”或”运算符,同时使用多个Flag

debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);

debugDraw.SetFillAlpha(0.5);

SetFillAlpha()方法是为了便于观看而设置的。形状的轮廓是不透明的,填 充的颜色是半透明的。这将使得调试绘图输出更加易于理解。

world.SetDebugDraw(debugDraw);

最后,我们将指派调试绘图(Debug draw)到我们刚刚创建的世界(world)

4.现在是时候来测试一下你影片了,然后你应该会看到下图所示的样子:

就这样!你设法看到了你放置在Box2D世界中的刚体。

目前,球体还无法在重力的作用下下落,但是不要担心,我们将在稍后修改它。

现在,让我们来创建一些可以作为地面的东西,例如一个放置在舞台底部边缘的大矩

形。从现在开始一切将更加简单,作为新的刚体将会很快的自动显示在它所添加的世界中。

完整源码在demo2-1.html中查看

创建矩形形状


让我执行下面的步骤:

  1.首先,刚体和夹具的定义可以重指定到我们定义的新的刚体上。这样,我 们无需再去定义bodyDef变量,但是我们要改变原先在创建球时使用的坐 标:


bodyDef.position.Set(320/worldScale,470/worldScale); 

  2.我们将用b2PolygonShape类创建一个多边形:  

var polygonShape = new b2PolygonShape(); 

  这样,我们以之前创建圆形形状时,相同的方法创建了一个多边形形状。

  3.多边形形状必须遵守一些限制,但是目前,因为我们只需要一个轴对称的矩 形,SetAsBox()方法便能满足我们的需要: 
 

polygonShape.SetAsBox(320/worldScale,10/worldScale);

  这个方法需要两个参数:矩形的半宽长和半高长。最后,我们的新多边形形状 的中心在像素(320,470),它的宽度为640像素和高度为20像素——这是我们 刚刚创建的地面的尺寸。 


  4.现在,我们改变定义的夹具的shape属性,附加新的多边形形状: 

fixtureDef.shape = polygonShape; 

  5.最后,我们可以创建刚体并将夹具附加上去,就像我们在球形上做的那样。

var theFloor = world.CreateBody(bodyDef);
theFloor.CreateFixture(fixtureDef); 

  6.你的main方法应该向下面这样:

function main(){world = new b2World(new b2Vec2(0, 9.81), true);var bodyDef = new b2BodyDef(); bodyDef.position.Set(320/worldScale,30/worldScale);var circleShape = new b2CircleShape(25/worldScale);var fixtureDef = new b2FixtureDef();fixtureDef.shape = circleShape;fixtureDef.density = 1;fixtureDef.restitution = .6;fixtureDef.friction = .1;var theBall = world.CreateBody(bodyDef);theBall.CreateFixture(fixtureDef);// 定义矩形地面bodyDef.position.Set(320/worldScale, 470/worldScale); bodyDef.type = b2Body.b2_staticBody;var polygonShape = new b2PolygonShape();polygonShape.SetAsBox(320/worldScale, 10/worldScale);fixtureDef.shape = polygonShape; // 复用夹具var theFloor = world.CreateBody(bodyDef);theFloor.CreateFixture(fixtureDef);//setup debug drawvar 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);setInterval(updateWorld, 1000 / 60);}

  7.测试影片,你将会看到地面:

  

完整源码在demo2-2.html中查看

你看是不是很简单?我们花了将近一章半去防止我们的第一个刚体,然后只花了很

少的几行代码添加另一个刚体。

不同的刚体类型——static,dynamic和 kinematic


有三种Box2D刚体的类型:staitc,dynamic和kinematic。

一个static类型的刚体不受任何力,冲量或撞击的影响并且不会移动。它只能通过 用户手动移动。默认情况下,所有的Box2D刚体都是static类型的刚体,这就是为什 么球不移动的原因。一个static类型的刚体不会和别的static或kinematic类型的刚体发 生碰撞。

一个dynamic类型的刚体受力,冲量,撞击以及任何世界事件的影响。它可以通过 手动移动,虽然我建议让它们通过世界的重力,和任何类型刚体的碰撞来移动。

一个kinematic类型的刚体是一个介于static和dynamic刚体之间的混合刚体。它不 受理的影响,但是可以通过手动和设置它们的速率来移动。它不能和static和 kinematic类型的刚体碰撞。

现在回到我们的 模拟钟来。那种类型是我们要指派给球和地面的呢?

地面是static类型的刚体,它无需移动,然而通过世界重力球要移动,所以是 dynamic类型的刚体。

你只需要设置刚体定义的type属性就能告知Box2D每一个刚体的类型,属性值可以是

b2Body.b2_staticBody, b2Body.b2_dynamicBody或b2Body.b2_kinematicBody分别对应 static,dynamic或kinematic刚体。

为球添加上bodyDef.type=b2Body.b2_dynamicBody;

地面添加上bodyDef.type=b2Body.b2_staticBody;

你的新main方法向下面这样:

function main(){world = new b2World(new b2Vec2(0, 9.81), true);var bodyDef = new b2BodyDef(); bodyDef.position.Set(320/worldScale,30/worldScale);bodyDef.type = b2Body.b2_dynamicBody;var circleShape = new b2CircleShape(25/worldScale);var fixtureDef = new b2FixtureDef();fixtureDef.shape = circleShape;var theBall = world.CreateBody(bodyDef);theBall.CreateFixture(fixtureDef);// 定义矩形地面bodyDef.position.Set(320/worldScale, 470/worldScale); // 复用定义刚体bodyDef.type = b2Body.b2_staticBody;var polygonShape = new b2PolygonShape();polygonShape.SetAsBox(320/worldScale, 10/worldScale);fixtureDef.shape = polygonShape; // 复用夹具var theFloor = world.CreateBody(bodyDef);theFloor.CreateFixture(fixtureDef);//setup debug drawvar 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);setInterval(updateWorld, 1000 / 60);
}

在恭喜你运行成功你的第一个模拟之前,让我们花点时间来说一下关于当使用调试 绘图(debug draw)时的不同颜色。

static类型的刚体将会绘制成绿色。dynamic类型的刚体,当它们没有在睡眠状态 时将会绘制成红色,在睡眠状态时将会绘制成灰色。

kinematic类型的刚体,在之 前的屏幕截图中没有显示,它将会被显示为蓝色。

现在,我们知道当刚体进入睡眠状态并节约CPU资源这个概念。正如你所见, 当球撞击地面,没有别的里影响它时,所以求可以进入睡眠状态,知道有什么 发生为止。

现在,有一个新的问题。球在落地后没有弹起。如果我们想要运行一个完美的模

拟,我们需要给我们的刚体一些更多的属性。

密度,摩擦和恢复


正如你已经知道怎样向世界添加刚体,那么我想向你介绍三种将会改变刚体行为

的属性:密度,摩擦和恢复。

密度(density)用来设置刚体的质量,按照公斤没平方米。越高的密度意味着越 重的刚体,并且该值不能为负。

摩擦(friction)在两个刚体在彼此的表面上移动时产生,它是通过一个系数来定 义的,通常它的范围在0(没有摩擦)-1(最大摩擦)之间。它不能为负数。

恢复(restitution)决定刚体在发生碰撞时反弹的程度。与密度(density)和摩擦 (friction)一样,它不能为负数并且它是一个介于0-1的系数来定义的。

一个小球 在恢复为0时落向地面,不发生反弹(无弹性碰撞),反之恢复为1时小球将会以此刻撞击时相同的速率弹起(完全弹性碰撞)。

密度(density),摩擦(friction)和恢复(restitution)必须添加到夹具上,所以在main方法中添加以下几行代码:


fixtureDef.density=1;

fixtureDef.restitution=0.6;

fixtureDef.friction=0.1;

在你的main函数内看起来应该这样

function main(){world = new b2World(new b2Vec2(0, 9.81), true);var bodyDef = new b2BodyDef(); bodyDef.position.Set(320/worldScale,30/worldScale);bodyDef.type = b2Body.b2_dynamicBody;var circleShape = new b2CircleShape(25/worldScale);var fixtureDef = new b2FixtureDef();fixtureDef.shape = circleShape;fixtureDef.density = 1;fixtureDef.restitution = .6;fixtureDef.friction = .1;var theBall = world.CreateBody(bodyDef);theBall.CreateFixture(fixtureDef);// 定义矩形地面bodyDef.position.Set(320/worldScale, 470/worldScale); // 复用定义刚体bodyDef.type = b2Body.b2_staticBody;var polygonShape = new b2PolygonShape();polygonShape.SetAsBox(320/worldScale, 10/worldScale);fixtureDef.shape = polygonShape; // 复用夹具var theFloor = world.CreateBody(bodyDef);theFloor.CreateFixture(fixtureDef);//setup debug drawvar 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);setInterval(updateWorld, 1000 / 60);}

我向夹具指派一次属性,而所有的刚体都将使用这个相同的夹具。在本书的整个

讲解过程中,我们将要处理很多夹具的属性,但是目前让我们只需要设置小球弹跳即可。

测试demo2-3.html,你就会发现小球在弹跳

祝贺你!你刚刚完成了你的第一个真实的Box2D项目,那么现在你有能力去创建 基础的形状和为它们分配特性和属性。

接下去让我开始来创建一个准游戏吧…


注:转载请注明出处博客园:sheldon-二狗-偷饭猫(willian12345@126.com)

https://github.com/willian12345

转载于:https://www.cnblogs.com/willian/p/5478539.html

HTML5之2D物理引擎 Box2D for javascript Games 系列 第二部分相关推荐

  1. 关于2d物理引擎box2d与ape的评论

    APE不行,做点简单的撞球,台球之类游戏还行.我最开始用他实验性做了个简单的基于物理引擎的泡泡龙类游戏,发现稍微多一点的几何体堆叠在一起就会产生渗透现象,没办法只好更改最初的设计.要专注做物理游戏,还 ...

  2. VUE,使用物理引擎Box2D设计类愤怒小鸟的击球游戏--基本架构设置

    我们都玩过愤怒的小鸟,该游戏一大特点是,两物体碰撞后,它会模拟现实世界物体碰撞后的反弹效果,因此游戏特别具有体感和逼真感,本节我们利用物理引擎Box2D,制作一个类似愤怒小鸟类型的碰撞游戏. 游戏的基 ...

  3. cocos2d-x物理引擎-Box2D介绍及开发实例

    物理引擎 Cocos2d-x引擎内置了两种物理引擎,它们分别是Box2D和Chipmunk,都是非常优秀的2D物理引擎,而且x引擎将它们都内置在SDK中.Box2D使用较为广泛,在这里选择Box2D来 ...

  4. cocos2dx-3.x物理引擎Box2D介绍

    原文:https://www.cnblogs.com/yyxt/p/4561410.html 理引擎 Cocos2d-x引擎内置了两种物理引擎,它们分别是Box2D和Chipmunk,都是非常优秀的2 ...

  5. APE 2D物理引擎使用

    先去http://www.cove.org/ape/index.htm 下载作者的最新版APE 然后打开说明文档 docs\api\index.html 这里一共十二个类,看起来很少,所以初学者可以更 ...

  6. 制作简单的2D物理引擎(零)

    最近发现了Github上的开源物理引擎项目Matter.js,对它很感兴趣,发现源码并不算长,算上注释大约1万行左右,值得剖析一番.Matter.js实现一个最小化的2D物理引擎,性能不错,故打算用C ...

  7. python 物理引擎 摩擦力_参赛作品2-phenom的2D物理引擎

    全球图形学领域教育的领先者.自研引擎的倡导者.底层技术研究领域的技术公开者,东汉书院在致力于使得更多人群具备内核级竞争力的道路上,将带给小伙伴们更多的公开技术教学和视频,感谢一路以来有你的支持.我们正 ...

  8. 2D物理引擎--谁碰了我的奶酪

    人生犹如"迷宫",每个人都在其中寻找各自的"奶酪"--稳定的工作.身心的健康.和谐的人际关系.甜蜜美满的爱情,或是令人充满想象的财富-- 那么,你是否正在享受你 ...

  9. 深入理解javascript函数系列第二篇——函数参数

    前面的话 javascript函数的参数与大多数其他语言的函数的参数有所不同.函数不介意传递进来多少个参数,也不在乎传进来的参数是什么数据类型,甚至可以不传参数.本文是深入理解javascript函数 ...

  10. cocos2d-x初探学习笔记(20)--物理引擎box2d(2)

    小满(bill man)个人原创,欢迎转载,转载请注明地址,小满(bill man)的专栏地址http://blog.csdn.net/bill_man 由于box2d的内容比较多,它也有自己的tes ...

最新文章

  1. ActionBar通过Tab进行不同的Fragment之间的交换
  2. Go语言 defer
  3. 密码学笔记——eval(function(p,a,c,k,e,d) 加密破解
  4. 常见错误 不能打开注册表关键字
  5. 内存的工作原里(二)
  6. 牛逼!Intellij IDEA竟然有个功能可自动生成代码,你用过没?
  7. centos上TURN服务器搭建及测试
  8. JS生成uuid(唯一标识符)。
  9. 小米手机解锁bootload教程及常见问题
  10. SD敢达服务器源码,《sd高达wars》金手指代码 SD高达G世纪WARS金手指
  11. Tips:PowerDesigner16.5 图表显示Code以及 Columns新增Commet显示
  12. Latex排版学习笔记(1)——希腊字母表及其在latex中的表示
  13. echarts动态渲染柱状图背景颜色以及顶部数值
  14. 【博客567】http/2 goaway frame 与 grpc graceful restart
  15. 南都周刊:别了,老兵乔布斯
  16. Bilibili Helper - 哔哩哔哩弹幕网辅助扩展插件
  17. php libev 主动发送,php的libev扩展
  18. ZMQ/ZeroMQ简介
  19. c++ 复合结构的基本用法
  20. 802.11kvr无线漫游原理及漫游测试工具推荐

热门文章

  1. 机器学习系列(九)【最大熵模型】
  2. 人脸识别 Face Recognition安装使用
  3. cyyz: Day 6 平衡树整理
  4. Largest Submatrix SPOJ - MINSUB (单调栈)
  5. css table 合并单元格
  6. AtCoder Grand Contest 016
  7. .net mvc 获取项目文件夹路径
  8. make:cc 命令未找到的解决方法
  9. iOS 点击tabbarItem的时候根据登录状态判断加载哪个视图控制器
  10. 如何用最简单的方法将PCweb改成适合各种设备的web