Math 在玩家身上打开速率';当它达到圆周率时,旋转360度
使用Golang制作游戏,因为它在游戏中似乎运行得很好。我让玩家总是面对鼠标,但想要一个旋转速度,使某些角色的旋转速度比其他角色慢。以下是它如何计算转弯圆:Math 在玩家身上打开速率';当它达到圆周率时,旋转360度,math,go,triggers,mouse,game-physics,Math,Go,Triggers,Mouse,Game Physics,使用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 pla
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
}
}
func(p*player)handleTurn(赢像素窗口,dt float64){
mouseRad:=math.Atan2(p.pos.Y-win.MousePosition().Y,win.MousePosition().X-p.pos.X)//玩家需要转向面对鼠标的角度
如果mouseRad>p.rotateRad-(p.turnSpeed*dt){
p、 旋转半径+=p.旋转速度*dt
}否则,如果mouseRad
mouseRad是朝向鼠标旋转的弧度,我只是添加旋转率[在本例中,2]
当鼠标到达左侧并穿过中心y轴时,弧度角从-pi变为pi,反之亦然。这将使玩家完成360度的全方位移动
解决这个问题的正确方法是什么?我尝试过将角度设置为绝对值,但它只使其出现在pi和0处[中心y轴上正方形的左侧和右侧]
我附加了一个问题的gif,以提供更好的可视化效果
基本概述:
玩家缓慢地旋转以跟随鼠标,但当角度达到pi时,极性会发生变化,这会导致玩家进行360度[将所有背面计数为相反的极性角度]
编辑:
dt是delta时间,只是对于适当的帧,运动的解耦变化明显
p、 rotateRad从0开始,是一个浮点64
Github回购协议暂时:
你需要建造它![go get it]前言:这个答案假设有一些线性代数、三角学和旋转/变换的知识 您的问题源于旋转角度的使用。由于逆三角函数的不连续性,很难(如果不是完全不可能的话)消除相对接近输入的函数值中的“跳跃”。具体来说,当
x<0
时,atan2(+0,x)=+pi
(其中+0
是非常接近零的正数),但atan2(-0,x)=-pi
。这就是为什么您会遇到导致问题的2*pi
的差异
因此,直接使用向量、旋转矩阵和/或四元数通常更好。他们使用角度作为三角函数的参数,三角函数是连续的,可以消除任何不连续性。在我们的情况下,我们应该这样做
由于您的代码测量鼠标相对位置与对象绝对旋转形成的角度,因此我们的目标归结为旋转对象,使局部轴(1,0)
(=(cos rotateRad,sin rotateRad)
指向鼠标。实际上,我们必须旋转对象,使(cos p.rotateRad,sin p.rotateRad)
等于(win.MousePosition().Y-p.pos.Y,win.MousePosition().X-p.pos.X)。标准化
slerp是如何在这里发挥作用的?考虑到上面的陈述,我们只需从(cos p.rotateRad,sin p.rotateRad)
(由current
表示)到(win.MousePosition().Y-p.pos.Y,win.MousePosition().X-p.pos.X)通过适当的参数,该参数将由转速决定
现在我们已经奠定了基础,我们可以继续实际计算新的旋转。根据slerp公式
slerp(p0, p1; t) = p0 * sin(A * (1-t)) / sin A + p1 * sin (A * t) / sin A
其中A
是单位向量p0
和p1
之间的角度,或cos A=dot(p0,p1)
在我们的例子中,p0==current
和p1==target
。剩下的唯一一件事是计算参数t
,它也可以被视为slerp通过的角度的分数。因为我们知道,在每个时间步,我们将以一个角度旋转,p.turnSpeed*dt
,t=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
我们现在有了计算结果所需的一切。如前所述,cosa=dot(p0,p1)
。类似地,sina=abs(cross(p0,p1))
,其中cross(A,b)=A.X*b.Y-A.Y*b.X
现在是从结果
实际查找旋转的问题。请注意,result=(cos-newRotation,sin-newRotation)
。有两种可能性:
通过p.rotateRad=atan2(result.Y,result.X)
直接计算rotateRad
,或
如果可以访问二维旋转矩阵,只需将旋转矩阵替换为该矩阵即可
|result.X -result.Y|
|result.Y result.X|
事先注意:我下载了您的示例回购协议,并对其应用了我的更改,它运行得非常完美。这里有一段录音:
(为了便于参考,使用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
}
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
}
}