您的位置:首页 > 其它

box2d 旋转到指定角度

2013-01-05 14:55 176 查看
最近在自己第一个游戏项目里面碰到一个看似简单却花了我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的值,改变旋转的最大速度。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: