cocos2d-x Box2D物理引擎

Box2d中概念:

 

1. 刚体

一块十分坚硬的物质,它上面的任何两点之间的距离都是完全不变的,我们用物体代替刚体。

  即是物理学中的质点,只有位置,没有大小,它又可以区分为以下几类

  静态刚体:静态刚体没有质量,没有速度,只可以手动来改变他的位置

  棱柱刚体:棱柱刚体没有质量,但是可以有速度,可以自己更新位置

  动态刚体:动态刚体有质量也有速度

 

物理引擎需要首先定义一个描述类,然后再根据描述类通过世界创建某个对象。创建刚体时需要有两个步骤,一是生成一个刚体定义,二是根据刚体定义生成刚体。在刚体创建时定义中的信息会被复制,也就是说创建完成后刚体只要没被释放掉,就还可以重复使用。

 

//定义刚体

b2BodyDef groundBodyDef;

groundBodyDef.position.Set(screenSize.width/2/PTM_RATIO, screenSize.height/2/PTM_RATIO);

//生成刚体

b2Body* groundBody = world->CreateBody(&groundBodyDef);

 

 

2. 形状

一块严格依附于物体的2D碰撞几何结构,形状具有摩擦和恢复等材料特性

  通过关联添加到刚体上,碰撞可以根据形状来判定。

b2PolygonShape groundBox;

// bottom

groundBox.SetAsBox(screenSize.width/2/PTM_RATIO, 0, b2Vec2(0, -screenSize.height/2/PTM_RATIO), 0);

3.关联

 关联是一种附加在刚体上的属性,一个刚体可以有多个关联,创建关联时,需要定义关联的信息,然后通过刚体创建关联,当关联被创建时关联定义中的信息也会被保留,也可以重用

//定义并创建关联

b2FixtureDef fixtureDef;

fixtureDef.shape = &dynamicBox;

fixtureDef.density = 1.0f;

fixtureDef.friction = 0.3f;

body->CreateFixture(&fixtureDef);

4.链接

链接可以联系多个刚体,使得刚体之间相互影响,同样的,也需要首先定义信息,之后通过世界创建链接,同样的,信息也可以被保留,从而重用

另外链接还支持限制和马达,限制就是限制物体运动的角度,马达就是依照关节中的限制来约束物体

链接有旋转,棱柱和距离等

b2RevoluteJointDef rjd;

rjd.Initialize(m_attachment, m_platform, b2Vec2(0.0f, 5.0f));

rjd.maxMotorTorque = 50.0f;

rjd.enableMotor = true;

m_world->CreateJoint(&rjd);

5.约束

  一个约束就是消除物体自由度的物理连接。在 2D 中,一个物体有 3 个自由度。如果我

们把一个物体钉在墙上(像摆锤那样),那我们就把它约束到了墙上。这样,此物体就只能绕着这个钉子旋转,所以这个约束消除了它 2 个自由度。

还有一种不须你创建的接触约束,一个防止刚体穿透,以及用于模拟摩擦和恢复的特殊约束。

6.世界

一个物理世界就是物体,形状和约束相互作用的集合,

  世界是遵循物理的空间,以上的所有都存在于世界中,可以创建多个世界,但很少这样用

创建世界需要两个步骤,一是生成重力向量,二是根据重力生成世界对象

//生成重力向量

b2Vec2 gravity;

gravity.Set(0.0f, -10.0f);

//生成世界对象

bool doSleep = true;

world = new b2World(gravity);

world->SetAllowSleeping(doSleep);

world->SetContinuousPhysics(true);

 

 

 

7.关节

它是一种用于把两个或多个物体固定到一起的约束,Box2D支持的关节类型有:旋转,棱柱,距离等,关节可以支持限制和马达。

 

 

 

 

 

 

//=========================================================

Box2D由三个模块组成:公用(Common),碰撞(Collision)以及动力学(Dynamics)。公用模块包括内存分配,数学库,设置。碰撞模块定义了形状,broad-phase算法,碰撞的功能/查询。最后动力学模块提供了模拟物理世界,物体,定制器(fixtures),以及连接器。

 

 

Box2d上有两个和栈有关的类

分别是b2StackAllocatorb2GrowableStack

B2StackAllocator主要是为了运行一个步长时满足box2d需要的临时内存空间,作为栈分配器来防止单步堆分配。

B2GrowableStack主要是为了满足动态树b2DynaicTree中光线投射和区域查询所需要的临时内存空间,这个到动态树的时候再做详细研究。

 

 

 

学习碰撞模块(collision)部分,该部分主要有一下内容:

1 形状,该部分包括的定义、实现。

2 操作形状方法,该部分包括距离(Distance)、撞击时间(Time of Impact)等。

3 算法辅助类,包括动态树和broad-phase算法。用来提高系统的碰撞检测的速度。

 

 

其中前两项均以算法辅助类为基础的,所以我们开始学习这一项,今天我们将学习动态树部分,动态树顾名思义就是会动的树,在Bx2d中动态树是由b2DynamicTree类来实现的,它主要是通过用户数据来操作轴对齐包围盒(axis-alignedbounding boxes,AABBs)来完成树的各种操作,并不需要知道形状是什么东东。(如有童鞋像我开始一样,不知道AABB是何物,请记得我们还有维基百科啦)。同时动态树继承自AABB树,树上的每一个节点都有两个孩子。叶节点是一个单独的用户AABB。即便是惰性输入,整个数也可以使用旋转保持平衡。

 

 

 

 

 

 

 

 

 

 

//=========================================================

 

定制器(Fixtures)

 

定制器用来描述场景中物体(body)的大小,形状,材质属性等。一个物体可以附加多个定制器,物体的质心会被附加定制器的顺序所影响。当两个物体相撞时,它们的定制器会决定它们的变化。定制器的主要属性如下:

·        形状 -多边形或圆弧

·        恢复 -定制器的弹力

·        摩擦 -光滑程度

·        密度 -物体大小的重量

 

//=========================================================

 

Box2D使用一个称为积分器(integrator)的算法。在离散的时间点上积分器可以模拟物理方程。例如我们在屏幕上进行翻书,那么Box2D会随着传统游戏的循环一起执行。所以我们需要为Box2D挑一个时间步长。通常游戏中的物理引擎时间步长至少是60H或者1/60秒。你可以设置更长的时间步长,但是你不得不更加小心的建设你的物理世界。我们也不喜欢时间步长改变的太大。一个变化的时间步长会产生很难调试的变化的结果。所以不要把时间步长绑定到你的游戏框架的帧率上(除非你真的,真的需要这么做)。

事不宜迟,这就是所说的时间步长。

1

float32 timeStep = 1.0f/60.0f;

 

除了积分器以外,Box2D还使用了大量的位代码来调用约束求解器(constraint solver)。约束求解器每次解决一个约束,解决了模拟过程中所有的约束问题。单个约束可以被完美的解决。尽管如此,每当我们解决了一个约束,我们又轻易的扰乱了其他约束。为了解决所有问题,我们需要多次遍历所有约束。

在约束求解器(constraint solver)中有两个阶段:速度阶段和位置阶段。在速度阶段,求解器为使物体能够正确的移动需要计算必要的冲量。在位置阶段,求解器调整物体的位置来降低物体之间的重叠和连接的分离程度(译者注:比如两个单独的物体碰撞完之后会有瞬间重叠的情况,还有多节点物体在碰撞完之后会有瞬间的分离的情况,所以会有重叠和分离两种情况)。每一个阶段都有其自身需要的迭代次数。另外,在位置阶段如果有误差足够小,可能会提早退出迭代。

Box2D建议的迭代次数是速度阶段8次,位置阶段3次。你可以根据你的喜好改变这个数字,但是要记住,这需要在效率和精度上做一个权衡。用更少的迭代次数会增加提升效率,但是会影响精度。同样,用更多的迭代次数会降低效率但会提升模拟质量,会产生更高的精度。对于这个简单的例子而言,我们不需要太多的迭代次数,下面是我们选择的迭代次数。

1

2

int32 velocityIterations = 6;

int32 positionIterations = 2;

注意时间步长和迭代次数完全不相关。一次迭代不是一个分步长。一次求解迭代是在一个时间步长单次遍历所有约束。你也可以在单个时间步长内多次遍历约束。

我们现在准备开始模拟循环。在你的游戏里模拟循环可以被合并到游戏循环当中。每当遍历你的游戏循环,你可以调用b2World::Step方法。每次只调用一次就足够了,当然这取决于你的游戏帧率和你的物理时间步长。

这里对Hello World项目进行了简单的设计,所以没有图形输出。代码打印出动态盒子的位置信息和旋转信息。这里模拟了一秒钟内60个时间步长的循环。

1

2

3

4

5

6

7

for(int32 i = 0; i < 60; ++i)

{

    world.Step(timeStep, velocityIterations, positionIterations);

    b2Vec2  position = body->GetPosition();

    float32  angle = body->GetAngle();

    printf(“%4.2f %4.2f %4.2f\n”, position.x, position.y,  angle);

}

 

 

//=========================================================

动力学模块

动力学模块是Box2D最复杂的部分,同样可能是与你进行交互最多的部分。动力学模块的地位在公共模块和碰撞模块之上,所以你现在就应该对它们有所熟悉。

动力学模块包括:

· 形状定制器类 (shape fixture class)

· 刚体类(rigid body class)

· 接触类(contact class)

· 各种连接器类(joint classes)

· 世界类(world class)

· 各种监听器类(listener classes)

 

 

 

//=========================================================

 

 

物体

物体有位置和速度。你可以将力,扭矩和冲量作用于物体之上。物体可以是静态物体(static),运动学物体(kinematic)或者动态物体(dynamic)。下面是物体类型的定义:

 

b2_staticBody

 

在模拟环境下静态物体是不会移动的,就好像有无限大的质量。在Box2D的内部会将质量至反,存储为零。静态物体可以被用户手动移动。静态物体有零速度。静态物体不能和其它静态或运动学物体进行碰撞。

 

b2_kinematicBody

 

运动学物体在模拟环境中根据自身的速度进行移动。运动学物体自身不受力的作用。虽然用户可以手动移动它,但是通常情况下我们会设置它的速度来进行移动。运动学物体的行为就像是有无限大的质量,尽管如此,在Box2D内部还是会对运动学物体的质量至反设置为零。运动学物体不能和其它静态或运动学物体进行碰撞。

 

 

b2_dynamicBody

 

动态物体可以进行全模拟。它们可以被用户手动移动,但是通常情况下会根据受力进行移动。动态物体可以和任何物体发生碰撞。动态物体总是拥有有限的非零质量。如果你尝试设置动态物体的质量为零,它会自动设置一个1千克质量的物体。

 

 

 

//=========================================================

 

世界(b2World):世界就是一个环境,所有物理运算都在这个里面进行。 
形状定义(b2ShapeDef):形状定义是什么?说简单点形状定义就是定义你这个对象的样子,它用来做什么?就是用来确定你的碰撞。 
刚体定义(b2BodyDef):刚体定义就是设定刚体的初始具体,在目前来说,最大的功能就是把你定义好的形状加到你想到的刚体上。 
刚体(b2Body):刚体就是物理引擎里面的东西(对象),它可以受力的作用进行当前位置的变化旋转等。你要在世界中使用的所有物体目前来说都是刚体。 

 

 

 

数据类型 
1、 b2Vec2 
就像在3D中Vector3类的使用一样,b2Vec2在Box2D中也应用广泛,你几乎在每个时刻都用到它,比如说定义坐标位置,定义Box大小等 

b2Vec2是由float32类型的x,y组成,支持负向量,+=,-=,*=操作符, 
支持的方法有 
Void SetZero();设置x,y为0 
Void Set(float32 x_, float32 y_);设置x,y为指定值 
b2Vec2 Make(float32 x_, float32 y_),生成一个值指定的b2Vec2 
float32 Length()取得向量的长度或模 
float32 Normalize()标准化向量 
bool IsValid()检查是否有效 
如果在这里你并不了解我在这里所提到的一些数学概念,比如说标准化向量,模,可以参照b2Math.h文件,或者直接找本数学书来看。 


2、b2Mat22 
HelloWorld教程中,b2Mat22虽然没有被使用到,我们这里先提出这个概念,以便于在下一节中讨论。 
其实b2Mat22一个由两个b2Vec2组成的2*2方阵,你可以直接由两个b2Vec2(col1、col2)构造或者由一个角度值构造。 
他的主要方法有: 
void Set(const b2Vec2& c1, const b2Vec2& c2) 
void Set(float32 angle), 
提供两种方式赋值方法 
void SetIdentity(),设定恒等式 
void SetZero(),把col1、col2的x,y都清为0 
b2Mat22 Invert(),转换相关数据 
b2Vec2 Solve(const b2Vec2& b),解决A * x = b 

3、b2AABB 

b2AABB就是一个盒子,是由两个向量组成,一个为minVertex是最小顶点,另一个为maxVertex是最大顶点,通过这两个顶点来表示最为普通的AABB框。 

4、b2ShapeDef 
b2ShapeDef直翻为形状定义,它用一个b2ShapeType型量type来表示形状类型,用函数指针来表示用户数据(userdata),用一个b2Vec2向量localPosition来表示当前位置,用float32的localRotation来表示当前角度,用float32的friction、density、restitution来表示摩擦力、密度、弹性系数,用uint16的categoryBits和maskBits来表示碰撞位及位标识(可以用来过滤一些碰撞),用int16的groupIndex来表示组号,这个组号可以用来过滤还比位标识优先。 

相关常量 

view plaincopy to clipboardprint?

1. enum b2ShapeType   

2. {   

3. e_unknownShape = -1,   

4. e_circleShape,   

5. e_boxShape,   

6. e_polyShape,   

7. e_meshShape,   

8. e_shapeTypeCount,   

9. };   


相关形状定义 
b2CircleDef继承于b2ShapeDef,type 为 e_circleShape,另外带有一个类型为float32的量radius来表示半径值。 
b2BoxDef继承于b2ShapeDef,type 为 e_ boxShape,另外带有一个类型为b2Vec2的量extents来表示区域值。 
b2PolyDef继承于b2ShapeDef,type 为 e_ polyShape,另外带有一个类型为b2Vec2的数组vertices来表示顶点,并带有一个int32型的量vertexCount来表示顶点数,目前顶点数最多支持8个。 
5、b2BodyDef 
b2BodyDef是刚体定义结构,由一个函数指针userData来表示用户数据,一组类型为b2ShapeDef*指针数组shapes来表示形状队列,目前形状数最大支持64个,用一个b2Vec2向量position来表示当前位置,用类型为float32的量rotation来表示当前角度,用类型为b2Vec2的量linearVelocity表示线速度,用类型为float32的angularVelocity来表示角速度,用类型为float32的量linearDamping来表示线性阻尼,用类型为float32的量angularDamping来表示角阻抗,用类型为bool的allowSleep 来表示是否可以允许休眠,用一个类型为bool的isSleeping来表示是否正在休眠,用一个类型为bool的量preventRotation来表示是否防止旋转,支持方法: 
AddShape(b2ShapeDef* shape)。 

 

 

 

 

 

 

四、 CollisionFiltering、 
碰撞过滤是用来防止形状与形状之间进行碰撞的,就像上文所示,它可以用碰撞种类和组名来区别,Box2D总共提供16种碰撞种类,每个形状都可以提定属于什么种类,那么它就可以和其他不同种类的形状碰撞,如果在一个多人在线游戏中,你想你的玩家在他们之间不进行碰撞,怪物和怪物之间不进行碰撞,但人和怪物进行碰撞,你可以使用 

view plaincopy to clipboardprint?

1. playerShapeDef.categoryBits = 0x0002;   

2.      monsterShapeDef.categoryBits = 0x0004;   

3.      playerShape.maskBits = 0x0004;   

4.      monsterShapeDef.maskBits = 0x0002;   


碰撞组索引是一个可以大量指定物体碰撞规则的东西,你可以通过它来指定成百上千的物体,当碰撞组索引为负数时,东西之间不碰撞,当为正数时进行碰撞,而且碰撞组索引的优先级比碰撞种类要高。 

view plaincopy to clipboardprint?

1. shape1Def.groupIndex = 2;   

2.      shape2Def.groupIndex = 2;   

3.      shape3Def.groupIndex = -8;   

4.      shape4Def.groupIndex = -8;   


形状1和2就碰撞,因为组索引大于0,而3和4不碰撞,因为小于0 
可以参考例子代码来确定你的碰撞方法.

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值