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方法实际上是先计算出余弦值再根据余弦值计算角度,然后我们再根据角度计算出余弦值,这个太绕了,属于脱那什么放那什么的做法。

        从数学原理上来说,计算夹角余弦的思路如下:

将公式代入得到:

但是这里面出现了两次开方运算,可以变换成如下形式:

这样就只需要一次开方运算,对应代码参考如下:

static public bool TryVectorAcos(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, out float acos)
{
	acos = 1;

	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 sqrMag1 = ax * ax + ay * ay;
	float sqrMag2 = bx * bx + by * by;

	if (Mathf.Approximately(sqrMag1, 0) || Mathf.Approximately(sqrMag2, 0)) return false;

	acos = (ax * bx + ay * by) / (Mathf.Sqrt(sqrMag1 * sqrMag2));

	return true;
}

static public bool TryVectorAcos(Vector2 direction1, Vector2 direction2, out float acos)
{
	acos = 1;

	float ax = direction1.x;
	float ay = direction1.y;
	float bx = direction2.x;
	float by = direction2.y;

	float sqrMag1 = ax * ax + ay * ay;
	float sqrMag2 = bx * bx + by * by;

	if (Mathf.Approximately(sqrMag1, 0) || Mathf.Approximately(sqrMag2, 0)) return false;

	acos = (ax * bx + ay * by) / (Mathf.Sqrt(sqrMag1 * sqrMag2));

	return true;
}

带入上面的余弦值公式可得到:

但这个公式也出现了两次开方运算,可以将该公式变换成如下形式:

这样也是只需要一次开方运算就好,对应代码参考如下:

static public bool TryVectorAsin(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, out float asin)
{
	asin = 0;

	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 sqrMag1 = ax * ax + ay * ay;
	float sqrMag2 = bx * bx + by * by;

	if (Mathf.Approximately(sqrMag1, 0) || Mathf.Approximately(sqrMag2, 0)) return false;

	asin = (ax * by - ay * bx) / Mathf.Sqrt(sqrMag1 * sqrMag2);

	return true;
}

static public bool TryVectorAsin(Vector2 direction1, Vector2 direction2, out float asin)
{
	asin = 0;

	float ax = direction1.x;
	float ay = direction1.y;
	float bx = direction2.x;
	float by = direction2.y;

	float sqrMag1 = ax * ax + ay * ay;
	float sqrMag2 = bx * bx + by * by;

	if (Mathf.Approximately(sqrMag1, 0) || Mathf.Approximately(sqrMag2, 0)) return false;

	asin = (ax * by - ay * bx) / Mathf.Sqrt(sqrMag1 * sqrMag2);

	return true;
}

        如果要同时获得正弦值和余弦值,以避免重复计算两个二维向量模的积,代码参考如下:

static public bool TryVectorAcosAndAsin(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4, out float acos, out float asin)
{
	acos = 0;
	asin = 0;

	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 sqrMag1 = ax * ax + ay * ay;
	float sqrMag2 = bx * bx + by * by;

	if (Mathf.Approximately(sqrMag1, 0) || Mathf.Approximately(sqrMag2, 0)) return false;

	float denominator = Mathf.Sqrt(sqrMag1 * sqrMag2);
	acos = (ax * bx + ay * by) / denominator;
	asin = (ax * by - ay * bx) / denominator;

	return true;
}

static public bool TryVectorAcosAndAsin(Vector2 direction1, Vector2 direction2, out float acos, out float asin)
{
	acos = 0;
	asin = 0;

	float ax = direction1.x;
	float ay = direction1.y;
	float bx = direction2.x;
	float by = direction2.y;

	float sqrMag1 = ax * ax + ay * ay;
	float sqrMag2 = bx * bx + by * by;

	if (Mathf.Approximately(sqrMag1, 0) || Mathf.Approximately(sqrMag2, 0)) return false;

	float denominator = Mathf.Sqrt(sqrMag1 * sqrMag2);
	acos = (ax * bx + ay * by) / denominator;
	asin = (ax * by - ay * bx) / denominator;

	return true;
}

       以上并没有提及两个“单位”二维向量之间夹角的余弦值和正弦值,因为两个“单位”二维向量的模都是1,所以其模的积也是1,所以夹角余弦值就是这两个二维向量的点乘值,即Vector2.Dot的值,而其正弦值就是这两个单位二维向量的叉乘值,不过Unity中只有Vector3有叉乘方法,即Vector3.Cross,Vector2并没有叉乘方法,这里我们自行编写代码,参考如下:

static public float NormalVectorAsin(Vector2 direction1, Vector2 direction2)
{
	return (direction1.x * direction2.y - direction1.y * direction2.x);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值