如何使用屏幕坐标上的三角函数计算两点之间的角度?

4
我正在开发一款游戏。这个游戏是俯视图的,实时的,并且必须具备路径规划功能。我的游戏必须计算玩家当前位置和他们点击要走到的位置之间的角度。
问题在于,我使用的是屏幕坐标,即“x向右增加,y向下增加”。
以下是我的一些代码:
package main

import (
  "fmt"
  "math"
)

func main() {
  position1 := &Position{550, 200}
  position2 := &Position{700, 500}
  vector1 := CreatePathVector(position1, position2, 50)

  fmt.Printf("position1: %v\nposition2: %v\n", position1, position2)

  position := position1
  for i := 0; i < 5; i++ {
    position = position.Add(vector1)
    fmt.Printf("next position: %v\n", position)
  }

  position3 := &Position{400, 500}
  position4 := &Position{700, 400}
  vector2 := CreatePathVector(position3, position4, 50)

  fmt.Printf("position3: %v\nposition4: %v\n", position3, position4)

  position = position3
  for i := 0; i < 5; i++ {
    position = position.Add(vector2)
    fmt.Printf("next position: %v\n", position)
  }
}

type Position struct {
  X float64
  Y float64
}

type Vector struct {
  Radians  float64
  Distance float64
}

func CreatePathVector(pos1 *Position, pos2 *Position, speed int) *Vector {
  ydiff := pos2.Y - pos1.Y
  xdiff := pos2.X - pos1.X
  radians := math.Atan2(ydiff, xdiff)

  return &Vector{
    Radians:  radians,
    Distance: float64(speed),
  }
}

func (p *Position) Add(v *Vector) *Position {
  return &Position{
    X: p.X + math.Sin(v.Radians)*v.Distance,
    Y: p.Y + math.Cos(v.Radians)*v.Distance,
  }
}

这里是输出结果

position1: &{550 200}
position2: &{700 500}
next position: &{594.7213595499958 222.3606797749979}
next position: &{639.4427190999916 244.72135954999578}
next position: &{684.1640786499873 267.0820393249937}
next position: &{728.8854381999831 289.44271909999156}
next position: &{773.6067977499789 311.80339887498945}
position3: &{400 500}
position4: &{700 400}
next position: &{384.1886116991581 547.4341649025257}
next position: &{368.37722339831623 594.8683298050514}
next position: &{352.56583509747435 642.3024947075771}
next position: &{336.75444679663246 689.7366596101028}
next position: &{320.9430584957906 737.1708245126285}

正如您所看到的,在这两个示例中,重复添加向量的步骤并不能朝着目的地前进。


1
Y轴向下增加不应该是一个问题!如果y轴向下增加,并且您想要玩家和点击之间的直线与水平线之间的角度,则角度将为math.Atan2(ydiff * -1, xdiff) - alephao
2
@Atomic_alarm:要求用户提供MCVE并不是很有帮助。你需要解释一下这是什么。更好的做法是提供一个链接到Stack Overflow的解释:如何创建一个最小、完整和可验证的示例。 - peterSO
1
一种选择是完全放弃角度,而是以笛卡尔形式表示向量。所以您将拥有一个具有 {xdiff, ydiff} 的向量,而不是 {Radians, Distance}。毕竟,先取反正切,然后取sin/cos只是从笛卡尔转换为极坐标再立即转换回笛卡尔的无用转换。除此之外,您的代码有什么问题?它的哪个部分不起作用? - David Etler
感谢迄今为止的回复。我已经发布了MCVE。 - Zach Taylor
@alaphao 我已经修改了我的程序,使用 math.Atan2(ydiff * -1, xdiff),但这并没有解决问题。如果需要,我可以发布输出结果。 @DavidEtler 我可能会转换为 {xdiff, ydiff} 向量类型。我喜欢那个建议。 - Zach Taylor
@ZachTaylor我觉得你需要检查一些条件(类似 if playerX >= cursorX { angle = atan(dy/dx) + pi )才能按照你的期望工作,但像@DavidEtler说的那样,使用向量可能更好。 - alephao
1个回答

3
以下是您选择使用笛卡尔坐标系的代码,就像我在评论中建议的那样:
package main

import (
  "fmt"
  "math"
)

func main() {
  position1 := &Position{550, 200}
  position2 := &Position{700, 500}
  vector1 := CreatePathVector(position1, position2, 70)

  fmt.Printf("position1: %v\nposition2: %v\n", position1, position2)

  position := position1
  for i := 0; i < 5; i++ {
    position = position.Add(vector1)
    fmt.Printf("next position: %v\n", position)
  }

  position3 := &Position{400, 500}
  position4 := &Position{700, 400}
  vector2 := CreatePathVector(position3, position4, 50)

  fmt.Printf("position3: %v\nposition4: %v\n", position3, position4)

  position = position3
  for i := 0; i < 5; i++ {
    position = position.Add(vector2)
    fmt.Printf("next position: %v\n", position)
  }
}

type Position struct {
  X float64
  Y float64
}

type Vector struct {
  dX  float64
  dY float64
}

func CreatePathVector(pos1 *Position, pos2 *Position, speed int) *Vector {
  ydiff := pos2.Y - pos1.Y
  xdiff := pos2.X - pos1.X
  mag := math.Sqrt(xdiff*xdiff+ydiff*ydiff)


  return &Vector{
    dX:  xdiff/mag*float64(speed),
    dY:  ydiff/mag*float64(speed),
  }
}

func (p *Position) Add(v *Vector) *Position {
  return &Position{
    X: p.X + v.dX,
    Y: p.Y + v.dY,
  }
}

如果你想使用角度,请在“Add”中交换“Cos”和“Sin”。这是因为屏幕的方向并不重要:如果你使用“t = arctan(y/x)”你可以从“sin(t)”获得“y”,从“cos(t)”获得“x”,无论“x”和“y”代表什么。因此,“Add”应该是这样的:
func (p *Position) Add(v *Vector) *Position {
  return &Position{
    X: p.X + math.Cos(v.Radians)*v.Distance,
    Y: p.Y + math.Sin(v.Radians)*v.Distance,
  }
}

我之前也曾制作过小游戏,尝试使用角度进行移动。我的建议是不要尝试。如果你想在游戏中添加更加逼真的物理效果,向量和线性代数将成为你最好的朋友。在我看来,角度和三角函数会变得太过混乱。


1
我已经完成了这个更改,并且它的工作效果符合我的预期。特别感谢您提供的代码片段,用于创建包括移动速度在内的适当大小的向量。 - Zach Taylor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接