Math 基于两个3D矢量计算目标角度?

Math 基于两个3D矢量计算目标角度?,math,vector,3d,angle,Math,Vector,3d,Angle,我用Ruby编写此函数是为了找到两个2D(x,y)向量之间的目标角度,但现在我想了解如何以类似的方式在3D中执行此操作: def target_angle(point1, point2) x1 = point1[0] y1 = point1[1] x2 = point2[0] y2 = point2[1] delta_x = x2 - x1 delta_y = y2 - y1 return Math.atan2(delta_y, delta

我用Ruby编写此函数是为了找到两个2D(x,y)向量之间的目标角度,但现在我想了解如何以类似的方式在3D中执行此操作:

def target_angle(point1, point2)
    x1 = point1[0]
    y1 = point1[1]
    x2 = point2[0]
    y2 = point2[1]
    delta_x = x2 - x1
    delta_y = y2 - y1
    return Math.atan2(delta_y, delta_x)
  end
给定一个对象(如本例中的子弹),我可以在给定播放器(x,y)和鼠标(x,y)之间的目标_角度的情况下拍摄该对象,如子弹更新功能:

  def update
    wall_collision
    # the angle here is the target angle where point 1 is the player and
    # point 2 is the mouse
    @x += Math.cos(angle)*speed
    @y += Math.sin(angle)*speed
  end

是否有类似的方法来计算3D中的目标角度,并以类似的方式使用该角度作为我的更新功能(在3D中射击子弹)?我怎样才能在两个3D向量(x,y,z)上实现这一点,其中玩家的位置(x,y,z)和其他任意3D点远离玩家。

我最近发布了一个向量/矩阵gem,虽然它是用C编写的,但我会尝试将其转换为纯Ruby

实际上,有几种不同的方法可以计算三维空间中两个向量之间的角度。使用典型的
acos
函数是常见的,但当坡度接近+/-1.0时,会出现较大的精度问题,因此最好使用角平分线进行计算。使用
acos
,即使是十字积的轻微精度误差也会导致角度的更大误差,所以经过一些研究,我发现这种方法始终是一致的

给定定义为
v1
v2
的两个向量:

# Normalize vectors
inv = 1.0 / Math.sqrt(v1.x * v1.x + v1.y * v1.y + v1.z * v1.z)
n1 = Vector.new(v1.x * inv, v1.y * inv, v1.z * inv)

inv = 1.0 / Math.sqrt(v2.x * v2.x + v2.y * v2.y + v2.z * v2.z)
n2 = Vector.new(v2.x * inv, v2.y * inv, v2.z * inv)

ratio = n1.x * n2.x + n1.y * n2.y + n1.z * n2.z


if ratio < 0.0
  x = -n1.x - n2.x
  y = -n1.y - n2.y
  z = -n1.z - n2.z
  length = Math.sqrt(x * x + y * y + z * z)
  theta = Math::PI - 2.0 * Math.asin(length / 2.0)
else
  x = n1.x - n2.x
  y = n1.y - n2.y
  z = n1.z - n2.z
  length = Math.sqrt(x * x + y * y + z * z)
  theta = 2.0 * asin(length / 2.0)
end

# Convert from radians to degrees
angle = theta * (180.0 / Math::PI)
#规范化向量
inv=1.0/Math.sqrt(v1.x*v1.x+v1.y*v1.y+v1.z*v1.z)
n1=新向量(v1.x*inv,v1.y*inv,v1.z*inv)
inv=1.0/Math.sqrt(v2.x*v2.x+v2.y*v2.y+v2.z*v2.z)
n2=新向量(v2.x*inv,v2.y*inv,v2.z*inv)
比率=n1.x*n2.x+n1.y*n2.y+n1.z*n2.z
如果比率<0.0
x=-n1.x-n2.x
y=-n1.y-n2.y
z=-n1.z-n2.z
长度=数学sqrt(x*x+y*y+z*z)
θ=数学::π-2.0*Math.asin(长度/2.0)
其他的
x=n1.x-n2.x
y=n1.y-n2.y
z=n1.z-n2.z
长度=数学sqrt(x*x+y*y+z*z)
θ=2.0*asin(长度/2.0)
结束
#从弧度转换为度
角度=θ*(180.0/数学::π)
我没有运行/测试这段代码,我不确定您使用的是什么确切的向量实现,我只是假设一个对象具有
x
y
、和
z
值作为图示点。可能需要做一些小的改进,例如乘以
0.5
而不是除以
2.0
,因为除法速度较慢,但基本前提应该会有所帮助

我从一个C项目中转换了这段代码,因此如果您对其他向量函数感兴趣,请参阅它(无耻的自我宣传)。

[伪代码]

两个向量的点积与它们之间夹角的余弦有关

COS(angle) = dot(a,b)/( |a|*|b| ) 
SIN(angle) = | cross(a,b) |/( |a|*|b| )
两个向量的叉积与它们之间角度的正弦有关

COS(angle) = dot(a,b)/( |a|*|b| ) 
SIN(angle) = | cross(a,b) |/( |a|*|b| )
所以切线就是正弦和余弦的比值(分母相互抵消)

注意
atan2(Δy,Δx)
的约定

最后,定义以下函数

magnitude(c) = sqrt(c.x^2+c.y^2+c.z^2)
dot(a,b) = a.x*b.x + a.y*b.y + a.z*b.z
cross(a,b) = [ a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a>x*b.y - a.y*b.x ]

太棒了,你出版了一本宝石!一旦你有了角度,我也有了一个更新函数,有没有一种方法可以使子弹的z位置朝着点增加,就像x和y一样?或者有其他方法可以做到这一点吗?因为子弹是直线运动的,你可以简单地计算一次,然后你就可以计算出它的轨迹,因为它不会(不应该?)改变。任何变化,如玩家移动等,都将由你的世界矩阵处理。是的,当子弹产生时,角度只计算一次。我的意思是,如何使用子弹的x,y,z位置使子弹朝着点移动?我的意思是,在我上面的更新函数中是否有类似的方法,但也有z?类似于
m=Math.sqrt(x1*x2+y1*y2+z1*z2)
计算距离,您可以使用它(在标准化向量上)来改变位置/速度。这些需要用你的世界/模型/投影矩阵来计算,因为它们还取决于玩家的速度/方向、相机等。如果你的“宝石”中包含If语句,那么你就做错了。当点积(您称之为比率)为负时,角度大于90°,atan2()函数非常擅长处理此类情况。此外,分割是多余的,因为它抵消了。它只是为了让你的代码慢下来。可能重复的是3D而不是2D@JA72同样的方法。将点积和叉积的大小放入
atan2()