cocos2d-x box2d物理引擎深入研究 第一篇之旋转关节详解(b2RevoluteJoint)

Posted QQ51931373

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了cocos2d-x box2d物理引擎深入研究 第一篇之旋转关节详解(b2RevoluteJoint)相关的知识,希望对你有一定的参考价值。

对于旋转关节场常见的包括如下:

  • 滚轮或滚筒
  • 链条或悬桥(使用多个旋转连接器)
  • 破布娃娃的关节
  • 转门,弹射器,杠杆

创建旋转关节

创建旋转关节首先设置b2RevoluteJointDef属性,然后用世界对象创建之.

然后我们看一堆关于旋转关节的属性。

  • localAnchorA - 基于刚体A的本地坐标系,在刚体A上的点,刚体A围绕这一点进行旋转.
  • localAnchorB - 基于刚体B的本地坐标系,在刚体B上的点,刚体B围绕这一点进行旋转.
  • eferenceAngle - 刚体B减去刚体B的弧度值.Box2D中角度用弧度表示.
  • enableLimit - 关节的限制是否开启
  • lowerAngle - 角度的最低限制
  • upperAngle - 角度的最高限制
  • enableMotor -关节马达是否开启
  • motorSpeed - 关节马达的目标速度(线性速度)
  • maxMotorTorque - 马达允许使用的最大扭矩值.扭矩就是力矩.力矩 = 力*力臂.

下面就用图形化来详细解释这些属性对旋转关节(RevoluteJoint)的作用.

1.localAnchorA  和 localAnchorB.

    这两个属性实际都是坐标点.而这两个坐标点都是基于刚体A和刚体B的自身本地坐标系而言的.比如:刚体A是一个宽度和高度都是4的正方形.若localAnchorA是b2Vec2(2,2).则localAnchorA这个点就在刚体A的最右上角.因为刚体A的原点(origin point)是在刚体A的正中心(刚体A的锚点默认是(0.5,0.5)),所以b2Vec2(2,2)就自然是在正方形的最右上角.

见下图:

见上图:假设正方形的宽高是2,原点是在正中心.则localAnchorA(1,1)就在其右上角. localAnchorB(0,0)就是圆的正中心.这样就相当于把正方形的右上角和圆的中心重合,并用一个旋转关节(RevoluteJoint)进行固定.就像一颗钉子一样.

 

2.eferenceAngle 参考角度.

参考角度实际上就是刚体B减去刚体A的角度.如果这个值为0,就是表示两个刚体的角度差为0,参考角度实际上就是关节的角度,在程序中可以通过GetJointAngle()来查看关节的旋转程度。当你使用关节限制(joint limits)的时候,参考角度还是有用的。

看下图:

在上图中的左边图,如果参考角度为0, 则在B逆时针旋转45角后(右边图),通过GetJointAngle()返回关节角度的值为45.即在参考角度上加45.

在上图中的左边图,如果参考角度为123,则在B逆时针旋转45角后(右边图),通过GetJointAngle()返回关节角度的值为168.即在参考角度上加45.

注意一下,bodyB的旋转可以作为关节的角度值,并且相对于bodyA是逆时针方向。如果两个物体同时被移动或旋转,关节角度是不会改变的,因为它代表的是两个物体的相对角度(Angle_B - Angle_A)。

 

3.旋转关节限制(revolute joint limits)

  

根据目前为止我们所讲解的属性来看,关节中的两个物体可以无限制的围绕锚点旋转,但是旋转关节也可以对旋转范围作出限制。可以对旋转的上限和下限作出设定,可以在’关节角度(joint angle)’方面作出设定。

假设你想对上面的例子在初始设定角度的时候范围限定在45度内

 

当使用关节限制的时候有些东西需要牢记…

  • 对于enableLimits属性的设置会同时影响两个限制,所以如果你只想对其中一个做限制,需要把限制设置成一个非常高(例如,upper limit)或者非常低的值(例如,lower limit),以保证实际使用过程中永远达不到此值。
  • 旋转关节限制的设置可以超过一个全旋转,比如说把一对lower/upper限制设置为-360,360,可以允许物体之间的相对旋转为两圈以内。
  • 可以很方便的把限制设定为相同值,可以把连接器’夹’在给定的角度上。这个角度可以逐步的改变直到最后达到合适的位置,并保持其与另一个物体的之前的状态,并且不需要连接器马达(joint motor)。
  • 在有限的时间步长内,快速的旋转会穿过设定的限制,直到旋转速度被修正。
  • 检查当前是否达到限制非常简单:
bool atLowerLimit = joint->GetJointAngle() <= joint->GetLowerLimit();
bool atUpperLimit = joint->GetJointAngle() >= joint->GetUpperLimit();
 


 

一个关节限制强制关节角度保持在lower和upper之间。限制范围应该包含0,否则关节将在模拟开始的时候发生倾斜.

 

4.旋转连接器马达revolute joint motor

旋转关节默认的行为是没有阻力的。如果你想控制物体运动让其旋转,需要对其施加力矩或转动惯量。也可以设置能够让关节以某个特定的线速度旋转物体的关节’马达’。如果你想模拟具有动力效果的事物,比如汽车轮子或吊桥类型的门,这个特性还是很有用的。

线速度仅仅是一个目标速度,这也就意味着关节并不能保证达到这个速度。通过为关节马达指定最大可允许的扭矩,你能够控制关节最终达到目标速度的快慢程度,甚至在有些例子中决定了最终是否能够达到目标速度.下面一段引文解释了joint motor的最大扭矩.英文很简单我就不翻译了。

A joint motor allows you to specify the joint speed (the time derivative of the angle). The speed can be
negative or positive. A motor can have infinite force, but this is usually not desirable. Recall the eternal
question:
"What happens when an irresistible force meets an immovable object?"
I can tell you it's not pretty. So you can provide a maximum torque for the joint motor. The joint motor
will maintain the specified speed unless the required torque exceeds the specified maximum. When the
maximum torque is exceeded, the joint will slow down and can even reverse.

使用关节马达的时候需要注意一些事情…

  • 为最大力矩设置一个小值,关节会花一些时间到达期望的速度。如果你增加关节所连接的物体,让其更重一点儿的话,如果你还想让物体保持同样的加速度,你需要增加最大扭矩值。
  • 可以设置马达速度为零,让关节保持静止。为最大扭矩设置一个较低的值,起到刹车的作用,逐渐关节慢下来。使用高最大扭矩值可以让关节迅速停下来,需要一个很大的力移动关节,有点类似生锈的轮子。
  • 驱动汽车或者说是车辆的轮子可以通过直接改变马达的速度,当汽车停止的时候,通常会把目标速度设置为零

下面以一个实例来进行说明.

首先是初始化:

		//设置重力
		b2Vec2 gravity;
		gravity.Set(0.0f, 0.0f);
		m_world = new b2World(gravity);
		//设置是否sleep
		m_world->SetAllowSleeping(true);
		m_world->SetContinuousPhysics(true);

		//创建GLESDebugDraw对象.在DEBUG调试环境下对刚体轮廓和关节等信息
		//进行绘制。它是在b2World::Step中被调用.
		m_DebugDraw = new GLESDebugDraw(32.0f);
		m_world->SetDebugDraw(m_DebugDraw);
		uint32 flags = 0;
		//e_shapeBit:刚体的轮廓被绘制出来
		flags += b2Draw::e_shapeBit;
		m_DebugDraw->SetFlags(flags);


然后创建正方形和圆形的刚体

		b2BodyDef bodyDef;
		bodyDef.type = b2_dynamicBody;
 		b2FixtureDef fixtureDef;
		fixtureDef.density = 1;
		fixtureDef.filter.groupIndex = int16(1);
		fixtureDef.filter.categoryBits = uint16(2);
		fixtureDef.filter.maskBits = uint16(12);
 		b2PolygonShape boxShape;
 		boxShape.SetAsBox(2,2);
		CCSize size = CCDirector::sharedDirector()->getWinSize();
		bodyDef.position.Set(size.width/2/32, size.height/2/32);
		fixtureDef.shape = &boxShape;
		m_bodyA = m_world->CreateBody( &bodyDef );
		m_bodyA->CreateFixture( &fixtureDef );

		b2BodyDef bodyDefEx;
		bodyDefEx.type = b2_dynamicBody;
		bodyDefEx.position.Set(size.width/2/32, size.height/2/32);
		b2FixtureDef fixtureDedEx;
		fixtureDedEx.density = 1;
		fixtureDedEx.filter.groupIndex = int16(1);
		fixtureDedEx.filter.categoryBits = uint16(4);
		fixtureDedEx.filter.maskBits = uint16(12);
		b2CircleShape boxShapeEx;
		boxShapeEx.m_radius = 2;	
		fixtureDedEx.shape = &boxShapeEx;
		m_bodyB = m_world->CreateBody(&bodyDefEx);
		m_bodyB->CreateFixture(&fixtureDedEx);


最后创建关节,把两个刚体连接起来.

		b2RevoluteJointDef revoluteJointDef;
		revoluteJointDef.bodyA = m_bodyA;
		revoluteJointDef.bodyB = m_bodyB;
		revoluteJointDef.collideConnected = false;
		revoluteJointDef.localAnchorA.Set(2,2);
		revoluteJointDef.localAnchorB.Set(0,0);
		revoluteJointDef.motorSpeed = 100;
		revoluteJointDef.maxMotorTorque = 100;
		revoluteJointDef.enableMotor = true;
		revoluteJointDef.referenceAngle = 0;
 		revoluteJointDef.enableLimit = true;
 		revoluteJointDef.lowerAngle =-45  ;
 		revoluteJointDef.upperAngle =  45;
	   m_joint =  (b2RevoluteJoint*)m_world->CreateJoint( &revoluteJointDef );


刚体A顺时针旋转。刚体B逆时针旋转.

没看到哪里可以上传附件的地方。郁闷。否则把源代码传上去 。

以上是关于cocos2d-x box2d物理引擎深入研究 第一篇之旋转关节详解(b2RevoluteJoint)的主要内容,如果未能解决你的问题,请参考以下文章

实例介绍Cocos2d-x中Box2D物理引擎:碰撞检測

cocos2d-x之Box2d初试

Cocos2d-x 3.1.1 学习日志13--物理引擎登峰造极之路

Cocos2d-x旧引擎文件夹结构

Cocos2d-x 3.0 简捷的物理引擎

PhysX 物理引擎研究源码编译