Tôi có thể nhảy từ A đến B không?


10

Tôi đang tạo ra một số AI thô sơ cho máy cuộn phụ của mình và tôi cần biết liệu một đơn vị AI có thể đạt đến điểm B từ điểm A hay không chỉ bằng cách nhảy.

Quỹ đạo bay của các nhân vật của tôi hơi bất thường vì họ có thể tác dụng lực trong không trung (ví dụ như trong Jazz Jackrabbit 2), nên không giống như quỹ đạo cổ điển của một viên đạn về ...

con đường mà một viên đạn ném hoặc phóng sẽ đi (...) mà không cần lực đẩy.

... Tôi cho rằng vấn đề của tôi là về một viên đạn có lực đẩy (ví dụ như tên lửa).

Để minh họa điều này, đây là cách đường cong của nhân vật của tôi trông như thế nào nếu tôi nhảy và liên tục nhấn "nút bên trái" (nó trông khác ở đầu bên trái, đây là nơi tôi đang thực hiện một số thao tác trên không trung): nhập mô tả hình ảnh ở đây

Lực tác dụng trong suốt chuyến bay luôn song song với trục X, do đó, nó là F = (-f, 0) nếu tôi giữ "trái" và đó là F = (f, 0) nếu tôi giữ "phải".

Anh ta có thể di chuyển rất giống một người nhảy trượt tuyết:

nhập mô tả hình ảnh ở đây

Vì vậy, nó khác rất nhiều với quỹ đạo cổ điển đơn giản là một parabola (nguồn: wikipedia ):

nhập mô tả hình ảnh ở đây

Để làm cho nó khó khăn hơn, tôi đang mô phỏng sức cản không khí đơn giản để các nhân vật của tôi chỉ có thể tăng tốc lên đến một số giá trị tốc độ tối đa.

Điều này được thực hiện bằng cách áp dụng một lực nhỏ theo hướng ngược lại :

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 là một hằng số mà trong trường hợp của tôi bằng 0,1.

Hãy giả sử rằng nhân vật của tôi là một điểm nhỏ vô hạn.

Và tôi KHÔNG xem xét các chướng ngại vật, vì vậy câu hỏi của tôi như thế này ...

Cách xác định (ít nhất là đoán một cách đáng tin cậy), với vận tốc ban đầu V, xung J = (0, -j) mà tôi áp dụng cho nhân vật khi nhảy, trọng lực G = (0, g) , lực F = (+ -f , 0) liên tục được áp dụng trong suốt chuyến bay và AIR_RESISTANCE_MULT nếu chúng tôi thực sự quyết định tính đến sức cản không khí (đây là tùy chọn) , liệu một điểm nằm dưới đường cong được vẽ bởi con đường mà nhân vật của tôi sẽ đi?

Tôi thực sự không biết bắt đầu từ đâu với các tính toán và trên thực tế, tôi không nhất thiết phải quan tâm đến một câu trả lời chính xác; một hack / xấp xỉ hoạt động tốt sẽ là tuyệt vời vì AI không có nghĩa là cần phải hành động hoàn hảo.

chỉnh sửa: Tôi đã quyết định giải quyết vấn đề này bằng cách sử dụng mô phỏng như Jason gợi ý, nhưng làm thế nào để xử lý trường hợp như vậy? nhập mô tả hình ảnh ở đây

Tôi có nên vẽ một đoạn từ C đến D và kiểm tra xem điểm mong muốn có nằm dưới đoạn này không?

Hoặc tôi nên tìm kiếm nhị phân các dấu thời gian giữa CD để tìm điểm đủ gần trong khoảng cách ngang đến điểm mong muốn và chỉ sau đó kiểm tra độ chênh lệch dọc? (có vẻ hơi quá mức với tôi)


Tôi nghĩ rằng tôi đã tìm thấy một giải pháp cho trường hợp chúng ta không tính đến sức cản không khí: gamedev.stackexchange.com/questions/37916/
mẹo

Câu trả lời:


4

Như bạn nêu, sự lựa chọn tốt nhất là gần đúng, trong trường hợp này là sử dụng sơ đồ số. Chia thời gian thành các dấu thời gian lớn (giả sử 100-300ms) và sử dụng phép tính xấp xỉ parabol cho mỗi dấu thời gian. Các lực lượng là như nhau trong suốt ngoại trừ sức cản không khí. Đường parabol về cơ bản là để tăng tốc liên tục, nhưng với sức cản không khí, gia tốc thay đổi vì lực phụ thuộc vào tốc độ. Một xấp xỉ hợp lý là coi sức cản không khí là hằng số theo từng dấu thời gian. Nhưng sử dụng một xấp xỉ bậc hai (tức là parabol) khi tích hợp cho phép bạn xử lý các dấu thời gian lớn hơn nhiều. Sau đó, bạn chỉ cần tính toán cho đến khi một parabol vượt qua điểm mong muốn theo hướng ngang, và sau đó so sánh độ cao.

EDIT: Chi tiết hơn một chút về so sánh. Bạn biết rằng qua dấu thời gian (có thể có nhiều trong khung trò chơi), người chơi sẽ vượt qua mục tiêu <targetx,targety>. Con đường của họ được mô tả bởi vị trí <ax*t^2 + bx*t + cx, ay*t^2 + by*t + cy>trong đó:

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

tlà thời gian thông qua dấu thời gian ( 0 <= t <= dt) và tương tự cho y. Vì vậy, khi t=0nhân vật ở vị trí trước, và khi nào t=dt, họ ở vị trí tiếp theo. Lưu ý rằng về cơ bản, đây là bản cập nhật Euler được dtthay thế bằng tđể chúng ta có thể tính toán bất cứ nơi nào dọc theo quỹ đạo. Bây giờ chúng ta biết vị trí x là một hàm bậc hai, vì vậy chúng ta có thể giải ax*t^2 + bx*t + cx = targetx và nhận (tối đa) hai lần trong bước mà nhân vật ở ngay trên hoặc dưới mục tiêu. Sau đó, chúng tôi đưa ra bất kỳ giải pháp nào không có trong phạm vi [0,dt], vì những điều này không có trong dấu thời gian hiện tại. (Để mạnh mẽ, hãy thêm một hằng số nhỏ vào cuối phạm vi để bạn không gặp vấn đề về vòng). Bây giờ chúng tôi không thể có giải pháp (sau khi lọc), trong trường hợp đó chúng tôi không đạt được mục tiêu theo dấu thời gian này. Mặt khác, chúng tôi đánh giá ay*t^2 + by*t + cytại các giải pháp và so sánh y này với targety. Lưu ý rằng bạn có thể ở trên mục tiêu tại một điểm trong quỹ đạo của bạn và bên dưới mục tiêu sau (hoặc ngược lại). Bạn sẽ cần diễn giải những tình huống như vậy theo những gì bạn muốn làm.

Việc xem xét một loạt các dấu thời gian dễ dàng hơn nhiều so với việc tìm một giải pháp phân tích cho vấn đề ban đầu, và linh hoạt hơn nhiều khi bạn có thể thay đổi mô hình chuyển động và điều này vẫn sẽ hoạt động tốt.

Điểm thưởng khi sử dụng các bước thay đổi, ví dụ: 100ms cho giây đầu tiên (mười điểm), 200ms cho hai lần tiếp theo (mười điểm nữa), 400ms trong 4 giây, v.v. Trên thực tế, khi nhân vật của bạn tiếp cận vận tốc đầu cuối, sự thay đổi trong sức đề kháng giảm xuống và dù sao bạn cũng không cần thời gian lớn hơn. Bằng cách này, bạn có thể xử lý các bước nhảy thực sự dài mà không cần xử lý quá nhiều, vì độ phức tạp trong T giây là O (log T) chứ không phải O (T).

Bạn cũng có thể mô phỏng những gì xảy ra khi nhân vật ngừng tăng tốc giữa chừng khi họ nhảy hoặc bắt đầu tăng tốc theo cách khác. Với thủ thuật trên, độ phức tạp là O ((log T) ^ 2), không quá tệ.


+1, câu trả lời tuyệt vời! Làm thế nào tôi có thể không xem xét các mô phỏng thực tế. Bạn có thể vui lòng giải thích về "xấp xỉ parabol" (tôi không hiểu lắm)? Bạn có nghĩa là phương pháp tích hợp vận tốc, ví dụ như RK4 và Euler? Nếu vậy, bạn có thể giải thích nó hoặc ít nhất là liên kết đến một số thông tin về cách thực hiện nó?
Patryk Czachurski

1
Thông thường bạn làm x'= x + v*dt. Thay vì sử dụng x' = x + v*dt + 1/2*a*dt*dt. Khi dtnhỏ, dt^2nhỏ, vì vậy nhìn chung nó bị bỏ lại trong tích hợp Euler truyền thống trong các trò chơi. Ở đây dtkhông nhỏ, vì vậy bạn cần thuật ngữ tăng tốc. Kể từ khi dtđược nâng lên sức mạnh thứ hai, đây là một tích hợp bậc hai, và đường dẫn là một parabol, do đó xấp xỉ parabol. RK4 về cơ bản tính toán các dẫn xuất cao hơn, và do đó có thể đưa ra các xấp xỉ bậc ba, tứ phân, quintic, v.v. RK4 là quá mức cần thiết cho điều này, rất có thể, vì sự ổn định là không quan trọng.

và tôi cho rằng chính vận tốc nên được tích hợp như trong Euler truyền thống? v' = v + a*dt
Patryk Czachurski

1
Vâng. Bạn không bị giật, bạn cho rằng nó bằng không.

Xin hãy xem bản chỉnh sửa.
Patryk Czachurski

4

Yay! Tôi đã làm nó!

Tôi đang sử dụng mô phỏng đơn giản để đưa vị trí đầu tiên hạ cánh xuống sau trục thẳng đứng của điểm mục tiêu - từ đó, tôi lấy vị trí mô phỏng trước đó và tạo một phân đoạn. Bây giờ tôi kiểm tra xem điểm mục tiêu có nằm dưới phân khúc này không. Nếu có - chúng ta có thể nhảy vào đó.

nhập mô tả hình ảnh ở đây

Đó là một nhân vật do người chơi điều khiển trên gif. Màu hồng là đường dự đoán, các phân đoạn màu vàng được dự đoán các vị trí bước tiếp theo và phân đoạn cuối cùng chuyển sang màu trắng nếu điểm mục tiêu nằm bên dưới nó, màu đỏ khác. Đường cong màu đỏ là đường bay thực tế. Có một số điểm không chính xác do nội suy trạng thái vật lý được bật.

Các tính toán hóa ra dễ dàng đến mức đáng kinh ngạc, tuy nhiên làm cho môi trường của tôi hoạt động giống như những tính toán thuần túy này ... là một nỗi đau lớn ở mông. Ít nhất tôi đã giải quyết được một số lỗi nghiêm trọng ngoài kia, vì vậy đó là một bài tập hữu ích.

Đây là mã hoàn chỉnh trong Lua được sử dụng để giải quyết vấn đề ban đầu (mã giả định rằng bạn có thói quen "debug_draw" của riêng bạn và lớp vectơ của riêng bạn với các phương thức cơ bản như "length_sq" (bình phương độ dài), "chuẩn hóa" hoặc toán tử +, * :

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

Chấp nhận đi đến Jason để đặt tôi đi đúng hướng! Cảm ơn!


2

Bạn có thể muốn "chỉ tính toán" câu trả lời nhưng tôi chắc chắn rằng bạn sẽ thấy nó không đủ khi bạn đã nhận được vì tính tương tác cao của vật lý "rơi tự do" của bạn.

Xem xét sử dụng một cách tiếp cận khác: Tìm kiếm. Đây là cách nó được thực hiện cho Super Mario AI: http://aigamedev.com/open/interview/mario-ai/

Tìm kiếm các đường dẫn có thể để đi từ A đến B cho phép tương tác không giới hạn trong không trung mà vẫn hiệu quả về mặt tính toán.


1
Điều đó chỉ thực tế cho một số thế giới nhất định. Cụ thể, Mario giới hạn kích thước của biểu đồ tìm kiếm bằng cách gần như tuyến tính, có số lượng vận tốc hạn chế và có một heuristic tuyệt vời. Tùy thuộc vào trò chơi, điều này có thể không đúng. Ngoài ra hiệu quả tính toán là tương đối, vì AI này có thể sẽ phải làm việc cho nhiều hơn một nhân vật / kẻ thù, trong khi trong Mario chỉ có một để điều khiển.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.