计算将箭头旋转到令牌的最小角度
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_x
和 dist_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
该解决方案比我预期的要简单得多,但效果很好并且很有意义。
感谢大家的帮助和想法!
我正在开发一个平台游戏,我希望有一个指向令牌的箭头:
只需将箭头对准令牌即可 - 一切正常:
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_x
和 dist_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
该解决方案比我预期的要简单得多,但效果很好并且很有意义。
感谢大家的帮助和想法!