~~~~我的生活,我的点点滴滴!!
让物体移动,需要对其施加力或者冲量。可以通过时间的不断积累对物体施加力的作用来改变物体的运动,然而冲量作用于物体则会立刻
改变物体的速度。举个例子,想象一下现在你的车坏了,你想推车,可以选择驱动另外一辆车缓慢开向这辆坏了的车,当两辆车的保险杠
接触之后,继续对这辆车施加推力,然后通过时间不断的积累慢慢的让其移动。或者也可以用冲量,驱动一辆完好的车全速驶向这辆坏了
车。具体使用哪种方法取决于你。
也可以通过简单设置物体的位置来瞬间“曲速(参见Warpdrive)”物体。在游戏中你可能想有一个瞬移的特性,但是你要知道在真正的物理
世界当中并不存在这样一种特性!Box2D的全部侧重点是让所模拟的物体看起来更真实,基于这一点考虑我会建议你尽量使用力和冲量来移
动物体。大多数时候这么做看起来貌似过于严谨,但是在真实的世界里所有事情的发生都是通过力和冲量的,除非你就是想创建一个不是
真实世界里的特性(瞬移,等等),要三思!而且如果你这么做下去的话,可能会给你带来更多的麻烦。
角运动也可以通过力和冲量来控制,效果就像上面提到的缓慢/瞬间的线性版本一样。角力矩也称为扭矩(torque)。可以想象成转动强度,
就像你拧瓶盖儿一样-瓶盖不会跑出去,而且你可以持续对瓶盖施加作用力(扭矩,torque)。
我们将创建三个物体,使用上面提到的两种力来分别作用于它们平移或扭曲(twist)它们,代码如下:
//.h中
//定义了三个物体,全为指针对象
b2Body *m_body[3];
//.cpp中
b2Vec2 gravity(0, 0);
/*********************start*************************/
//定义一个物体的基本属性,他基本包含了我们所知现实世界物体的所有属性
b2BodyDef bodyDef;
//设置一个动态物体
bodyDef.type = b2_dynamicBody;
bodyDef.angle = 0;
//形状
b2PolygonShape boxShape;
//创建一个2x2的正方形盒子
boxShape.SetAsBox(1,1);
//定制器
b2FixtureDef fixtureDef;
fixtureDef.shape = &boxShape;
fixtureDef.density = 10;
for(int i = 0 ;i < 3; ++ i)
{
bodyDef.position.Set( -10 + i * 10, 20 );
m_body[i] = m_world->CreateBody(&bodyDef);
m_body[i]->CreateFixture(&fixtureDef);
}
///*************到底就一个正方形物体产生了******************/
//产生一条水平线
b2BodyDef lineDef;
lineDef.type = b2_staticBody;
lineDef.position.Set(0,0);
b2EdgeShape edgeShape;
edgeShape.Set(b2Vec2(-20,0), b2Vec2(20,0));
b2FixtureDef edgeFixture;
edgeFixture.shape = &edgeShape;
b2Body *lineBody = m_world->CreateBody(&lineDef);
lineBody->CreateFixture(&edgeFixture);
我们需要一个不使用鼠标就能影响物体运动的方法。我们可以利用testbed框架中键盘输入特性的时候了。覆盖Test类中Keyboard方法,
为每个物体定义不同的方法。
//.h中
void Keyboard(unsigned char key)override;
void Step(Settings* settings)override;
bool m_forceOn;
//.cpp中
//构造函数中初始化
m_forceOn = false;
void UserTest::Keyboard(unsigned char key)
{
switch (key)
{
case 'q':
{
cout << "q" << endl;
m_forceOn = !m_forceOn;
}
break;
case 'w':
{
cout << "w" << endl;
//向上直接用力(冲量)
m_body[1]->ApplyLinearImpulse(b2Vec2(0,30), m_body[1]->GetWorldCenter());
}
break;
case 'e':
{
cout << "e" << endl;
//瞬间或曲速(表示特别快)到目标地
m_body[2]->SetTransform(b2Vec2(10,30), 0);
}
break;
default:
Test::Keyboard(key);
}
}
void UserTest::Step(Settings* settings)
{
if( m_forceOn )
{
//向上渐渐的力,起始点为了物体的质心在世界中的位置(力)
m_body[0]->ApplyForce(b2Vec2(0,30), m_body[0]->GetWorldCenter());
}
Test::Step(settings);
}
其中SetTransform方法已经在物体(bodies)话题中讨论过了。ApplyForce和ApplyLinearImpulse方法传入了两个参数,第一个参数应该是所
施加的力的方向,本例中是线性增加。运行测试然后按下q/w/e按键。受到冲量作用的物体就像被什么东西突然撞到一样。这个运动的物体
被瞬间移动到一个新位置,但是注意它仍然保持了原来的线性和角速度。我们对物体施加了作用力之后到底发生了些什么呢?Keyboard
方法只有当我们按下按键的时候才会被调用一次,不是每个时间步长都会调用。我们使用了相同大小(50)的力和冲量,但是为什么力(ApplyForce)
的效果看起来比冲量不明显呢?嗯,那是因为重力也是作用力。试着取消重力作用或许能让问题的答案更清晰一些。原因是因为力在每个
时间步长(timestep)上让物体上移一点,随后在重力的作用下物体又下降一点。就这样不断的上下上下的纠结。那么对于冲量来说,在重力
对物体进行干扰之前完成了所有的工作。失重状态下,试着让力作用大概1秒钟时间,紧接着停止施加作用力。你会注意到,一秒钟力的作用
效果和冲量的作用效果有相同的速度。现在让我们来看看之前在施加作用力/冲量方法中所忽略的第二参数(m_body[0]->GetWorldCenter())
是什么意思?到目前为止我们一直使用物体自身可以获取质心的GetWorldCenter()方法来设置此参数。正如我们所看到的,当作用力作用于质心
的时候并不会引起物体的旋转。我们稍微偏移一下之前施加力/冲量的作用点。改变一下ApplyForce和ApplyLinearImpulse的作用点:
//上面我们设置盒子为了1x1(实际是2x2大小)这里设置b2Vec2(1,1)为右上角
m_body[1]->GetWorldPoint(b2Vec2(1,1)
这次,你应该会看到当盒子们运动的时候,会发生旋转。GetWorldPoint()方法用来将物体自身的坐标(物体坐标系(body coordinates))转换到
世界坐标系中,所以即便盒子发生旋转之后我们依然可以把力作用于盒子的右上角。可能会被认为是“拽着盒子角把盒子拎起来”,但是作用力
并不是作用在可见的矩形定制器上-力和冲量是作用于物体上,不是它的定制器上。力可以轻松的作用于任何之前的点上,即便是没有定制器
(fixtures)的空点上。
角运动被角力矩(扭矩,torque)和角动量所控制。它们的行为和线性运动相似,力是缓慢作用的而角动量是瞬间作用的。我们继续在Keyboard
中添加测试代码:
switch (key)
{
case 'q':
{
cout << "q" << endl;
m_forceOn = !m_forceOn;
m_body[0]->ApplyTorque( 20 );
}
break;
case 'w':
{
cout << "w" << endl;
//向上直接用力(冲量)
m_body[1]->ApplyLinearImpulse(b2Vec2(0,30), m_body[1]->GetWorldPoint(b2Vec2(1,1)));
//逆时针旋转(冲量)---这里我故意写成m_body[0]下面的gif图片会看到效果
m_body[0]->ApplyAngularImpulse( 20 );
}
break;
case 'e':
{
cout << "e" << endl;
//瞬间或曲速(表示特别快)到目标地
m_body[2]->SetTransform(b2Vec2(10,30), 0);
}
break;
default:
Test::Keyboard(key);
}
在长按住w后,你会发现中间的小盒子旋转向上,左边的盒子也在旋转,正常左边的盒子不会发生移动的,但是它们还是会有一点点移动,这是
因为它和地面之间有摩擦并且运动起来就像方轮子一样。为了能够更好的说明扭矩和角动量到底都做了些什么,关闭重力效果。在失重环境中,
我们可以看到对于转动力矩/角动量方法为什么只需要一个参数-既然不会引起线性运动,那么唯一我们需要指定的就是转动的方向。由于物体的
转动总是绕着物体质心展开,所以不会像上面线性力矩那样产生任何偏差。和线性力矩一样,传入相同的参数,ApplyTorque执行一秒钟以后会和
ApplyAngularImpulse立即执行所产生的角速度相同。