~~~~我的生活,我的点点滴滴!!
让物体移动,需要对其施加力或者冲量。可以通过时间的不断积累对物体施加力的作用来改变物体的运动,然而冲量作用于物体则会立刻
改变物体的速度。举个例子,想象一下现在你的车坏了,你想推车,可以选择驱动另外一辆车缓慢开向这辆坏了的车,当两辆车的保险杠
接触之后,继续对这辆车施加推力,然后通过时间不断的积累慢慢的让其移动。或者也可以用冲量,驱动一辆完好的车全速驶向这辆坏了
车。具体使用哪种方法取决于你。
也可以通过简单设置物体的位置来瞬间“曲速(参见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立即执行所产生的角速度相同。