【问题标题】:Turn rate on a player's rotation doing a 360 once it hits pi一旦击中 pi,玩家的旋转速度就会进行 360 度旋转
【发布时间】:2018-04-08 05:45:12
【问题描述】:

使用 Golang 制作游戏,因为它似乎非常适合游戏。我让玩家总是面对鼠标,但想要一个转身速度来让某些角色比其他角色转得慢。以下是它计算转弯圆的方法:

func (p *player) handleTurn(win pixelgl.Window, dt float64) {
    mouseRad := math.Atan2(p.pos.Y-win.MousePosition().Y, win.MousePosition().X-p.pos.X) // the angle the player needs to turn to face the mouse
    if mouseRad > p.rotateRad-(p.turnSpeed*dt) {
        p.rotateRad += p.turnSpeed * dt
    } else if mouseRad < p.rotateRad+(p.turnSpeed*dt) {
        p.rotateRad -= p.turnSpeed * dt
    }
}

mouseRad 是转弯面对鼠标的弧度,我只是添加转弯速率 [在本例中为 2]。

当鼠标到达左侧并穿过中心 y 轴时,弧度角从 -pi 变为 pi,反之亦然。这会导致玩家进行完整的 360 度全景拍摄。

解决此问题的正确方法是什么?我尝试将角度设为绝对值,但它只发生在 pi 和 0 [y 轴中心正方形的左侧 右侧]。

我附上了问题的 gif 以提供更好的可视化效果。

基本总结:

玩家慢慢旋转跟随鼠标,但当角度达到 pi 时,它会改变极性,导致玩家做一个 360 度 [将所有的返回计数到相反的极性角度]。

编辑: dt 是增量时间,显然只是为了在运动中进行适当的帧解耦变化

p.rotateRad 从 0 开始,是一个 float64。

Github repo 临时:here

您需要this library 来构建它! [去拿吧]

【问题讨论】:

  • 有什么方法可以把它推送到一个仓库,这样我就可以记录一些东西来调试和测试?
  • @Ron 是的,给我一分钟。它显然使用了 Go,还有库 github.com/faiface/pixel——去搞定吧
  • @Ron 添加到主帖
  • 谢谢,我知道有人已经回答了,但我还是要下载并检查一下。我之前没有使用过任何可视化库,我想弄乱它。

标签: math go triggers mouse game-physics


【解决方案1】:

事先注意:我下载了你的示例 repo 并将我的更改应用到它上面,它完美地工作。这是它的录音:

(供参考,用byzanz录制的GIF)


一个简单的解决方案是不比较角度mouseRad 和更改后的p.rotateRad),而是计算并“标准化”差异所以它在-Pi..Pi 的范围内。然后您可以根据差异的符号(负或正)来决定转向哪条路

“归一化”一个角度可以通过加/减2*Pi直到它落在-Pi..Pi范围内来实现。加/减2*Pi 不会改变角度,因为2*Pi 正好是一个完整的圆。

这是一个简单的归一化函数:

func normalize(x float64) float64 {
    for ; x < -math.Pi; x += 2 * math.Pi {
    }
    for ; x > math.Pi; x -= 2 * math.Pi {
    }
    return x
}

并像这样在您的handleTurn() 中使用它:

func (p *player) handleTurn(win pixelglWindow, dt float64) {
    // the angle the player needs to turn to face the mouse:
    mouseRad := math.Atan2(p.pos.Y-win.MousePosition().Y,
        win.MousePosition().X-p.pos.X)

    if normalize(mouseRad-p.rotateRad-(p.turnSpeed*dt)) > 0 {
        p.rotateRad += p.turnSpeed * dt
    } else if normalize(mouseRad-p.rotateRad+(p.turnSpeed*dt)) < 0 {
        p.rotateRad -= p.turnSpeed * dt
    }
}

您可以在这个有效的Go Playground 演示中使用它。

请注意,如果您存储归一化的角度(在-Pi..Pi 范围内),normalize() 函数中的循环将最多进行 1 次迭代,所以这会非常快。显然你不想存储像100*Pi + 0.1 这样的角度,因为它与0.1 相同。 normalize() 会在这两个输入角度下产生正确的结果,而前者的循环将有 50 次迭代,而后者的循环将有 0 次迭代。

还请注意,normalize() 可以通过使用类似于整数除法和余数的浮点运算来针对“大”角度进行优化,但如果您坚持归一化或“小”角度,这个版本实际上更快。

【讨论】:

  • 谢谢你,因为我现在只使用“小”角度,这个答案是完美的。解释也很好,效果很好!
  • @HKVariant 不要误会,我的解决方案适用于所有角度(不仅仅是“小”角度),您甚至可能不会注意到性能差异。我只是说对于“大”角度,归一化可能更有效。详情请见thisthis
【解决方案2】:

前言:此答案假定您具备一定的线性代数、三角学和旋转/变换知识。

您的问题源于旋转角度的使用。由于反三角函数的不连续性,很难(如果不是完全不可能的话)消除相对接近输入的函数值的“跳跃”。具体来说,当x &lt; 0atan2(+0, x) = +pi(其中+0 是一个非常接近于零的正数),但是atan2(-0, x) = -pi。这正是您遇到2 * pi 差异的原因,这会导致您的问题。

因此,直接使用向量、旋转矩阵和/或四元数通常会更好。他们使用角度作为三角函数的参数,三角函数是连续的并且消除了任何不连续性。在我们的例子中,spherical linear interpolation (slerp) 应该可以解决问题。

由于您的代码测量鼠标的相对位置与对象的绝对旋转形成的角度,我们的目标归结为旋转对象,使得 local(1, 0) (@ world 空间中的 987654329@)指向鼠标。实际上,我们必须旋转对象,使(cos p.rotateRad, sin p.rotateRad) 等于(win.MousePosition().Y - p.pos.Y, win.MousePosition().X - p.pos.X).normalized

slerp 是如何在这里发挥作用的?考虑到上述陈述,我们只需将slerp geometrically(cos p.rotateRad, sin p.rotateRad)(由current表示)到(win.MousePosition().Y - p.pos.Y, win.MousePosition().X - p.pos.X).normalized(由target表示)通过一个适当的参数来确定,该参数将由旋转速度决定。

现在我们已经奠定了基础,我们可以继续实际计算新的旋转。根据slerp公式,

slerp(p0, p1; t) = p0 * sin(A * (1-t)) / sin A + p1 * sin (A * t) / sin A

其中A 是单位向量p0p1cos A = dot(p0, p1) 之间的角度。

在我们的例子中,p0 == currentp1 == target。唯一剩下的就是参数t的计算,也可以认为是通过的角度的分数。因为我们知道我们将在每个时间步旋转一个角度p.turnSpeed * dtt = p.turnSpeed * dt / A。代入t的值后,我们的slerp公式就变成了

p0 * sin(A - p.turnSpeed * dt) / sin A + p1 * sin (p.turnSpeed * dt) / sin A

为了避免使用acos 计算A,我们可以使用sin 的复合角度公式来进一步简化这个过程。请注意,slerp 操作的结果存储在result 中。

result = p0 * (cos(p.turnSpeed * dt) - sin(p.turnSpeed * dt) * cos A / sin A) + p1 * sin(p.turnSpeed * dt) / sin A

我们现在拥有计算result 所需的一切。如前所述,cos A = dot(p0, p1)。同样,sin A = abs(cross(p0, p1)),其中cross(a, b) = a.X * b.Y - a.Y * b.X

现在是从result 实际找到旋转的问题。请注意result = (cos newRotation, sin newRotation)。有两种可能:

  1. 通过p.rotateRad = atan2(result.Y, result.X)直接计算rotateRad,或者
  2. 如果您可以访问二维旋转矩阵,只需将旋转矩阵替换为矩阵

    |result.X -result.Y|
    |result.Y  result.X|
    

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-30
    • 1970-01-01
    相关资源
    最近更新 更多