我试图将帆船移动到我用鼠标单击的位置。该运动应该是现实的(船在其后方的桨桨桨),因此,如果鼠标左键单击,并且在船的前方,则该船之后应以弯曲的路径向那里移动,以便正确旋转
我试图将帆船移动到我用鼠标单击的位置。该运动应该是现实的(船在其后方的桨桨桨),因此,如果鼠标左键单击,并且在船的前方,则该船之后应以弯曲的路径向那里移动,以便正确旋转
Answers:
看到这个页面
增加逼真的转弯
下一步是为我们的单元添加逼真的弯曲转弯,以使它们似乎不会在每次需要转弯时都突然改变方向。一个简单的解决方案包括使用样条曲线将陡峭的拐角平滑成转弯。尽管这解决了一些美学上的问题,但对于大多数单元而言,仍然导致物理上非常不现实的运动。例如,它可能会将水箱的突然转弯变成紧密的曲线,但弯曲的转弯仍会比水箱的实际作用更紧。
为了获得更好的解决方案,我们首先需要知道的是设备的转弯半径。转弯半径是一个非常简单的概念:如果您在车内的大型停车场中,将车轮向左旋转至尽可能远的距离并继续行驶一圈,则该转弯的半径就是您的转弯半径。大众甲壳虫的转弯半径将大大小于大型SUV的转弯半径,而人的转弯半径将大大小于笨拙的大型熊的转弯半径。
假设您在某个点(原点)并指向某个方向,并且您需要到达其他点(目标),如图5所示。最短的路径可以通过向左转而找到可以绕圈走直到您直接指向目的地,然后继续前进,或者右转并做同样的事情。
在图5中,最短的路线显然是底部的绿线。由于某些几何关系,该路径的计算非常简单,如图6所示。
首先,我们计算点P的位置,该点是我们的转弯圆的中心,并且始终是距起点的半径r。如果我们从初始方向向右转,则意味着P与原点成一个(initial_direction-90)角,因此:
angleToP = initial_direction - 90 P.x = Origin.x + r * cos(angleToP) P.y = Origin.y + r * sin(angleToP)
既然我们知道了中心点P的位置,我们就可以计算出从P到目的地的距离,如图中的h所示:
dx = Destination.x - P.x dy = Destination.y - P.y h = sqrt(dx*dx + dy*dy)
在这一点上,我们还想检查目的地是否在圆圈内,因为如果目的地不在,我们将永远无法到达目的地:
if (h < r) return false
现在我们可以计算段d的长度,因为我们已经知道了直角三角形另外两个边的长度,即h和r。我们还可以从直角三角形关系确定角度:
d = sqrt(h*h - r*r) theta = arccos(r / h)
最后,要找出要离开圆并从直线开始的点Q,我们需要知道总角度+,并且很容易确定为从P到目的地的角度:
phi = arctan(dy / dx) [offset to the correct quadrant] Q.x = P.x + r * cos(phi + theta) Q.y = P.y + r * sin(phi + theta)
以上计算代表了右转的路径。可以用完全相同的方式计算左侧路径,除了我们在initial_direction加90以计算angleToP,然后使用-代替+之外。在计算完两者之后,我们只需查看哪个路径较短并使用该路径即可。
在我们对该算法及其后继算法的实现中,我们利用了一种数据结构,该数据结构最多存储四个不同的“线段”,每个线段可以是直线或曲线。对于此处描述的弯曲路径,仅使用两个部分:弧线和直线。数据结构包含一些成员,这些成员指定该段是弧线还是直线,段的长度及其起始位置。如果线段是直线,则数据结构还指定角度;对于圆弧,它指定圆心,圆弧的起始角度以及圆弧覆盖的总弧度。
一旦我们计算出了到达两点之间所需的弯曲路径,就可以轻松地计算出在任何给定时刻的位置和方向,如清单2所示。
清单2.计算特定时间的位置和方向。
distance = unit_speed * elapsed_time loop i = 0 to 3: if (distance < LineSegment[i].length) // Unit is somewhere on this line segment if LineSegment[i] is an arc //determine current angle on arc (theta) by adding or //subtracting (distance / r) to the starting angle //depending on whether turning to the left or right position.x = LineSegment[i].center.x + r*cos(theta) position.y = LineSegment[i].center.y + r*sin(theta) //determine current direction (direction) by adding or //subtracting 90 to theta, depending on left/right else position.x = LineSegment[i].start.x + distance * cos(LineSegment[i].line_angle) position.y = LineSegment[i].start.y + distance * sin(LineSegment[i].line_angle) direction = theta break out of loop else distance = distance - LineSegment[i].length
作为简单的解决方案,正如我在评论中所说,您可以尝试以下方法:
考虑一个阶段,您将船指向目标方向,在该阶段中,您对船ip进行了旋转但也进行了向前运动。当飞船已经面向目标时,您可以应用全速前进。我在love2d中安排了一个测试,在这里按照ship更新方法进行。
turnAngSpeed = 0.4 --direction changing speed
ForwordSpeed = 40 -- full forward speed
turnForwordSpeed = ForwordSpeed *0.6 -- forward speed while turning
function ent:update(dt)
dir = getVec2(self.tx-self.x,self.ty-self.y) -- ship --> target direction (vec2)
dir = dir.normalize(dir) --normalized
a= dir:angle() - self.forward:angle() --angle between target direction e current forward ship vector
if (a<0) then
a=a+math.pi *2 -- some workaround to have all positive values
end
if a > 0.05 then -- if angle difference
if a < math.pi then
--turn right
self.forward = vec2.rotate(self.forward,getVec2(0,0),turnAngSpeed * dt)
else
--turn left
self.forward = vec2.rotate(self.forward,getVec2(0,0),-turnAngSpeed * dt)
end
--apply turnForwordSpeed
self.x = self.x+ self.forward.x * turnForwordSpeed * dt
self.y = self.y+ self.forward.y * turnForwordSpeed * dt
else
--applly ForwordSpeed
self.x = self.x+ self.forward.x * ForwordSpeed * dt
self.y = self.y+ self.forward.y * ForwordSpeed * dt
end
end
动画示例(最终循环)显示了船舶无法到达目标的情况,因为转弯和前进速度的组合定义了转弯半径太大,在这种情况下可以减小“ turnForwordSpeed
”或更好地使其变小取决于角度距离(a
)和目标距离。