zoukankan      html  css  js  c++  java
  • 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自然的加速到一个指定速度,我将在下次详细讲下方法。

  • 相关阅读:
    HTML5
    js实现查找字符串中最多的字符的个数
    get和post的区别
    第十七篇 类的特殊成员
    第十八篇 面向对象修饰符
    MariaDB+Keepalived双主高可用配置MySQL-HA
    linux命令详解——crontab
    Java的内存泄漏
    jvm监控工具jconsole进行远程监控配置
    loadrunner执行场景时报Error -27040: Data Format Extension: Init: Internal error问题解决
  • 原文地址:https://www.cnblogs.com/sawyerzhu/p/2538556.html
Copyright © 2011-2022 走看看