游戏中使用了atan和atan2吗?


47

我有一些麻烦的了解Math.tan()Math.atan()Math.atan2()

我具有三角学的基本知识,但对我来说,在游戏开发中使用SIN,COS和TAN等非常新。

我正在阅读一些教程,并且看到通过使用切线,我们可以得出一个对象需要旋转一个角度以面对另一个对象(例如鼠标)的角度。那么为什么我们仍然需要使用atan或atan2?


atan用于确定角度,对一千种不同的事物有用。您对它的使用有实际疑问,还是只是在寻求一般的数学帮助?
BlueRaja-Danny Pflughoeft 2011年

5
您绝对需要了解这些函数背后的数学/几何;一旦掌握了它们,它们就会成为您“世界理解”的一部分,就像您日常使用的基本语法一样。一旦您能够“讲数学/几何”,您将看到那些功能是获得结果的简便工具,最自然地使用。
FxIII 2011年

这些教程是错误的,或者是您误解了。您可以使用atan2()获取从一个对象到另一个对象的角度。下面说明其工作原理。
2011年

感谢人们的回答,现在我很遗憾没有在课堂上
给予

Answers:


93

切线公式是这样的:

tan(angle) = opposite/adjacent

参考此图:

直角三角形的示意图,带有角度theta及其相对的和相邻的边

其中a,相邻边o是相对的边,theta是角度。同样,正弦和余弦分别为sin(ang)= o / h和cos(ang)= a / h,其中h长边是:http : //www.mathwords.com/s/sohcahtoa.htm

同时atan反正切的缩写,也称为反正切)是的反tan像,例如:

atan(opposite/adjacent) = angle

因此,如果您知道相对侧和相邻侧的值(例如,通过从鼠标坐标中减去对象的坐标),则可以使用来获得角度值atan

但是,在游戏开发中,相邻边等于0(例如,矢量的x坐标为0)可能经常发生。请记住,tan(angle) = opposite/adjacent灾难性的被零除错误的可能性应该很清楚。因此,许多库都提供了一个名为的函数atan2,该函数可让您同时指定xy参数,从而避免为您除以零并在右象限中给出一个角度。

atan2图

(图由gareth提供,也请投票支持他的答案)


三角函数在游戏开发中的使用非常普遍,尤其是矢量图形,但通常库会为您隐藏三角函数。您可以使用sin / cos / tan执行许多涉及几何操作的任务,以便从三角形中查找值。您需要的是3个值(边长/角度值)来查找矩形三角形的其他值,因此它非常有用。

您甚至可以将正弦函数和余弦函数的“循环”特性用于游戏中的特殊行为,例如,我已经看到cos / sin经常用于使一个对象绕另一个对象旋转。


8
值得注意的是,Wikipedia描述了Atan2不是 atan)的其他用法,而不仅仅是避免被零除。例如,它会针对所使用的象限进行自我校正,通常情况下,您必须自己完成所有操作。
doppelgreener 2011年

确实,非常重要的说明就在那里。更新我的答案。
杰西·埃蒙德

在您的第一个较长的段落中,您没有晒黑吗?您将使用atan来获取角度(即,使方程逆转),并使用tan来获得边的比率(即,方程式正好表示)。
2011年

好吧,如果您知道o / a等于例如3,那么如果您想要该角度,请执行atan(3),这就是我的意思:如果要隔离该角度,请在比率上使用atan。如果要隔离比率,请在角度上使用tan。
杰西·埃蒙德

那我将更改您的措辞,因为听起来您在说相反的话。
2011年

63

在此处输入图片说明


1
编辑最佳答案以在其中包含此图是否不礼貌?该图很棒,在杰西(Jesse)对atan2()的书面解释之后,就非常合适。
2011年

1
继续:成为我的客人!完成后,我将删除此答案。
加雷斯·里斯

7
不,不。保持!它被认为是值得人们赞扬的图表。
杰西·埃蒙德

12

这是关于Trig函数(包括atan()和atan2())的一种稍微不同的思考方式,我认为是有帮助的(由于某些原因,“对立/相邻”的解释使我感到困惑)。

x,y,r,θ

可以通过移动从一个点到另一个X水平单元和ÿ单元垂直(称为长方形笛卡尔坐标)或通过移动距离ř在角Ɵ(称为极性在2D坐标)。

假设我们有一个极坐标(r,Ɵ),我们想将其转换为(x,y)。

cos(Ɵ)给出沿x轴的r的比例:

  • 如果r = 1,则x = cos(Ɵ)。
  • 如果r = 100,则x = 100 * cos(Ɵ)。
  • 通常x = r * cos(s)。

同样,sin(Ɵ)给出沿y轴的r的比例:

  • 如果r = 1,则y = sin(Ɵ)。
  • 如果r = 100,则y = 100 * sin(Ɵ)。
  • 通常,y = r * sin(Ɵ)。

如何将直角坐标(x,y)转换为极坐标(r,Ɵ)?

rxy形成的直角三角形的斜边,因此:

  • r = sqrt(x x + y y)

tan(Ɵ)给出长度为r的直线的斜率(线段上的上升量)。所以:

  • tan(Ɵ)= y / x
  • Ɵ= atan(y / x)

但是,执行y / x时,计算3/4得出的答案与计算-3 / -4相同。同样,-3 / 4给出与3 / -4相同的答案。因此,我们有atan2(y,x)可以正确处理各个符号并防止被零除/无穷大错误。

  • Ɵ= atan2(y,x)

4

杰西(Jesse)和西德(Sid)基本上是正确的,但我怀疑您真的对问题有所了解。

需要使用Atan2(),因为atan()不能告诉您所需的水平角度,因为它无法处理象限。

这意味着对向量(-2,2)和(2,-2)使用atan将获得相同的值。然后,您将打开参数的符号并将pi添加到结果中。另外,您可以考虑杰西提到的零分特例。当x接近0时,atan2()也会比atan更好

因此,如果您想要-pi与pi之间的向量角度

x = -2
y = 2
angle = Math.Atan2(y, x)

要么

x = -2
y = 2
angle = calculateAngle(y, x);

double CalculateAngle(double y, double x)
{
    double angle = 0;
    if (x == 0)
    {
        if (y == 0)
            angle = 0;
        else if (y > 0)
            angle = Math.PI/2;
        else
            angle = -Math.PI/2;
    }
    else
    {
        angle = Math.Atan(y/x);
        if (x < 0)
        {
            if (y > 0)
            {
                angle += Math.PI;
            }
            else if (y < 0)
            {
                angle -= Math.PI;
            }
            else
            {
                angle = Math.PI;
            }
        }
    }
    return angle;
}

1
“这意味着对向量(-2,2)和(2,2)使用atan将获得相同的值。” 在这种情况下,您的atan难以置信地被破坏,因为其中一个应该是-π/ 4,另一个应该是-π/ 4。对投票给这些垃圾的所有人感到羞耻。

代码仍然是错误的。您正在测试,y==0然后x在另一个分支中除以。
sam hocevar 2012年

0

我将以简洁的方式澄清一些事情。请参考在线三角学教程以获取详细说明。

设一个角度。然后tan(a)= tan(a + 2 * pi)。

atan是tan逆,即给定tan的角度。当您呼叫atan(tan(a + 2 * pi))时,答案将是a。这对于您的应用程序是不够的。

atan2将使用2个参数来帮助解决这种确切情况。atan取x和y,它们基本上是cos(a)和sin(a)。

atan2(sin(a),cos(a))= atan2(sin(a + 2 * pi),cos(a + 2 * pi))= a + 2 * pi / * sin和cos具有不同的符号,导致换一个答案* /

请找到一些教程来解释为什么这样做。

您的代码应如下所示:

if (mouseMoved)
{
  double angle = atan2(mousey - objecty, mousex - objectx);

  object. setTransform to Rotate(angle);

  // If you want to print it
  print radian_to_degrees(angle); // Because angle is in radian 360 degrees = 2*Pi radians
}

tan(a) = - tan(-a),您想要表达的方程式可能是tan(a) = tan(pi+a)
Ali1S232

0

atan2我在代码中发现的一种用法是“符号角”。

通常,找到两个向量之间的角度的方法是

inline float angleWith( const Vector2f& o ) const
{
    return acosf( this->normalizedCopy().dot(o.normalizedCopy()) ) ;
}

但这并不能告诉您哪一条“领先”(即“顺时针方向”比另一条领先)。此信息对于手势跟踪可能很重要。

您可以找到(1,0)两个向量从x轴的角度,但是存在一个棘手的歧义问题:使用上述cos方法,角度为315度的向量返回45度,角度为45度的向量也是如此。您可以进行签名检查y以解决此问题,也可以使用atan2

// Returns + if this leads o.
// more expensive than unsigned angle.
inline float signedAngleWith( const Vector2f& o ) const
{
  float aThis = atan2f( y, x );
  float aO = atan2f( o.y, o.x ) ;
  return aThis - aO ;
}

0

请注意,atan没有损坏。反正切或反切仅是-PI / 2和PI / 2之间的函数。它会重复此模式,但它不是一个功能,因为它无法处理多个答案,因此对于计算机来说是一个问题。

对于-PI / 2和PI / 2之间的asin以及0和PI之间的acos而言,这是相同的。这些是功能发生的最简单范围。对于atan和asin,它从最消极的状态变为最积极的状态。对于acos,它从最积极的方面转到最消极的方面。(这有助于内插更准确的答案)

因此asin,acos和atan是数学函数。

但是,atan2在编程中更为有用,因为它提供了完整的旋转(以弧度或360度或400度为单位的PI)。请注意,他们只为晒黑而不是为罪或cos生产一个。Tan是唯一使用水平和垂直(x,y)的人

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.