计算将箭头旋转到令牌的最小角度

Calculate minimum angle to rotate arrow to token

我正在开发一个平台游戏,我希望有一个指向令牌的箭头:

只需将箭头对准令牌即可 - 一切正常:

const dist_x = player.x - token.x
const dist_y = player.y - token.y
const angle = Math.atan2(dist_y, dist_x)

arrow.angle = angle

但问题是我希望箭头围绕令牌摆动,如下所示。它工作正常,但是当箭头的角度接近 360 度并且令牌的角度接近 0 时,代码认为箭头需要一直摆动到零 - 而不是只是加起来一点点并且很好循环。这是我的代码:

const dist_x = player.x - token.x
const dist_y = player.y - token.y
const angle = Math.atan2(dist_y, dist_x) // angle between arrow & token

arrow.angle += arrow.rot_speed // acceleration
arrow.rot_speed *= .9 // slowly calm down

// distance between goal angle of arrow and angle at the moment
const angle_dist = angle - arrow.angle

// move in that direction
arrow.rot_speed += angle_dist

这个问题多次成为我的阻碍,所以我将不胜感激。

要解决这样的问题,您需要检查您摆动的角度差是否低于 180,如果不是,则将您摆动的角度加上 360。我认为这将修复它会摆动的部分。我不知道你是如何在你的代码中实现它的,但这是我在以前的经验中遇到的一般解决方案。

当然,您需要将所有这些都转换成弧度:D

我不确定这是否有帮助。假设有 dist_xdist_y 的值用于使用 atan2() 返回角度,如下所示

N = 8
r = 1
console.log("angle dist_x dist_y atan2 angle")
for(i = 0; i < N; i++)  {
  q = (i / 8) * 2 * Math.PI
  dist_x = r * Math.cos(q)
  dist_y = r * Math.sin(q)
  theta = Math.atan2(dist_y, dist_x)
  theta2 = theta
  if(q > Math.PI) {
    theta2 += 2 * Math.PI
  }
  console.log(
    q.toFixed(3),
    dist_x.toFixed(3),
    dist_y.toFixed(3),
    theta.toFixed(3),
    theta2.toFixed(3)
  )
}

产生

angle   dist_x  dist_y  atan2  angle
0.000   1.000   0.000   0.000  0.000
0.785   0.707   0.707   0.785  0.785
1.571   0.000   1.000   1.571  1.571
2.356  -0.707   0.707   2.356  2.356
3.142  -1.000   0.000   3.142  3.142
3.927  -0.707  -0.707  -2.356  3.927   <---
4.712  -0.000  -1.000  -1.571  4.712   <---
5.498   0.707  -0.707  -0.785  5.498   <---

显示结果在第 3 和第 4 象限

3rd quadrant: dist_x < 0, dist_y < 0
4rd quadrant: dist_x > 0, dist_y < 0

必须加上 2 PI 以获得 0 和 2 PI 之间的所有角度。

编辑
给定代码的可能实现

..
const angle = Math.atan2(dist_y, dist_x) // angle between arrow & token

/* 
result of atan2():
dist_x > 0, dist_y > 0 ==>    0  < angle <  PI/2  (1st quadrant)
dist_x < 0, dist_y > 0 ==>  PI/2 < angle <  PI    (2nd quadrant)
dist_x < 0, dist_y < 0 ==> -PI   < angle < -PI/2  (3rd quadrant)
dist_x > 0, dist_y < 0 ==> -PI/2 < angle <  0     (4th quadrant)
*/

// Map all angles from (-PI, PI) to (0, 2 PI)
if(dist_y < 0) angle += 2 * Math.PI

谢谢@Aquil Contractor 的回答,我试了一下代码,这是我想出的解决方案:

const dist_x = player.x - token.x
const dist_y = player.y - token.y

let angle = Math.atan2(dist_y, dist_x) // angle between arrow & token

const quarter = Math.PI / 2 // equivalent to 90° (I just thought I'd mention)

/* if the distance between the angles is greater
than 180°, bring the goal angle toward the current. */
if (arrow.angle > quarter && angle < -quarter) angle += Math.PI * 2
else if (arrow.angle < -quarter && angle > quarter) angle -= Math.PI * 2

arrow.angle += arrow.rot_speed
arrow.rot_speed *= .9
arrow.rot_speed += angle - arrow.angle

该解决方案比我预期的要简单得多,但效果很好并且很有意义。

感谢大家的帮助和想法!