Unity计算二维向量夹角余弦值和正弦值的优化方法参考
如果不考虑优化问题,计算两个向量的余弦值或者正弦值可以直接使用类似的方法:
[SerializeField]
Vector2 v1, v2;
void Start()
{
float valCos = Mathf.Acos(Vector2.SignedAngle(v1, v2));
float valSin = Mathf.Asin(Vector2.SignedAngle(v1, v2));
}
但是上面的Vector2.SignedAngle方法实际上是先计算出余弦值再根据余弦值计算角度,然后我们再根据角度计算出余弦值,这个太绕了,属于脱那什么放那什么的做法。
从数学原理上来说,计算夹角余弦的思路如下:
将公式代入得到:
但是这里面出现了两次开方运算,可以变换成如下形式:
这样就只需要一次开方运算,对应代码参考如下:
bool TryVectorAcos(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, out float acos)
{
acos = 0;
if (Mathf.Approximately((p2 - p1).sqrMagnitude, 0) || Mathf.Approximately((p4 - p3).sqrMagnitude, 0)) return false;
float ax = p2.x - p1.x;
float ay = p2.y - p1.y;
float bx = p4.x - p3.x;
float by = p4.y - p3.y;
acos = (ax * bx + ay * by) / (Mathf.Sqrt((ax * ax + ay * ay) * (bx * bx + by * by)));
return true;
}
带入上面的余弦值公式可得到:
但这个公式也出现了两次开方运算,可以将该公式变换成如下形式:
这样也是只需要一次开方运算就好,对应代码参考如下:
bool TryVectorAsin(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, out float asin)
{
asin = 0;
if (Mathf.Approximately((p2 - p1).sqrMagnitude, 0) || Mathf.Approximately((p4 - p3).sqrMagnitude, 0)) return false;
float ax = p2.x - p1.x;
float ay = p2.y - p1.y;
float bx = p4.x - p3.x;
float by = p4.y - p3.y;
asin = (ax * by - ay * bx) / Mathf.Sqrt((ax * ax + ay * ay) * (bx * bx + by * by));
return true;
}
如果要同时获得正弦值和余弦值,以避免重复计算两个矢量模的积,代码参考如下:
bool TryVectorAcosAndAsin(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, out float acos, out float asin)
{
acos = 0;
asin = 0;
if (Mathf.Approximately((p2 - p1).sqrMagnitude, 0) || Mathf.Approximately((p4 - p3).sqrMagnitude, 0)) return false;
float ax = p2.x - p1.x;
float ay = p2.y - p1.y;
float bx = p4.x - p3.x;
float by = p4.y - p3.y;
float denominator = Mathf.Sqrt((ax * ax + ay * ay) * (bx * bx + by * by));
acos = (ax * bx + ay * by) / denominator;
asin = (ax * by - ay * bx) / denominator;
return true;
}