我有一些麻烦的了解Math.tan()
和Math.atan()
和Math.atan2()
。
我具有三角学的基本知识,但对我来说,在游戏开发中使用SIN,COS和TAN等非常新。
我正在阅读一些教程,并且看到通过使用切线,我们可以得出一个对象需要旋转一个角度以面对另一个对象(例如鼠标)的角度。那么为什么我们仍然需要使用atan或atan2?
我有一些麻烦的了解Math.tan()
和Math.atan()
和Math.atan2()
。
我具有三角学的基本知识,但对我来说,在游戏开发中使用SIN,COS和TAN等非常新。
我正在阅读一些教程,并且看到通过使用切线,我们可以得出一个对象需要旋转一个角度以面对另一个对象(例如鼠标)的角度。那么为什么我们仍然需要使用atan或atan2?
Answers:
切线公式是这样的:
tan(angle) = opposite/adjacent
参考此图:
其中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
,该函数可让您同时指定x
和y
参数,从而避免为您除以零并在右象限中给出一个角度。
(图由gareth提供,也请投票支持他的答案)
三角函数在游戏开发中的使用非常普遍,尤其是矢量图形,但通常库会为您隐藏三角函数。您可以使用sin / cos / tan执行许多涉及几何操作的任务,以便从三角形中查找值。您需要的是3个值(边长/角度值)来查找矩形三角形的其他值,因此它非常有用。
您甚至可以将正弦函数和余弦函数的“循环”特性用于游戏中的特殊行为,例如,我已经看到cos / sin经常用于使一个对象绕另一个对象旋转。
这是关于Trig函数(包括atan()和atan2())的一种稍微不同的思考方式,我认为是有帮助的(由于某些原因,“对立/相邻”的解释使我感到困惑)。
可以通过移动从一个点到另一个X水平单元和ÿ单元垂直(称为长方形或笛卡尔坐标)或通过移动距离ř在角Ɵ(称为极性在2D坐标)。
假设我们有一个极坐标(r,Ɵ),我们想将其转换为(x,y)。
cos(Ɵ)给出沿x轴的r的比例:
同样,sin(Ɵ)给出沿y轴的r的比例:
如何将直角坐标(x,y)转换为极坐标(r,Ɵ)?
r是x和y形成的直角三角形的斜边,因此:
tan(Ɵ)给出长度为r的直线的斜率(线段上的上升量)。所以:
但是,执行y / x时,计算3/4得出的答案与计算-3 / -4相同。同样,-3 / 4给出与3 / -4相同的答案。因此,我们有atan2(y,x)可以正确处理各个符号并防止被零除/无穷大错误。
杰西(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;
}
y==0
然后x
在另一个分支中除以。
我将以简洁的方式澄清一些事情。请参考在线三角学教程以获取详细说明。
设一个角度。然后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)
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 ;
}
请注意,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)的人