我可以从A跳到B吗?


10

我正在为侧滑车制作一些基本的AI,并且我需要知道AI单元是否可以仅通过跳跃就可以从A点到达B点。

我的角色的飞行轨迹有点用处不大,因为他们可以在空中施加力量(例如在Jazz Jackrabbit 2中),因此与经典弹丸的轨迹不同:

投掷或发射的弹丸将采取(...)没有推进力的路径。

……我想我的问题更多是关于带有推进力的弹丸(例如火箭)。

为了说明这一点,这是我跳并不断按下“左键”时飞行曲线的样子(在左端看起来有所不同,这是我在空中进行一些操作的地方): 在此处输入图片说明

飞行期间施加的力始终平行于X轴,因此如果我按住“左”,则为F =(-f,0);如果我按住“右” 则为F =(f,0)

他的动作非常像跳台滑雪者:

在此处输入图片说明

因此,它与经典的抛物线轨迹有很大不同(来源:Wikipedia):

在此处输入图片说明

更困难的是,我正在模拟简单的空气阻力,因此我的角色只能加速到某个最大速度值。

这是通过在相反的方向施加较小的力来完成的:

b2Vec2 vel = body->GetLinearVelocity();
float speed = vel.Normalize(); //normalizes vector and returns length
body->ApplyForce( AIR_RESISTANCE_MULT * speed * speed * -vel, body->GetWorldCenter() );

AIR_RESISTANCE_MULT是一个常数,在我的情况下等于0.1。

假设我的角色是一个非常小的点。

而且我没有考虑障碍,所以我的问题是这样的...

在给定初始速度V的情况下,如何确定(至少可靠地猜测)我在跳跃时应用于角色的脉冲J =(0,-j),重力G =(0,g),力F =(+ -f ,0)如果我们真的决定考虑空气阻力 (这是可选的) 那么在飞行和AIR_RESISTANCE_MULT期间会持续应用(这是可选的),是否某个点位于我的角色将采用的路径绘制的曲线下方?

我实际上不知道从哪里开始计算,实际上,我不一定对确切的答案感兴趣。良好的破解/逼真度将非常棒,因为AI绝对不需要完美运行。

编辑:我决定按照杰森(Jason)的建议使用模拟解决此问题,但是如何处理这种情况? 在此处输入图片说明

我应该画一个从CD的线段,并检查所需的点是否在该线段的下方?

还是应该二进制搜索CD之间的时间步长,以寻找在水平距离上与所需点足够接近的点,然后才检查垂直差?(对我来说似乎有点矫kill过正)


我想我找到的情况下的解决方案,我们不采取空气阻力考虑: gamedev.stackexchange.com/questions/37916/...
Patryk Czachurski

Answers:


4

如您所言,最佳选择是近似值,在这种情况下,使用数字方案。将时间划分为较大的时间步长(例如100-300ms),并对每个时间步长使用抛物线近似。除空气阻力外,所有力均相同。抛物线路径基本上用于恒定的加速度,但是由于阻力取决于速度,因此在空气阻力作用下,加速度会发生变化。一个合理的近似值是将空气阻力在每个时间步长上视为恒定。但是在积分时使用二次(即抛物线)近似值可以处理更大的时间步长。然后,您只需进行计算,直到抛物线在水平方向上越过所需的点,然后比较高度即可。

编辑:有关比较的更多细节。您知道,随着时间的推移(在游戏帧中可能会很多),玩家会越过目标<targetx,targety>。它们的路径由以下位置描述<ax*t^2 + bx*t + cx, ay*t^2 + by*t + cy>

ax = 1/2 * accel.x
bx = velocity.x
cx = position.x

t是通过时间步(0 <= t <= dt)的时间,同样是y。因此,当t=0角色在上一个位置时t=dt,当角色在下一个位置时。请注意,这基本上是dt替换为的Euler更新,t以便我们可以沿轨迹的任何位置进行计算。现在我们知道x位置是二次函数,因此在字符直接位于目标上方或下方的步骤中,我们可以求解 ax*t^2 + bx*t + cx = targetx两次(最多)。然后,我们抛出不在[0,dt],因为这些不在当前时间步中。(为提高鲁棒性,请在范围的末端添加一个小常数,这样就不会出现舍入问题)。现在我们可能没有解决方案(在过滤之后),在这种情况下,我们不会达到此目标。否则,我们将评估ay*t^2 + by*t + cy解决方案,并将y与进行比较targety。请注意,您可能会在轨迹的某一点处高于目标,而在其后则低于目标(反之亦然)。您将需要根据自己的意愿来解释这种情况。

考虑一堆时间步长比找到针对原始问题的解析解决方案要容易得多,而且灵活得多,因为您可以更改运动模型,并且仍然可以正常工作。

使用可变步长的奖励积分,例如,第一秒为100毫秒(十个点),下一秒为200毫秒(十个以上点),四秒内为400毫秒,等等。实际上,当角色接近终极速度时,阻力下降了,而且您也不需要更长的时间步长。这样,您可以处理非常长的跳跃而无需进行过多处理,因为T秒的复杂度是O(log T)而不是O(T)。

您还可以模拟当角色在跳跃过程中停止增强或以其他方式增强时发生的情况。通过以上技巧,复杂度为O((log T)^ 2),这还不错。


+1,好答案!我怎么不考虑实际的模拟。您能否详细说明“抛物线近似”(我不太了解)?您是否只是说要整合速度的方法,例如RK4和Euler?如果是这样,您能解释一下还是至少链接到一些有关如何执行的信息?
Patryk Czachurski 2014年

1
通常你会的x'= x + v*dt。改为使用x' = x + v*dt + 1/2*a*dt*dt。当dt很小的时候,dt^2很小的时候,因此通常在游戏中传统的Euler集成中将其忽略。这里dt不小,因此需要加速项。由于dt被提高到第二幂,这是二次积分,并且路径是抛物线,因此是抛物线近似。RK4本质上计算出更高的导数,因此可以给出三次近似,四次近似,五次近似等。RK4在这方面过于矫over过正,因为稳定性并不重要。

我认为速度本身应该像传统的欧拉一样整合吗?v' = v + a*dt
Patryk Czachurski 2014年

1
是的 您没有混蛋,您假设它为零。

请看一下编辑。
Patryk Czachurski 2014年

4

好极了!我做的!

我正在使用简单的模拟,该模拟的第一个位置降落在目标点的垂直轴后面-从那里开始,我采用先前的模拟位置并进行分割。现在,我检查目标点是否在此线段之下。如果是,我们可以跳到那里。

在此处输入图片说明

它是gif上由玩家控制的角色。粉色是预测的路径,黄色段是预测的后续步进位置,如果目标点位于其下方,则最后一段变为白色,否则为红色。红色曲线是实际的飞行路径。由于打开了物理状态插值,因此存在一些轻微的错误。

计算结果出乎意料地容易,但是让我的环境像这些纯计算一样进行工作……这是一个巨大的麻烦。至少我解决了一些严重的错误,所以毕竟这是一个有用的练习。

这是Lua中用于解决原始问题的完整代码(代码假定您拥有自己的“ debug_draw”例程和带有基本方法(例如“ length_sq”(长度平方),“ normalize”或运算符+,*的向量类)的代码:

function simple_integration(p, dt)
    local new_p = {}

    new_p.acc = p.acc
    new_p.vel = p.vel + p.acc * dt 
    new_p.pos = p.pos + new_p.vel * dt
    -- uncomment this if you want to use quadratic integration
    -- but with small timesteps even this is an overkill since Box2D itself uses traditional Euler
    -- and I found that for calculations to be accurate I either way must keep the timesteps very low at the beginning of the jump
     --+ p.acc * dt * dt * 0.5

    return new_p
end

function point_below_segment(a, b, p)
    -- make sure a is to the left
    if a.x > b.x then a,b = b,a end

    return ((b.x - a.x)*(p.y - a.y) - (b.y - a.y)*(p.x - a.x)) < 0
end

-- returns true or false
function can_point_be_reached_by_jump
(
gravity, -- vector (meters per seconds^2)
movement_force, -- vector (meters per seconds^2)
air_resistance_mult, -- scalar
queried_point, -- vector (meters)
starting_position, -- vector (meters)
starting_velocity, -- vector (meters per seconds)
jump_impulse, -- vector (meters per seconds)
mass -- scalar (kilogrammes)
)

    local my_point = {
        pos = starting_position,
        vel = starting_velocity + jump_impulse/mass
    }

    local direction_left = movement_force.x < 0
    local step = 1/60

    while true do           
        -- calculate resultant force
        my_point.acc = 
        -- air resistance (multiplier * squared length of the velocity * opposite normalized velocity)
        (vec2(my_point.vel):normalize() * -1 * air_resistance_mult * my_point.vel:length_sq()) / mass
        -- remaining forces
        + gravity + movement_force/mass

        -- I discard any timestep optimizations at the moment as they are very context specific
        local new_p = simple_integration(my_point, step)

        debug_draw(my_point.pos, new_p.pos, 255, 0, 255, 255)
        debug_draw(new_p.pos, new_p.pos+vec2(0, -1), 255, 255, 0, 255)

        if (direction_left and new_p.pos.x < queried_point.x) or (not direction_left and new_p.pos.x > queried_point.x) then
            if point_below_segment(new_p.pos, my_point.pos, queried_point) then
                debug_draw(new_p.pos, my_point.pos, 255, 0, 0, 255)
                return true
            else
                debug_draw(new_p.pos, my_point.pos, 255, 255, 255, 255)
                return false
            end
        else 
            my_point = new_p
        end
    end

    return false
end

接受Jason指导我朝正确的方向前进!谢谢!


2

您可能想“仅仅计算”答案,但是我敢肯定,一旦获得答案,由于“自由落体”物理学的高度交互性,您会发现答案不足。

考虑使用另一种方法:搜索。这是超级Mario AI的处理方式:http : //aigamedev.com/open/interview/mario-ai/

搜索从A到B的可能路径可以实现空中无限的交互性,同时仍能提高计算效率。


1
这仅适用于某些世界。特别是,马里奥(Mario)通过大致呈线性,具有有限数量的速度以及出色的启发式来限制搜索图的大小。取决于游戏,这可能不是正确的。同样,计算效率也是相对的,因为这种AI可能必须针对多个角色/敌人工作,而在Mario中,只有一个角色需要控制。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.