这是我在尝试使用LÖVE引擎实现游戏时遇到的问题,该引擎覆盖了带有Lua脚本的box2d.
目标很简单:类似炮塔的物体(从顶部看,在2D环境中)需要定位自己,使其指向目标.
炮塔位于x,y坐标上,目标位于tx,ty.我们可以认为x,y是固定的,但是tx,ty往往会从一个瞬间变化到另一个瞬间(即它们将是鼠标光标).
转台有一个转子,可以在任何给定的时刻,顺时针或逆时针施加旋转力(扭矩).该力的大小有一个名为maxTorque的上限.
转台还具有一定的转动惯量,其作用于角运动,与质量作用于线性运动的方式相同.没有任何摩擦,因此如果它具有角速度,炮塔将继续旋转.
炮塔具有较小的AI功能,可重新评估其方向以验证其指向正确的方向,并激活旋转器.这发生在每dt(每秒约60次).它现在看起来像这样:
function Turret:update(dt)
local x,y = self:getPositon()
local tx,ty = self:getTarget()
local maxTorque = self:getMaxTorque() -- max force of the turret rotor
local inertia = self:getInertia() -- the rotational inertia
local w = self:getAngularVelocity() -- current angular velocity of the turret
local angle = self:getAngle() -- the angle the turret is facing currently
-- the angle of the like that links the turret center with the target
local targetAngle = math.atan2(oy-y,ox-x)
local differenceAngle = _normalizeAngle(targetAngle - angle)
if(differenceAngle <= math.pi) then -- counter-clockwise is the shortest path
self:applyTorque(maxTorque)
else -- clockwise is the shortest path
self:applyTorque(-maxTorque)
end
end
Run Code Online (Sandbox Code Playgroud)
... 它失败.让我用两个说明性的情况来解释:
我认为我的炮塔应该在达到目标角度之前开始在"最短路径的相反方向"上施加扭矩(就像停车前的汽车制动一样).
直觉上,我认为炮塔应该"在最短路径的相反方向上开始施加扭矩,当它大约是目标物体的一半"时.我的直觉告诉我它与角速度有关.然后事实是目标是移动的 - 我不知道我是否应该以某种方式考虑到这一点或者只是忽略它.
如何计算炮塔何时必须"开始制动"?
好吧,我相信我找到了解决方案。
\n\n这是基于 Beta 的想法,但进行了一些必要的调整。事情是这样的:
\n\nlocal twoPi = 2.0 * math.pi -- small optimisation \n\n-- returns -1, 1 or 0 depending on whether x>0, x<0 or x=0\nfunction _sign(x)\n return x>0 and 1 or x<0 and -1 or 0\nend\n\n-- transforms any angle so it is on the 0-2Pi range\nlocal _normalizeAngle = function(angle)\n angle = angle % twoPi\n return (angle < 0 and (angle + twoPi) or angle)\nend\n\nfunction Turret:update(dt)\n\n local tx, ty = self:getTargetPosition()\n local x, y = self:getPosition()\n local angle = self:getAngle()\n local maxTorque = self:getMaxTorque()\n local inertia = self:getInertia()\n local w = self:getAngularVelocity()\n\n local targetAngle = math.atan2(ty-y,tx-x)\n\n -- distance I have to cover\n local differenceAngle = _normalizeAngle(targetAngle - angle)\n\n -- distance it will take me to stop\n local brakingAngle = _normalizeAngle(_sign(w)*2.0*w*w*inertia/maxTorque)\n\n local torque = maxTorque\n\n -- two of these 3 conditions must be true\n local a,b,c = differenceAngle > math.pi, brakingAngle > differenceAngle, w > 0\n if( (a and b) or (a and c) or (b and c) ) then\n torque = -torque\n end\n\n self:applyTorque(torque)\nend\nRun Code Online (Sandbox Code Playgroud)\n\n这背后的概念很简单:我需要计算炮塔需要多少“空间”(角度)才能完全停止。这取决于炮塔移动的速度以及它可以施加给自身的扭矩有多大。简而言之,这就是我用 计算的结果brakingAngle。
我计算这个角度的公式与 Beta 的略有不同。我的一个朋友帮我解决了物理问题,而且,他们似乎很有效。添加 w 符号是我的想法。
\n\n我必须实现一个“归一化”功能,将任何角度放回 0-2Pi 区域。
\n\n最初,这是一个纠结的 if-else-if-else。由于条件非常重复,我使用了一些布尔逻辑来简化算法。缺点是,即使它工作正常并且并不复杂,也无法解释它为什么工作。
\n\n一旦代码更加纯净,我将在此处发布演示链接。
\n\n多谢。
\n\n编辑:工作 L\xc3\x96VE 示例现已在此处提供。重要的东西在 actor/AI.lua 里面(.love 文件可以用 zip 解压缩器打开)
\n| 归档时间: |
|
| 查看次数: |
3209 次 |
| 最近记录: |