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

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

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


: 나는 우리가 계정으로 공기 저항을 고려하지 않는 경우에 대한 해결책을 찾을 생각 gamedev.stackexchange.com/questions/37916/...
Patryk Czachurski

답변:


4

당신이 말했듯이, 최선의 선택은이 경우 수치 체계를 사용하여 근사하는 것입니다. 시간을 큰 타임 스텝 (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)이며, 그렇게 나쁘지 않습니다.


+1, 좋은 답변입니다! 실제 시뮬레이션을 어떻게 고려할 수 없었습니까? "포물선 근사"에 대해 자세히 설명해 주시겠습니까 (잘 모르겠습니다)? 예를 들어 RK4 및 Euler와 같은 속도 통합 방법을 의미합니까? 그렇다면 설명하거나 최소한 수행 방법에 대한 정보로 연결하는 것이 가능합니까?
Patryk Czachurski

1
일반적으로 x'= x + v*dt. 대신을 사용하십시오 x' = x + v*dt + 1/2*a*dt*dt. dt작을 때는 작기 dt^2때문에 일반적으로 전통적인 오일러 통합 게임에서는 제외됩니다. 여기 dt가 작지 않으므로 가속 항이 필요합니다. dt두 번째 거듭 제곱으로 올라 가기 때문에 이것은 2 차 적분이며 경로는 포물선이므로 포물선 근사입니다. RK4는 기본적으로 더 높은 도함수를 계산하므로 입방, quartic, quintic 등의 근사치를 제공 할 수 있습니다. RK4는 안정성이 중요하지 않기 때문에 과잉입니다.

전통적인 오일러처럼 속도 자체를 통합해야한다고 생각합니까? v' = v + a*dt
Patryk Czachurski

1
네. 당신은 바보가 없습니다, 당신은 그것이 0이라고 가정합니다.

편집 내용을 살펴보십시오.
Patryk Czachurski

4

예이! 내가 해냈어!

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

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

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


2

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

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

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


1
그것은 특정 세계에서만 실용적입니다. 특히 Mario는 대략 선형적이고 속도가 제한되어 있으며 휴리스틱이 우수하여 검색 그래프의 크기를 제한합니다. 게임에 따라 사실이 아닐 수도 있습니다. 또한이 AI는 하나 이상의 캐릭터 / 적을 위해 작동해야하는 반면, 마리오에는 제어 할 것이 하나뿐이므로 계산 효율성도 상대적입니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.