如何计算两个2D向量之间的角度和正确的转向方向?


26

我正在研究一些运动AI,该运动没有障碍并且运动仅限于XY平面。我正在计算两个向量,v是船1的朝向,w是从船1的位置指向船2的向量。

然后,我使用公式计算这两个向量之间的角度

arccos((v · w) / (|v| · |w|))

我遇到的问题是arccos仅返回0°到180°之间的值。这使得无法确定我应该向左还是向右转以面对另一艘船。

有一个更好的方法吗?


1
如果您使用的是Unity,请签出Mathf.DeltaAngle()
罗素·博罗戈夫

Answers:


22

使用二维叉积会更快。不涉及昂贵的触发功能。

b2Vec2 target( ... );
b2Vec2 heading( ... );

float cross = b2Cross( target, heading );

if( cross == -0.0f )
   // turn around

if( cross == 0.0f )
  // already traveling the right direction

if( cross < 0.0f)
  // turn left

if( cross > 0.0f)
  // turn right

如果仍然需要实际角度,建议使用atan2atan2将为您提供任何矢量的绝对角度。要获取矢量之间的相对角度,请计算它们的绝对角度并使用简单的减法。

b2Vec2 A(...);
b2Vec2 B(...);

float angle_A = std::atan2(A.y,A.x);
float angle_B = B.GetAngle(); // Box2D already figured this out for you.

float angle_from_A_to_B = angle_B-angle_A;
float angle_from_B_to_A = angle_A-angle_B;

1
阅读@Tetrad的答案后,我想您可以将叉积和组合arccos。这样,您将仅使用一个触发功能,但仍具有实际角度。但是,我建议您不要进行此优化,直到您确定AI角度跟踪对游戏的性能产生显着影响。
deft_code 2011年

2
是的,当在向量和角度之间转换时,atan2()绝对是您的朋友。
bluescrn 2011年

1
谢谢!我发现我实际上并不需要角度,抓住2D十字产品足以满足我的需求。
错误454

2
另外,您的if( cross == -0.0f )vs if( cross == 0.0f )支票看起来非常脆弱。
bobobobo 2012年

1
@bobobobo,使用物理引擎,只能选择一个方向并移动可能不是一个选择。魔术般的转弯会导致物理引擎发疯。进一步神奇的旋转看起来对于动画也很可怕。所以是的,您无需左右就可以解决此问题,但是完善的解决方案通常需要这些。
deft_code 2012年

10

使用2D叉积的arcsin(即叉积向量的z分量)。这样您将获得-90到90,这将使您知道是左走还是右走。

请注意,因为A十字B与B十字A不同。

另一种策略(但可能不那么直接)是使用atan2计算两个向量的“航向”,然后找出指向X度的A是否需要向左或向右转到指向y度的B。


感谢您的答复。为了使将来的浏览器更清楚,将2d叉积的量值取反正弦将产生0到90之间的值。以2d叉积的z分量的正弦值将产生所需的结果。
错误454

@错误454,您绝对正确,修正了我的帖子。
四分

1

使用矢量重定向飞船。这就是“转向行为”的工作方式-您无需计算角度,只需使用已有的向量即可。这在计算上要便宜得多。

向量w(从1号船到2号船的向量)是您需要的所有信息。使用的加权版本修改船1的速度矢量或船1的加速度矢量(甚至直接航向矢量)w

在此处输入图片说明

幅度如何遥远船1偏离航线(多么糟糕V不以w匹配)可以通过使用发现(1 - dot(v,w)

  • dot(v,w)最大化时间vw然后精确排列)
  • 1 - dot(v,w))在vw完全对齐,提供vw标准化后给出0。

0

有一种简单的方法可以通过法线几何找到矢量的绝对角度。

例如向量V = 2i-3j;

x系数的绝对值= 2;

y系数的绝对值= 3;

角度= atan(2/3); [角度将在0到90之间]

基于象限的角度将被改变。

如果(x系数<0且y系数> 0),则angle = 180-angle;

如果(x系数<0且y系数<0),则angle = 180 + angle;

如果(x系数> 0且y系数<0),则angle = 360-angle;

如果(x系数> 0且y系数> 0),则angle =角度;

找到第一和第二矢量的角度后,只需从第二矢量减去第一矢量角度即可。然后,您将获得两个向量之间的绝对角度。


5
这正是atan2()函数为您实现的。:)
内森·里德

@NathanReed,是的,但是您不能将这种方法与点乘产品一起使用以避免触发开销吗?
jdk1.0 '18年

0

也许我应该发布一个稍有不同的问题,然后自己回答。这是我所遇到的最接近的问题。

我正在html画布中进行2D工作,其中的旋转角度以弧度而不是矢量为单位。我需要从“当前航向”(h)到“目标航向”(t)的“转角”(ta)。

这是我的解决方案:

var ta = t - h,
    ata = Math.abs(ta)
;
if (ta > Math.PI) ta = (ta / ata) * (Math.PI - ata)
return ta;
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.