BOX2D 自然的旋转到一个指定角度

最近在自己第一个游戏项目里面碰到一个看似简单却花了我2天时间才解决的问题

特地发出来分享一下

 

在BOX2D 中如何控制body 自然的旋转到一个指定角度?

这个问题在许多游戏中控制角度时都会遇到,但是在BOX2D中,你必须考虑到如果转动中与其他body碰撞等物理因素。

能够想到的解决方案有三种:

1 在update方法里不断更改body的角度,使他接近于要设定的角度。 

b2vec2 clickedPoint;//设定点的向量

float bodyAngle = body->GetAngle();//取得物体自身的弧度

b2Vec2 toTarget = clickedPoint - body->GetPosition();//计算角度差

float desiredAngle = atanf( -toTarget.x, toTarget.y );//计算设定的弧度

float totalRotation = desiredAngle - bodyAngle;//计算需要旋转的弧度

while ( totalRotation < -180 * DEGTORAD ) totalRotation += 360 * DEGTORAD;//调整设定角度到-180到180度之间

while ( totalRotation >  180 * DEGTORAD ) totalRotation -= 360 * DEGTORAD;

float change = 1 * DEGTORAD; //每次间隔允许最大旋转角度 

float newAngle = bodyAngle + min( change, max(-change, totalRotation)); 

body->SetTransform( body->GetPosition(), newAngle );

 

 

很明显,第一种方法并不适合在物理引擎中使用,因为不断设定body的角度会打乱BOX2D中的仿真效果。

 

 

2 在update方法里不断给body施加一个能使body转到设定角度的力矩。

刚开始肯定会想到这样做:

  float totalRotation = desiredAngle - bodyAngle;

  while ( totalRotation < -180 * DEGTORAD ) totalRotation += 360 * DEGTORAD;

  while ( totalRotation >  180 * DEGTORAD ) totalRotation -= 360 * DEGTORAD;

  body->ApplyTorque( totalRotation);

 

但是运行看看?你会发现一个问题,物体始终受到一个朝向设定点角度方向的力矩,直到物体转到这个角度,乍看好像没问题,但是实际上,物体在到达设定角度时角速度并不是零,所以物体将继续转过这个角度,并受到一个反向的力矩,然后到达设定角度后又一次超过设定角度,这样永远循环摆动,却永远到达不了设定角度。

总结了下原因没有考虑到自身本身的角速度影响。

真的非常难解释的非常清楚,可能我也理解的不太够吧,直接给出解决方案。

方法是假设一定时间间隔dt内body不受任何转矩,计算出dt间隔后的body角度,用来替换现在的body角度:

float dt = 1.0f / 60.0f;

float nextAngle = bodyAngle + body->GetAngularVelocity() *dt;

float totalRotation = desiredAngle - nextAngle;

while ( totalRotation < -180 * DEGTORAD ) totalRotation += 360 * DEGTORAD;

while ( totalRotation >  180 * DEGTORAD ) totalRotation -= 360 * DEGTORAD;

float desiredAngularVelocity = totalRotation / dt;

float torque = body->GetInertia() * desiredAngularVelocity / dt;

body->ApplyTorque( torque );

 

但是这样还不是最好的方法,body仍然需要来回晃动数个来回才能最终停下来。

 

3 在update方法里不断给body施加一个能使body转到设定角度的惯性冲量。

最终的解决方案是通过施加一个冲量,和上面的方法相似:

float dt = 1.0f / 60.0f;

float nextAngle = bodyAngle + body->GetAngularVelocity() *dt;

float totalRotation = desiredAngle - nextAngle;

while ( totalRotation < -180 * DEGTORAD ) totalRotation += 360 * DEGTORAD;

while ( totalRotation >  180 * DEGTORAD ) totalRotation -= 360 * DEGTORAD;

float desiredAngularVelocity = totalRotation / dt;

float impulse = body->GetInertia() * desiredAngularVelocity;// disregard time factor

body->ApplyAngularImpulse( impulse );

 

此外,如果你想在旋转过程中设定一个最大旋转速度,可以添加一个change值

float dt = 1.0f / 60.0f;

float nextAngle = bodyAngle + body->GetAngularVelocity() *dt;

float totalRotation = desiredAngle - nextAngle;

while ( totalRotation < -180 * DEGTORAD ) totalRotation += 360 * DEGTORAD;

while ( totalRotation >  180 * DEGTORAD ) totalRotation -= 360 * DEGTORAD;

float desiredAngularVelocity = totalRotation / dt;

float change = 1 * DEGTORAD; //allow 1 degree rotation per time step

desiredAngularVelocity = min( change, max(-change, desiredAngularVelocity));

float impulse = body->GetInertia() * desiredAngularVelocity;// disregard time factor

body->ApplyAngularImpulse( impulse );

 

至此,基本完美的解决了这个问题,并且可以通过调整dt的值,可以实现不同精度的旋转body到指定角度,通过调整change的值,改变旋转的最大速度。

 

对应的,此方法还可以实现控制body自然的加速到一个指定速度,我将在下次详细讲下方法。

你可能感兴趣的:(box2D)