1

情况

一段时间以来,我一直在努力解决这个问题。基本上,我有一个角度r (弧度),我想在模拟的每个步骤中以设定的速度dr (弧度/秒)将其更改为所需的角度wr (弧度) 。因此,我需要对于每一步,我都有一个新的角度nr,其中nr = r + dr * dtdt是自上一步以来的增量时间。

角度在空间 [-pi, pi] 中,增量角度由最短的转弯方式决定。对于每一步,增量角度被添加到当前旋转,包装到 [-pi, pi] 并存储为新角度。

由于我的步数没有无限精确,我显然会 - 实际上 - 永远不会直接达到所需的角度,因此我需要找到达到所需旋转的时间 -并交叉- 然后停止旋转并设置角度到所需的角度。在伪代码中:

if rotationSpeed!= 0
     angle = WrapRotation(angle + rotationSpeed * deltaTime)
     if desiredAngle has been reached or crossed
       angle = desiredAngle
       rotationSpeed = 0

问题

在几乎所有情况下,这很容易做到,但是当您的角度足够高或足够低(接近 -pi 或 pi)并且新角度带您越过“边界”时,可以说,事情变得复杂了。我所有的尝试都未能涵盖当前、先前和所需轮换的所有可能情况。所以我问你是否碰巧知道解决方案?

如果需要,我在下面附上了我的代码(C#):

// Sets the desired rotation and rotation speed in radians/second:
public void SetRotation(float rotation, float speed)
{
        desiredRotation = MathHelper.WrapAngle(rotation);
        if (desiredRotation != this.rotation)
        {
            // Determine the shortest way to turn (indicated by the sign of speed)
            float a = desiredRotation - this.rotation;
            if (a > Math.PI) a -= 2 * (float)Math.PI;
            if (a < -Math.PI) a += 2 * (float)Math.PI;

            desiredRotationSpeed = a < 0 ? -speed : speed;
        }
 }

 // Update is called per each step. Takes in the amount of seconds since the last call.
 public void Update(float seconds)
 {
        /* Other stuff */
        if (desiredRotationSpeed != 0)
        {
            float delta = desiredRotationSpeed * seconds;
            rotation = MathHelper.WrapAngle(rotation + delta);
            if( /* This is the part I'm struggling with. */ )
            {
                desiredRotationSpeed = 0f;
                rotation = desiredRotation;
            }
        }
        /* Other stuff */
 }

在正常情况下(非循环行为),以下工作:

if (Math.Abs(value - desiredValue) < Math.Abs(deltaValue))
    /* Desired has been reached. Do stuff! */

所以要澄清。我想找出何时达到所需的角度(并且由于精度而超过),以便我可以将当​​前角度设置为所需的角度,并停止旋转。

非常感谢你的帮助!我有一种感觉,这真的有一个简单的解决方案!:3

4

2 回答 2

2

为什么要越过所需的旋转?为什么不将 delta 计算为当前角度和所需角度之间的最小角度以及您想要移动的 delta?这样,最后一步将您移动到所需的角度。

我还会更改您的代码,以便SetRotation只存储两个值desiredRotationspeed然后计算Update可以将所需角度与当前角度进行比较,计算差异,将其限制为速度值 x 秒数,然后移动该金额(与方向 +ve 或 -ve 的适当 if 语句)。

于 2012-05-30T17:01:51.307 回答
0

多亏了 Ian Mercer,我才知道该怎么做。

与普通号码一样:

if (Math.Abs(value - desiredValue) < Math.Abs(deltaValue)) 
    /* Desired has been reached. Do stuff! */ 

我所需要的只是一些三角函数来找到角度之间的最小角度。将这两个角度视为从 origo 指向的向量(或者更确切地说是两个向量,它们与 x 轴之间的角度),那么最小的角度很容易找到。考虑以下:

Math.Atan2(Math.Sin(toAngle - fromAngle), Math.Cos(toAngle - fromAngle))

那会给我最小的角度。找出何时停止旋转只是看旋转的距离是否大于两个角度之间的最小距离/角度。

对上面的代码进行以下更改将解决问题:

  double distance = Math.Atan2(Math.Sin(desiredRotation - rotation),
       Math.Cos(desiredRotation - rotation));
  if(Math.Abs(distance) < Math.Abs(delta))
  {
       desiredRotationSpeed = 0f;
       rotation = desiredRotation;
  }

我希望我曾为这个问题咨询过亲爱的老乔治·波利亚。感谢人们的帮助,我需要朝着正确的方向前进!

于 2012-05-30T17:24:28.617 回答