问题描述
我的问题是关于在2D中计算两个向量之间的最小角度的方向。我在C ++中制作一个游戏,其中一个障碍是寻找热导弹发射器。我有它的工作通过计算目标和子弹之间的矢量,规范化矢量,然后乘以它的速度。然而,我现在回到这个班,以使它更好。而不是立即锁定到播放器我想要它只有当子弹矢量在一定角度(子弹矢量和矢量bulletloc->目标之间的角度)时才这样做。否则,我想让它慢慢地朝目标平移一度,这样给玩家足够的空间,以避免它。我做了所有这一切(在一个vb.net项目,所以我可以简化的问题,工作,然后重写在C + +)。然而,子弹总是顺时针朝目标旋转,即使最快的路线是逆时针。所以问题是工作的方向应用旋转,所以最小的角度被覆盖。这是我的代码,所以你可以试试看看我的描述:
函数Rotate(ByVal a As Double,ByVal tp As
点bir方向到目标点cp =目标点cp =当前点cv =当前向量'
Dim dir As RotDir'direction to转向'
Dim tv As Point'目标向量cp-> tp'
Dim d As point'目标点(d)= cp +向量'
Dim normal As point
Dim x1 As Double
Dim y1 As Double
Dim VeritcleResolution As Integer = 600
tp.Y = VeritcleResolution - tp.Y'修改y零件在平面中与原点存在(0,0)在左下角'
cp.Y = VeritcleResolution - cp.Y
cv.Y = cv.Y * -1
tv.X = tp。 X-cp.X'work out cp - > tp'
tv.Y = tp.Y - cp.Y
'计算顶点与目标之间的角度,并计算角度'
Dim tempx As Double
Dim tempy As Double
tempx = cv.X * tv.X
tempy = cv.Y * tv.Y
Dim DotProduct As Double
DotProduct = tempx + tempy'cp->的点积d和cp-> tp'
Dim magCV As double'电流矢量的大小
Dim magTV As double'目标矢量的大小
magCV = Math.Sqrt
magTV = Math.Sqrt(Math.Pow(tv.X,2))+ Math.Pow(cv.Y,2) )
Dim VectorAngle As Double
VectorAngle = Acos(DotProduct /(magCV * magTV))
VectorAngle = VectorAngle * 180 / PI' > d和cp-> tp'
如果VectorAngle<如果角度足够小,直接转换到目标'
cv =新点(tp.X - cp.X,tp.Y - cp.Y)
magCV = Math.Sqrt( cv.X ^ 2)+(cv.Y ^ 2))
如果magCV = 0则
x1 = 0
y1 = 0
否则
x1 = cv.X / magCV
y1 = cv.Y / magCV
结束如果
normal =新点(x1 * 35,y1 * 35)
normal.Y = normal.Y * -1
cv = normal
ElseIf VectorAngle> a然后'否则平滑地转向目标'
Dim x As Single
d =新点(cp.X + cv.X,cp.Y + cv.Y)
a =(a * -1)* PI / 180'此线控制方向a =(a * -1)* PI / 180将使逆时针旋转'
' (dX * Cos(a)) - (dY * Sin(a))
dX - = cp.X
dY - = cp.Y
b $ b dY =(dX * Sin(a))+(dY * Cos(a))
dX + = cp.X
dY + = cp.Y
cv.X = dX - cp.X
cv.Y = dY - cp.Y
cv.Y = cv.Y * -1
结束If
返回cv
结束函数
想法我已经是出了两个向量的方位,如果差异大于180度,顺时针旋转,否则逆时针旋转,任何想法将有所帮助。感谢。
编辑:我想补充一点,这个网站是非常有帮助的。我经常使用他人提出的问题来解决我自己的问题,我想借此机会说谢谢。
在你的代码中写入,两个(规范化)向量之间的角度是其点积的反余弦。
要获得角度,你可以使用第三个向量来表示其他两个向量所在的平面的法线 - 在你的2D情况下,这将是一个直接向上指向(0,0,1)的3D向量。 / p>
然后,取第一个向量(你希望角度相对于的那个)与第二个向量的交叉乘积(注意叉积不是交换的) 。
在代码中(C#,对不起) - 注意所有向量都假定为规范化:
public static double AngleTo(this Vector3 source,Vector3 dest)
{
if(source == dest){
return 0;
}
double dot; Vector3.Dot(ref source,ref dest,out dot);
return Math.Acos(dot);
}
public static double SignedAngleTo(Vector3 source,Vector3 dest,Vector3 planeNormal)
{
var angle = source.AngleTo(dest);
Vector3 cross; Vector3.Cross(ref source,ref dest,out cross);
double dot; Vector3Dot(ref cross,ref planeNormal,out dot);
return dot< 0? -angle:angle;
}
这通过利用两个向量之间的叉积产生第三矢量垂直于(正交于)由前两个所定义的平面(因此其固有地是3D操作)。 axb
= - (bxa)
,所以矢量将始终垂直于平面,在 a
和 b
(有一些名为)。
所以交叉产品给了我们一个当垂直于当向量之间的角度通过180°时改变方向的平面。如果我们预先知道垂直于指向直线的平面的向量,则我们可以通过检查它们的点的符号来判断叉积是否与该平面法线相同的方向产品。
my question is regarding working out the direction of the smallest angle between two vectors in 2D. I am making a game in C++ where one of the obstacles is a heat seeking missile launcher. I have it working by calculating the vector between the target and bullet, normalising the vector and then multiplying it by its speed. However, I am now coming back to this class to make it better. Instead of instantly locking onto the player I want it to only do so only when the bullets vector is within a certain angle (the angle between the bullets vector and the vector bulletloc->target). Otherwise I want it to slowly pan towards the target by a degrees thus giving the player enough space to avoid it. I have done all this (in a vb.net project so i could simplify the problem, work it out then re write in in C++). However the bullet always rotates clockwise towards the target even if the quickest route would be counter clockwise. So the problem is working out the direction to apply the rotation in so the smallest angle is covered. Here is my code so you can try and see what I am describing:
Function Rotate(ByVal a As Double, ByVal tp As Point, ByVal cp As Point, ByVal cv As Point)
'params a = angle, tp = target point, cp = current point, cv = current vector of bullet'
Dim dir As RotDir 'direction to turn in'
Dim tv As Point 'target vector cp->tp'
Dim d As Point 'destination point (d) = cp + vector'
Dim normal As Point
Dim x1 As Double
Dim y1 As Double
Dim VeritcleResolution As Integer = 600
tp.Y = VeritcleResolution - tp.Y 'modify y parts to exist in plane with origin (0,0) in bottom left'
cp.Y = VeritcleResolution - cp.Y
cv.Y = cv.Y * -1
tv.X = tp.X - cp.X 'work out cp -> tp'
tv.Y = tp.Y - cp.Y
'calculate angle between vertor to target and vecrot currntly engaed on'
Dim tempx As Double
Dim tempy As Double
tempx = cv.X * tv.X
tempy = cv.Y * tv.Y
Dim DotProduct As Double
DotProduct = tempx + tempy 'dot product of cp-> d and cp -> tp'
Dim magCV As Double 'magnitude of current vector'
Dim magTV As Double 'magnitude of target vector'
magCV = Math.Sqrt(Math.Pow(cv.X, 2) + Math.Pow(cv.Y, 2))
magTV = Math.Sqrt(Math.Pow(tv.X, 2) + Math.Pow(tv.Y, 2))
Dim VectorAngle As Double
VectorAngle = Acos(DotProduct / (magCV * magTV))
VectorAngle = VectorAngle * 180 / PI 'angle between cp->d and cp->tp'
If VectorAngle < a Then 'if the angle is small enough translate directly towards target'
cv = New Point(tp.X - cp.X, tp.Y - cp.Y)
magCV = Math.Sqrt((cv.X ^ 2) + (cv.Y ^ 2))
If magCV = 0 Then
x1 = 0
y1 = 0
Else
x1 = cv.X / magCV
y1 = cv.Y / magCV
End If
normal = New Point(x1 * 35, y1 * 35)
normal.Y = normal.Y * -1
cv = normal
ElseIf VectorAngle > a Then 'otherwise smootly translate towards the target'
Dim x As Single
d = New Point(cp.X + cv.X, cp.Y + cv.Y)
a = (a * -1) * PI / 180 'THIS LINE CONTROL DIRECTION a = (a*-1) * PI / 180 would make the rotation counter clockwise'
'rotate the point'
d.X -= cp.X
d.Y -= cp.Y
d.X = (d.X * Cos(a)) - (d.Y * Sin(a))
d.Y = (d.X * Sin(a)) + (d.Y * Cos(a))
d.X += cp.X
d.Y += cp.Y
cv.X = d.X - cp.X
cv.Y = d.Y - cp.Y
cv.Y = cv.Y * -1
End If
Return cv
End Function
One idea I had was to work out the bearing of the two vectors and if the difference is greater than 180 degrees, rotate clockwise otherwise rotate counter clockwise, any ideas would be helpful. Thanks.
EDIT: I would like to add that this site is very helpful. I often use questions posed by others to solve my own problems and I want to take the chance to say thanks.
As you've written in your code, the angle between two (normalized) vectors is the inverse cosine of their dot product.
To get a signed angle, you can use a third vector representing the normal of the plane that the other two vectors lie on -- in your 2D case, this would be a 3D vector pointing straight "up", say (0, 0, 1).
Then, take the cross-product of the first vector (the one you want the angle to be relative to) with the second vector (note cross-product is not commutative). The sign of the angle should be the same as the sign of the dot product between the resulting vector and the plane normal.
In code (C#, sorry) -- note all vectors are assumed to be normalized:
public static double AngleTo(this Vector3 source, Vector3 dest)
{
if (source == dest) {
return 0;
}
double dot; Vector3.Dot(ref source, ref dest, out dot);
return Math.Acos(dot);
}
public static double SignedAngleTo(this Vector3 source, Vector3 dest, Vector3 planeNormal)
{
var angle = source.AngleTo(dest);
Vector3 cross; Vector3.Cross(ref source, ref dest, out cross);
double dot; Vector3.Dot(ref cross, ref planeNormal, out dot);
return dot < 0 ? -angle : angle;
}
This works by taking advantage of the fact that the cross product between two vectors yields a third vector which is perpendicular (normal) to the plane defined by the first two (so it's inherently a 3D operation). a x b
= -(b x a)
, so the vector will always be perpendicular to the plane, but on a different side depending on the (signed) angle between a
and b
(there's something called the right-hand rule).
So the cross product gives us a signed vector perpendicular to the plane which changes direction when the angle between the vectors passes 180°. If we know in advance a vector perpendicular to the plane which is pointing straight up, then we can tell whether the cross product is in the same direction as that plane normal or not by checking the sign of their dot product.
这篇关于两个向量之间的最短旋转方向的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!