ฉันสามารถกระโดดจาก A ถึง B ได้หรือไม่


10

ฉันกำลังทำ AI พื้นฐานสำหรับนักเลื่อนด้านข้างของฉันและฉันจำเป็นต้องรู้ว่าหน่วย AI สามารถเข้าถึงจุด B จากจุด A ได้ง่ายๆหรือไม่โดยการกระโดด

เที่ยวบินวิถีของตัวละครของฉันเป็นบิตผิดปกติเพราะพวกเขาสามารถใช้บังคับในกลางอากาศ (เช่นในแจ๊ส Jackrabbit 2 ตัวอย่าง) ดังนั้นจึงแตกต่างจากวิถีดั้งเดิมของกระสุนปืนซึ่งเป็นเรื่องเกี่ยวกับ ...

เส้นทางที่กระสุนปืนโยนหรือเปิดตัวจะใช้เวลา (... ) โดยไม่มีแรงขับ

... ฉันคิดว่าปัญหาของฉันเป็นเรื่องเกี่ยวกับกระสุนปืนด้วยการขับเคลื่อน (เช่นจรวด)

เพื่อแสดงให้เห็นว่านี่เป็นลักษณะที่โค้งของเที่ยวบินสำหรับตัวละครของฉันถ้าฉันกระโดดและกดปุ่ม "ซ้าย" อย่างต่อเนื่อง (มันดูแตกต่างกันที่ปลายด้านซ้ายนี่คือจุดที่ฉันสร้าง manuevers กลางอากาศ): ป้อนคำอธิบายรูปภาพที่นี่

แรงที่กระทำระหว่างการบินจะขนานกับแกน X เสมอดังนั้นมันจึงเป็นF = (-f, 0)ถ้าฉันถือ "ซ้าย" และมันคือF = (f, 0)ถ้าฉันถือ "ขวา"

เขาสามารถเคลื่อนไหวได้เหมือนนักเล่นสกี:

ป้อนคำอธิบายรูปภาพที่นี่

ดังนั้นมันจึงแตกต่างจากวิถีคลาสสิกซึ่งเป็นเพียงพาราโบลา (ที่มา: วิกิพีเดีย ):

ป้อนคำอธิบายรูปภาพที่นี่

เพื่อให้ยากขึ้นฉันกำลังจำลองการต้านทานอากาศอย่างง่ายเพื่อให้ตัวละครของฉันสามารถเร่งความเร็วได้สูงสุดถึงค่าความเร็วสูงสุดเท่านั้น

ทำได้โดยใช้แรงเล็กน้อยในทิศทางตรงกันข้ามของการเดินทาง :

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และตรวจสอบว่าจุดที่ต้องการอยู่ด้านล่างส่วนนี้?

หรือฉันควรค้นหาไบนารีเวลาระหว่างCและDเพื่อค้นหาจุดที่อยู่ใกล้พอในระยะทางแนวนอนไปยังจุดที่ต้องการและจากนั้นตรวจสอบความแตกต่างแนวตั้ง? (ดูเหมือนจะเกินความจริงเล็กน้อยสำหรับฉัน)


ฉันคิดว่าฉันพบวิธีแก้ปัญหาสำหรับกรณีที่เราไม่คำนึงถึงการต่อต้านทางอากาศ: gamedev.stackexchange.com/questions/37916//
Patryk Czachurski

คำตอบ:


4

ในขณะที่คุณระบุตัวเลือกที่ดีที่สุดคือประมาณในกรณีนี้ใช้รูปแบบตัวเลข แบ่งเวลาออกเป็นช่วงเวลาขนาดใหญ่ (ประมาณ 100-300 มิลลิวินาที) และใช้การประมาณพาราโบลาสำหรับแต่ละการประทับเวลา แรงจะเหมือนกันตลอดยกเว้นความต้านทานอากาศ เส้นทางพาราโบลานั้นใช้สำหรับเร่งความเร็วคงที่ แต่ด้วยความต้านทานอากาศการเปลี่ยนแปลงจะเร่งขึ้นเนื่องจากแรงนั้นขึ้นอยู่กับความเร็ว การประมาณที่สมเหตุสมผลคือการรักษาความต้านทานของอากาศให้คงที่ในแต่ละช่วงเวลา แต่การใช้การประมาณกำลังสอง (เช่นพาราโบลา) เมื่อรวมจะช่วยให้คุณสามารถจัดการกับการประทับเวลาที่มีขนาดใหญ่กว่ามาก จากนั้นคุณก็คำนวณจนกระทั่งพาราโบลาข้ามจุดที่ต้องการในทิศทางแนวนอนแล้วเปรียบเทียบความสูง

แก้ไข:รายละเอียดเพิ่มเติมเล็กน้อยเกี่ยวกับการเปรียบเทียบ คุณจะรู้ว่าที่ผ่าน timestep (ซึ่งอาจจะเป็นจำนวนมากในเฟรมเกม) <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เป็นเวลาที่ผ่าน timestep ( 0 <= t <= dt) yและในทำนองเดียวกันสำหรับ ดังนั้นเมื่อt=0ตัวละครอยู่ในตำแหน่งก่อนหน้านี้และเมื่อใดตัวละครt=dtเหล่านั้นจะอยู่ในตำแหน่งต่อไป โปรดทราบว่านี่เป็นการอัปเดตออยเลอร์ด้วยการdtแทนที่tเพื่อให้เราสามารถคำนวณได้ทุกที่ตามเส้นทางการเคลื่อนที่ ตอนนี้เรารู้แล้วว่าตำแหน่ง x เป็นฟังก์ชันกำลังสองดังนั้นเราสามารถแก้ปัญหา ax*t^2 + bx*t + cx = targetxและรับ (มากถึง) สองครั้งในระหว่างขั้นตอนที่ตัวละครอยู่เหนือหรือต่ำกว่าเป้าหมาย จากนั้นเราจะโยนโซลูชันที่ไม่อยู่ในช่วง [0,dt] เนื่องจากสิ่งเหล่านี้ไม่ได้อยู่ในการกำหนดเวลาปัจจุบัน (เพื่อความทนทานให้เพิ่มค่าคงที่เล็ก ๆ ไปยังจุดสิ้นสุดของช่วงเพื่อที่คุณจะได้ไม่เกิดปัญหารอบ) ตอนนี้เราอาจไม่มีวิธีแก้ปัญหา (หลังจากกรองแล้ว) ซึ่งในกรณีนี้เราไม่ได้กำหนดเป้าหมายเวลานี้ มิฉะนั้นเราจะประเมินในการแก้ปัญหาและเปรียบเทียบปีนี้กับay*t^2 + by*t + cy targetyโปรดทราบว่าคุณสามารถอยู่เหนือเป้าหมายได้ที่จุดหนึ่งในเส้นทางการเคลื่อนที่ของคุณและต่ำกว่าเป้าหมายในภายหลัง (หรือในทางกลับกัน) คุณจะต้องตีความสถานการณ์ดังกล่าวตามสิ่งที่คุณต้องการจะทำ

การพิจารณากำหนดเวลาจำนวนมากนั้นง่ายกว่าการค้นหาโซลูชันการวิเคราะห์สำหรับปัญหาดั้งเดิมและมีความยืดหยุ่นมากขึ้นเนื่องจากคุณสามารถเปลี่ยนรูปแบบการเคลื่อนไหวและสิ่งนี้จะยังคงใช้งานได้ประมาณ

คะแนนโบนัสสำหรับการใช้ขั้นตอนผันแปรเช่น 100ms สำหรับวินาทีแรก (สิบคะแนน), 200ms สำหรับสองถัดไป (อีกสิบคะแนน), 400ms ใน 4 วินาทีเป็นต้นในความเป็นจริงเมื่อตัวละครของคุณเข้าใกล้ความเร็วเทอร์มินัล ความต้านทานลดลงและคุณไม่ต้องการการประทับเวลาที่มากขึ้น วิธีนี้คุณสามารถจัดการกับการกระโดดได้นานโดยไม่ต้องประมวลผลมากเกินไปเนื่องจากความซับซ้อนของ T วินาทีคือ O (log T) มากกว่า O (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ยกกำลังสองนี่คือการบูรณาการกำลังสองและเส้นทางคือพาราโบลาดังนั้นการประมาณพาราโบลา RK4 นั้นจะคำนวณอนุพันธ์ที่สูงกว่าและอาจให้ลูกบาศก์, ควอร์ทิค, ควินติคและอื่น ๆ โดยประมาณ RK4 นั้นเกินความเป็นไปได้สำหรับเรื่องนี้น่าจะเป็นเพราะความมั่นคงไม่สำคัญ

และฉันคิดว่าความเร็วของตัวเองควรจะรวมอยู่ในออยเลอร์แบบดั้งเดิม? v' = v + a*dt
Patryk Czachurski

1
อ๋อ คุณไม่มีกระตุกคุณคิดว่ามันเป็นศูนย์

โปรดดูการแก้ไข
Patryk Czachurski

4

เย้! ฉันทำมัน!

ฉันกำลังใช้การจำลองแบบง่าย ๆ ที่จะเอาตำแหน่งแรกไปวางไว้ด้านหลังแกนแนวตั้งของจุดเป้าหมาย - จากที่นั่นฉันใช้ตำแหน่งจำลองก่อนหน้านี้และสร้างเซ็กเมนต์ ตอนนี้ฉันตรวจสอบว่าจุดเป้าหมายต่ำกว่ากลุ่มนี้หรือไม่ ถ้าเป็น - เราสามารถกระโดดไปที่นั่นได้

ป้อนคำอธิบายรูปภาพที่นี่

มันเป็นตัวละครที่ผู้เล่นควบคุมบน gif สีชมพูเป็นเส้นทางที่คาดการณ์ส่วนสีเหลืองจะถูกทำนายตำแหน่งการก้าวต่อไปและส่วนสุดท้ายจะเปลี่ยนเป็นสีขาวหากจุดเป้าหมายอยู่ด้านล่างสีแดง เส้นโค้งสีแดงเป็นเส้นทางการบินจริง มีความคลาดเคลื่อนเล็กน้อยเนื่องจากการแก้ไขสภาวะของฟิสิกส์เปิดอยู่

การคำนวณกลายเป็นเรื่องง่ายอย่างน่าประหลาดใจอย่างไรก็ตามการทำให้สภาพแวดล้อมของฉันทำงานเหมือนกับที่การคำนวณล้วนๆทำ ... เป็นความเจ็บปวดครั้งใหญ่ในก้น อย่างน้อยฉันก็แก้ไขข้อผิดพลาดร้ายแรงบางอย่างที่นั่นดังนั้นมันจึงเป็นแบบฝึกหัดที่มีประโยชน์

นี่คือรหัสที่สมบูรณ์ใน Lua ที่ใช้ในการแก้ปัญหาเดิม (รหัสถือว่าคุณมีรูทีน "debug_draw" ของคุณเองและคลาสเวกเตอร์ของคุณเองด้วยวิธีการพื้นฐานเช่น "length_sq" (length squared), "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

คุณอาจต้องการ "เพียงคำนวณ" คำตอบ แต่ฉันแน่ใจว่าคุณจะพบว่ามันไม่เพียงพอเมื่อคุณได้รับเนื่องจากลักษณะการโต้ตอบสูงของฟิสิกส์ "ฤดูใบไม้ร่วงฟรี" ของคุณ

ลองใช้วิธีอื่น: การค้นหา นี่เป็นวิธีที่ทำได้สำหรับ Super Mario AI: http://aigamedev.com/open/interview/mario-ai/

การค้นหาเส้นทางที่เป็นไปได้ที่จะได้รับจาก A ถึง B ช่วยให้การติดต่อสื่อสารไม่ จำกัด ในกลางอากาศขณะที่ยังคงมีประสิทธิภาพในการคำนวณ


1
นั่นเป็นเพียงการปฏิบัติสำหรับโลกบางอย่าง โดยเฉพาะอย่างยิ่งมาริโอ จำกัด ขนาดของกราฟการค้นหาโดยการเชิงเส้นอย่างคร่าว ๆ มีความเร็ว จำกัด และมีฮิวริสติกที่ยอดเยี่ยม สิ่งนี้อาจไม่เป็นจริงขึ้นอยู่กับเกม ประสิทธิภาพในการคำนวณก็สัมพันธ์กันเช่นกันเพราะ AI นี้จะต้องทำงานให้กับตัวละคร / ศัตรูมากกว่าหนึ่งตัวในขณะที่มาริโอมีเพียงตัวเดียวที่จะควบคุม
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.