Oh!Coder

Coding Life

Box2D C++ 教程-连接器-平移

| Comments

声明:本文翻译自Box2D C++ tutorial-Joints-prismatic,仅供学习参考。

平移连接器(Prismatic joints)

平移连接器可能更多的俗称为滑动连接器。连接器上的两个物体相对于彼此来说保持固定的旋转,它们只能够沿着特定的轴线一起移动。平移连接器可以进行限定,保证其只能沿着某个轴在一定范围内进行移动。还可以设定此连接器的马达,连接的物体会以给定的力矩,以相应的速度进行移动。平移连接器经常使用的场景为:

  • 电梯
  • 移动平台
  • 滑动门
  • 活塞

创建平移连接器

平移连接器第一次创建的时候需要先定义b2PrismaticJointDef并进行设置,然后作为参数传入CreateJoint方法,此方法返回b2PrismaticJoint对象。我们已经在连接器-概述中看到了定义连接器所需要设置的一些共同的属性-连接器中的两个物体,以及它们之间是否会发生碰撞。下面先对这些参数进行设置:

1
2
3
4
b2PrismaticJointDef prismaticJointDef;
prismaticJointDef.bodyA = bodyA;
prismaticJointDef.bodyB = bodyB;
prismaticJointDef.collideConnected = false;

下面我们会碰到一堆详细设置平移连接器的属性。

  • localAxis1* - 沿轴(线)的移动(相对于bodyA)
  • referenceAngle - 连接器上物体之间被强制作用的角度
  • localAnchorA - body A所在轴线上的一点
  • localANchorB - body B所在轴线上的一点
  • enableLimit - 连接器限制的控制开关
  • lowerTranslation - 连接器位置的下限
  • upperTranslation - 连接器位置的上限
  • enableMotor - 连接器马达的控制开关
  • maxMotorForce - 连接器允许使用的最大马力

* Box2D v2.2.0中改为localAxisA

让我们更加详细的看看可以做些什么。

作为一个设置平移连接器的例子,我们会做一个简单的叉车。这里会用到连接器限制和马达。下面是我们要用的物体-一个大盒子作为叉车的外壳以及一个小盒子作为起重滑杆(译者注:我理解的应该是叉车前面铲东西的时候上下移动的滑杆吧)。既然前面很多例子我们做过很多完成的例子,所以这里我就不把所有的代码都列出来了,但是这里还是以一个前面的话题中所提到的’围栏’作为基本的场景,防止物体飞出场景。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//body and fixture defs - the common parts
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
b2FixtureDef fixtureDef;
fixtureDef.density = 1;

//two boxes
b2PolygonShape squareShapeA;
squareShapeA.SetAsBox(5,3);
b2PolygonShape squareShapeB;
squareShapeB.SetAsBox(1,4);

//large box a little to the left
bodyDef.position.Set(-10, 10);
fixtureDef.shape = &squareShapeA;
m_bodyA = m_world->CreateBody( &bodyDef );
m_bodyA->CreateFixture( &fixtureDef );

//smaller box a little to the right
bodyDef.position.Set( -4, 10);
fixtureDef.shape = &squareShapeB;
m_bodyB = m_world->CreateBody( &bodyDef );
m_bodyB->CreateFixture( &fixtureDef );

pic

连接器轴线(Joint axis)

连接器轴线可以让物体沿着这条线彼此产生相对移动。它限定在bodyA的本地坐标系,你可以认为bodyB可以沿着bodyA的直线视角的方向进行移动。比如说,如下所示bodyA有一个梯形定制器,bodyB有一个四方形定制器,你向让bodyB沿着梯形所’指向’的方向滑动…

pic

…或许你需要一个坐标为(0,1)的本地轴线。那么代码看起来像这个样子:

1
prismaticJointDef.localAxis1.Set(0,1);

*Box2D v2.2.0版本中改为了localAxisA方法

这也就意味着bodyB将会沿着bodyA上的轴线进行滑动,并作为bodyA在世界中移动,例如:

pic

注意轴线其实并没有在物体自身上有特别相关的点,只不过为物体的滑动指定了方向而已。这也是为什么在上图中我故意把虚线画到了物体的定制器以外:) 指定轴线应该是一个单位向量,所以如果你曾经用超过1的向量进行初始化,那么你应该先对向量进行标准化:

1
prismaticJointDef.localAxis1.Normalize();

还要注意一下,既然只指定了滑动的方向,那么负值很显然指的就是反方向。例如,上面例子中我们还可以使用(0,-1)。对于设置连接器的限制和马达这一点变的很重要。

这里我们的叉车连接器轴线设置为(0,1), 以保证铲起物体时的滑动方向是正方向。

本地锚点(Local anchors)

现在我们确认了两个物体各自移动的方向,我们可以在各自物体上设定沿着轴线移动的特定点。这些点是以每个物体自身的坐标系为基础的,所以你需要留心物体从哪个角度被观测这个问题。回到叉车这个例子中,话说我们想让起重滑杆(bodyB)离主体稍微偏右一点(bodyA)。我们可以如下设置位置:

pic

1
2
prismaticJointDef.localAnchorA.Set( 6,-3);//a little outside the bottom right corner
prismaticJointDef.localAnchorB.Set(-1,-4);//bottom left corner

现在对于基本的平移连接器,我们需要有一些适当的变量,然后我们创建连接器(这里我们假定有一个类成员变量为了以后方便访问,在此对连接器指针进行存储)。

1
m_joint = (b2PrismaticJoint*)m_world->CreateJoint( &prismaticJointDef );

当运行一开始立刻点击暂停,你会看到物体初始化的位置位于第一个时间步长期间平移约束发挥作用之前。

pic

既然连接器的轴线是(0,1)并且没有物体围绕起旋转,对于y值来说我们可以使用任何之前的数值设置锚点,物体依然可以沿着相同的轴线滑动。不管怎么说,当我们为连接器设置限制的时候,为了把物体限制在正确的位置,我们需要认真对待轴线的位置。为了能够在接下来更好的理解连接限制器的值,让我们在屏幕上打印出当前连接器的平移和速度数值:

1
2
3
4
5
//in Step()
m_debugDraw.DrawString(5, m_textLine, "Current joint translation: %.3f", m_joint->GetJointTranslation());
m_textLine += 15;
m_debugDraw.DrawString(5, m_textLine, "Current joint speed: %.3f", m_joint->GetJointSpeed());
m_textLine += 15;

下面是两个关于位置的例子。左边的那个,我们指定锚点在同一位置,连接器的平移认为是零。右边的例子,起重滑杆的连接器锚点被移到大物体顶部的位置并保持同一高度,根据屏幕上的显示,到6个单位高度的时候就停止。

pic

如果你用鼠标拾取物体并进行移动或旋转,你会发现轴线总是和两个物体保持相同的相对位置,并且平移的测量方式也总是沿着轴线进行计量的。

pic

参照角(Reference angle)

在这个例子中物体起始的默认角度为零,当我们使用平移连接器对它们进行限制的时候,它们将不能进行任何角度的旋转(相对于其他物体来说)。如果我们让连接器上的物体之间有不同角度的话,我们需要对平移连接器中的参照角属性进行定义。参照角作为bodyB的指定角度,并且bodyA作为视角。

作为例子,话说我们想让起重滑杆向后倾斜一点以保证货物不会从叉车上脱落。我们可以对连接器的参照角设置为5度,可以让bodyB相对于bodyA逆时针旋转5度。

1
prismaticJointDef.referenceAngle = 5 * DEGTORAD;

pic

既然每个物体的本地锚点被限制在轴线上,参照角有效引起bodyB围绕本地锚点进行旋转(底部左下角)。

平移连接器限制(Prismatic joint limits)

在当前设置下,可以使两个物体自由的在滑杆上沿着轴线滑动,但是我们可以通过设置连接器限制来控制移动范围。连接器限制结合连接器平移定义了下限和上限并将物体限制在其范围内。这些可以在屏幕上很方便的显示出来,这样我们可以很容易的看着屏幕上显示的数值,通过小范围移动物体来对设置限制范围。对于叉车这个例子而言,我们可以把下限设置为零(这也是起重滑杆和地面接触的数值),然后上限可以设置成,呃…大概10看起来还不错。

1
2
3
prismaticJointDef.enableLimit = true;
prismaticJointDef.lowerTranslation = 0;
prismaticJointDef.upperTranslation = 10;

pic

enableLimit属性的默认值为false。你可以在平移连接器创建之后通过get或set方法设置限制属性,具体使用如下:

1
2
3
4
5
6
7
8
//alter joint limits
void EnableLimit(bool enabled);
void SetLimits( float lower, float upper );

//query joint limits
bool IsLimitEnabled();
float GetLowerLimit();
float GetUpperLimit();

当使用连接器限制的时候需要对一些事情保持警惕…

  • 对enableLimits的设置会影响两个限制,所以如果你只想让其中一个受限,那就把另一个限制设置的非常高(对于上限而言)或者很低(对于下限而言),为的是永远都达不到这些限制。
  • 把限制设置成相同的值可以很方便的把物体’夹’在给定的平移位置。在保持物体不会漏出限制范围的同时可以缓慢的改变这个数值直至最后让物体到达目标位置,这么做还不会和其它物体发生碰撞,并且不需要连接马达。
  • 直到物体被修正过来,非常快速的移动物体可以让物体在短短的几个时间步长内穿透限制。
  • 检查当前连接器是否在限制位置非常简单:
1
2
bool atLowerLimit = joint->GetJointTranslation() <= joint->GetLowerLimit();
bool atUpperLimit = joint->GetJointTranslation() >= joint->GetUpperLimit();

平移连接器马达(Prismatic joint motor)

平移连接器默认的行为是在没有任何阻力的情况下滑动。如果你想控制物体的运动要么对物体施加力或冲量,要么也可以设置连接器’马达’引起物体之间相对滑动。如果你想模拟带有动力的运动这会非常有帮助,例如活塞或者电梯,在或者是叉车。

指定的速度不过是一个速度指标,也意味着连接器不能保证一定可以达到这个速度。通过指定连接器马达所允许的最大力矩,你可以控制连接器要达到目标速度的加速度,有时候这甚至决定了连接器是否能够达到目标速度。力矩作用于连接器上的行为与力和冲量话题中所讨论的一样。作为一个例子,试着想下面这样设置连接器马达,以此移动起重滑杆向上托起。

1
2
3
prismaticJointDef.enableMotor = true;
prismaticJointDef.maxMotorForce = 500;//this is a powerful machine after all...
prismaticJointDef.motorSpeed = 5;//5 units per second in positive axis direction

enableMotor默认值为false。注意这里我们需要考虑本话题开始的时候所提到的轴线方向的问题。马达转速的功能可以让bodyB沿着轴线方向移动。另外,你可以考虑一下在负方向轴线上移动bodyA,特别是因为马达并不能真正的移动两个物体,只不过在它们之间的合适的方向上施加了一个推力或拉力,那么你可以使用平移连接器马达把它们拉到一起也可以把它们推开。

当连接器创建之后你可以通过get或set方法设置马达属性,可以通过下面这些方法实现:

1
2
3
4
5
6
7
8
9
//alter joint motor
void EnableMotor(bool enabled);
void SetMotorSpeed(float speed);
void SetMaxMotorForce(float force);

//query joint motor
bool IsMotorEnabled();
float GetMotorSpeed();
float GetMotorForce();

当使用平移连接器马达的时候对一些事情保持警惕…

  • 使用较小的数值设置最大力矩,连接器需要花费一些时间才能到达目标速度。如果你像让连接物体表现的更有份量,如果你想一直保持相同的加速度,你需要增加最大力矩的数值。
  • 连接器马达可以设置为零,以此保持连接器静止。对最大力矩设置一个较小的数值其表现就像刹车一样,会缓慢的降低物体的速度。使用高的最大力矩可以瞬间让连接器停止,然后需要一个很大的外部力矩移动连接器,就像是在生锈的表面上呃…滑动东西。

例子

你可能已经注意到平移连接器的概念和转动连接器非常的类似,并且属性和方法都像是一个模子里刻出来的。既然我们上面已经涵盖了主要的点,并且实现了一个简单的例子,针对与本次话题我就不多说了。

如果你对平移连接器更多兴趣,可以下载源代码,然后看下’Joints-prismatic’测试部分的代码,展示了第二个连接器来模拟一个平移货物的叉车,你可以对其进行控制。

pic

Comments