任何更快的方法来移动一个圆圈的东西?

时间:2015-10-07 23:42:55

标签: performance actionscript-3 math

目前我正在使用Math.cos和Math.sin在我的游戏中将对象移动到一个圆圈中,但我怀疑它很慢(虽然没有进行适当的测试)但是在阅读了一下之后。

有没有办法以更快的方式计算出来?一直在阅读一个替代方案可能是有一种带有存储的预先计算结果的哈希表,就像老人们在计算机时代之前的旧时代一样。

赞赏任何意见。

3 个答案:

答案 0 :(得分:2)

扩展我的评论,如果你没有任何角加速度(角速度保持不变 - 这是一个要求对象保持在一个半径恒定的圆形中行进而不改变中心指向力,例如通过字符串中的张力),然后您可以使用以下策略:

1)计算B = angular_velocity * time_step_size。这是对象在一个时间步长内需要经过多少角度变化。

2)计算sinb = sin(B)cosb = cos(B)

3) 请注意,我们要将角度从A更改为A + B(对象逆时针方向)。在这个推导中,我们绕轨道运行的圆心由中心给出。 由于圆的半径不变,我们知道r*sin(A+B) = y_new = r*sin(A)cos(B) + r*cos(A)sin(B) = y_old * cos(B) + x_old*sin(B)r*cos(A+B) = x_new = r*cos(A)*cos(B) - r*sin(A)sin(B) = x_old*cos(B) - y_old*sin(B)

我们已经删除了我们还不知道的任何余弦和正弦,所以笛卡尔坐标可以写成

x_new = x_old*cosb - y_old*sinb

y_new = x_old*sinb + y_old*cosb

除了在一次调用的初始化步骤之外,不再有cos或sin调用。显然,如果B因任何原因(角速度或时间步长变化)不断变化,这将无法保存任何内容。

您会注意到这与将位置矢量乘以固定的旋转矩阵相同。您可以按圆圈中心进行翻译,如果您不想仅考虑原点中心的圆圈,则可以翻译回来。

首先编辑

正如@ user5428643所提到的,由于半径的漂移,这种方法在数值上是不稳定的。您可以通过定期重新规范化x和y(x_new = x_old * r_const / sqrt(x_old^2 + y_old^2)并且类似于每几千步的y - 来纠正这一点 - 如果您实现这一点,请保存因子r_const / sqrt(x_old^2 + y_old^2),因为{{1}是相同的}和x)。如果我想出一个更好的解决办法,我会考虑更多并编辑这个答案。

第二次编辑

关于数值随时间漂移的更多评论:

我在C ++和python中做了几个测试。在使用单精度浮点数的C ++中,当B = 0.1时,即使在100万个时间步长之后也存在相当大的漂移。我使用了一个半径为1的圆。在双精度中,我没有注意到在1亿步之后视觉上的任何漂移,但检查半径表明它被污染了较低的几位数。在每个步骤上执行重新规范化(如果您只是进行可视化,这是不必要的)导致运行时间比漂移版本慢大约4倍。但是,即使这个版本比每次迭代使用sin和cos快2-3倍。我在g ++中使用了完全优化(-O3)。在python中(使用数学包)我只在drifty和normalized版本之间加速了2,但是sin和cos版本实际上插入了这两者之间 - 就运行时间而言,它几乎正好在这两者之间。在几千步中重新规范每一次仍然会使这更快,但它并没有我的C ++版本所表明的差别那么大。

我没有做太多的科学测试来获得时间,只进行了几次测试,步数为100万到10亿步,增量为10。

答案 1 :(得分:2)

对不起,没有足够的代表发表评论。

@neocpp和@ oliveryas01的答案在没有舍入误差的情况下都是完全正确的。

@ oliveryas01的答案,只是直接使用正弦和余弦,并在必要时预先计算并存储许多值,这样可以正常工作。

然而,@ neocpp的回答是,使用旋转矩阵以小角度重复旋转,在数值上是不稳定的;随着时间的推移,半径中的舍入误差将呈指数级增长,因此如果长时间运行程序,对象将慢慢离开圆圈,向内或向外螺旋。

您可以通过一些数值分析在数学上看到这一点:在每个阶段,平方半径近似乘以一个近似恒定且近似等于1的数字,但由于浮动的不精确性,几乎肯定不会完全等于1点表示。

如果当然,如果您使用的是双精度数字并且只是尝试获得简单的视觉效果,则此错误可能不会对您造成影响。

答案 2 :(得分:1)

完全避免触发功能的另一种可能性是使用极坐标模型,您可以在其中设置距离和角度。例如,您可以将x坐标设置为距离,将旋转设置为角度,如...

    var gameBoardPin:Sprite = new Sprite();
    var gameEntity:Sprite = new YourGameEntityHere();
    gameBoardPin.addChild( gameEntity );

......并在你的循环中......

    // move gameEntity relative to the center of gameBoardPin
    gameEntity.x = circleRadius;
    // rotate gameBoardPin from its center causes gameEntity to rotate at the circleRadius
    gameBoardPin.rotation = desiredAngleForMovingObject

gameBoardPin的x,y坐标将设置为gameEntity的旋转中心。所以,如果你想让gameEntity围绕舞台中心旋转一个100像素的系绳,你可能会...

    gameBoardPin.x = stage.stageWidth / 2;
    gameBoardPin.y = stage.stageHeight / 2;
    gameEntity.x = 100;

...然后在循环中你可能......

    desiredAngleForMovingObject += 2;
    gameBoardPin.rotation = desiredAngleForMovingObject

使用此方法,您使用的是度数而不是弧度。