A에서 B로 점프 할 수 있습니까? 약간 쓸모가 없습니다

사이드 스크롤러를위한 초보적인 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이 제안한 것처럼 시뮬레이션을 사용 하여이 문제를 해결하기로 결정했지만 그러한 경우를 처리하는 방법은 무엇입니까?
여기에 이미지 설명을 입력하십시오

C 에서 D 로 세그먼트를 그려 원하는 지점이이 세그먼트 아래에 있는지 확인해야합니까?

또는 CD 사이의 시간 간격을 이진 검색하여 원하는 지점과 수평 거리에 충분히 가까운 지점을 찾은 다음 수직 차이 만 확인해야합니까? (나에게 약간의 잔인한 것으로 보인다)



답변

당신이 말했듯이, 최선의 선택은이 경우 수치 체계를 사용하여 근사하는 것입니다. 시간을 큰 타임 스텝 (100-300ms)으로 나누고 각 타임 스텝에 대한 포물선 근사를 사용하십시오. 공기 저항을 제외하고는 힘이 동일합니다. 포물선 경로는 기본적으로 일정한 가속을위한 것이지만, 공기 저항의 경우 힘이 속도에 따라 달라지기 때문에 가속이 변경됩니다. 합리적인 근사치는 각 시간 간격 동안 공기 저항을 일정하게 취급하는 것입니다. 그러나 적분시 2 차 (예 : 포물선) 근사를 사용하면 훨씬 더 큰 단계를 처리 할 수 ​​있습니다. 그런 다음 포물선이 원하는 지점을 가로 방향으로 교차 할 때까지 계산 한 다음 높이를 비교합니다.

편집 : 비교에 대해 조금 더 자세히 설명합니다. 당신은 타임 스텝 (게임 프레임에서 많을 수 있음)에 걸쳐 플레이어가 목표를 교차한다는 것을 알고 <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다음 위치에 있습니다. 이것은 기본적으로 Euler 업데이트로 dt대체되어 t궤도의 어느 곳에서나 계산할 수 있습니다. 이제 우리는 x 위치가 2 차 함수라는 것을 알고 있으므로 캐릭터가 목표 바로 위 또는 아래에있는 단계에서 두 번 풀고 ax*t^2 + bx*t + cx = targetx 얻을 수 있습니다 . 그런 다음 [0,dt], 현재 시간 단계가 아니기 때문입니다. 견고성을 위해 범위 끝에 작은 상수를 추가하면 반올림 문제가 발생하지 않습니다. 이제 우리는 (필터링 후) 솔루션을 가질 수 없었습니다.이 경우 우리는이 단계에서 목표를 달성하지 못했습니다. 그렇지 않으면 해를 평가 ay*t^2 + by*t + cy하고이 y를와 비교합니다 targety. 궤적의 한 지점에서 목표 위에있을 수 있으며 나중에 그 아래에있을 수도 있습니다 (또는 그 반대). 원하는 상황에 따라 그러한 상황을 해석해야합니다.

많은 시간 단계를 고려하는 것은 원래 문제에 대한 분석 솔루션을 찾는 것보다 훨씬 쉽고 모션 모델을 변경할 수 있으므로 훨씬 유연하며 여전히 작동합니다.

가변 단계 사용에 대한 보너스 포인트 (예 : 첫 번째 초 (10 포인트) 100ms, 다음 2 개 (10 개 포인트) 200ms, 4 초에 400ms 등) 실제로 캐릭터가 터미널 속도에 접근함에 따라 저항이 떨어지고 더 큰 시간 간격이 필요하지 않습니다. 이 방법을 사용하면 T 초의 복잡성이 O (T)가 아니라 O (log T)이므로 너무 많은 처리 없이도 실제로 긴 점프를 처리 할 수 ​​있습니다.

캐릭터가 점프 도중에 부스트를 중지하거나 다른 방법으로 부스트를 시작할 때 발생하는 상황을 시뮬레이션 할 수도 있습니다. 위의 트릭으로 복잡성은 O ((log T) ^ 2)이며, 그렇게 나쁘지 않습니다.


답변

예이! 내가 해냈어!

목표 지점의 수직 축 뒤에 놓이는 첫 번째 위치를 취하는 간단한 시뮬레이션을 사용하고 있습니다. 거기에서 이전의 시뮬레이션 된 위치를 가져와 세그먼트를 만듭니다. 이제 목표 지점이이 세그먼트 아래에 있는지 확인합니다. 그렇다면-우리는 그곳으로 뛰어 넘을 수 있습니다.

여기에 이미지 설명을 입력하십시오

gif의 플레이어 제어 캐릭터입니다. 분홍색은 예측 된 경로이고, 노란색 세그먼트는 후속 스테핑 위치를 예측하며, 목표 지점이 그 아래에 있으면 마지막 세그먼트가 흰색으로 바뀌고 그렇지 않으면 빨간색으로 바뀝니다. 빨간색 곡선은 실제 비행 경로입니다. 물리 상태 보간이 켜져 있어 약간의 부정확성이 있습니다.

계산은 놀랍도록 쉬운 것으로 판명되었지만 내 환경이 순수한 계산과 같은 방식으로 작동하게 만드는 것은 엉덩이에 엄청난 고통이었습니다. 적어도 나는 약간의 심각한 버그를 해결 했으므로 결국 유용한 운동이었습니다.

원래 문제를 해결하는 데 사용되는 Lua의 전체 코드는 다음과 같습니다 (이 코드에서는 “length_sq”(길이 제곱), “정규화”또는 연산자 +, *와 같은 기본 메소드를 사용하여 자체 “debug_draw”루틴 및 자체 벡터 클래스가 있다고 가정합니다. :

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에게갑니다! 감사!


답변

답을 “그냥 계산”하고 싶을 수도 있지만 “자유 낙하”물리학의 대화식 특성으로 인해 답을 얻지 못하면 충분하지 않을 것입니다.

다른 접근 방식 (검색)을 사용해보십시오. Super Mario AI에서 수행되는 방법은 다음과 같습니다. http://aigamedev.com/open/interview/mario-ai/

A에서 B로가는 가능한 경로를 검색하면 공중에서 무제한의 상호 작용이 가능하면서도 계산 효율은 여전히 ​​높습니다.


답변