क्या मैं A से B में कूद सकता हूं?


10

मैं अपने साइड-स्कॉलर के लिए कुछ अल्पविकसित एअर इंडिया बना रहा हूं और मुझे यह जानना होगा कि क्या कोई एआई इकाई केवल एक कूदने से बिंदु ए से बिंदु बी तक पहुंच सकती है।

मेरे पात्रों की उड़ान प्रक्षेपवक्र थोड़ी असामान्य है क्योंकि वे मध्य-वायु में बल लगा सकते हैं (उदाहरण के लिए जैज़ जैकबबिट 2 में), इसलिए एक प्रक्षेप्य के क्लासिक प्रक्षेपवक्र के विपरीत जो ...

पथ कि फेंक दिया या प्रक्षेपित प्रक्षेप्य (बिना) प्रणोदन के बिना ले जाएगा।

... मुझे लगता है कि मेरी समस्या प्रणोदन (जैसे रॉकेट) के साथ एक प्रक्षेप्य के बारे में अधिक है ।

यह वर्णन करने के लिए, यह है कि फ्लाइट वक्र मेरे चरित्र के लिए कैसा दिखता है अगर मैं कूदता हूं और "बाएं बटन" को लगातार दबाता हूं (यह बाएं छोर पर अलग दिखता है, यह वह जगह है जहां मैं मध्य हवा में कुछ पैंतरेबाज़ी कर रहा था): यहाँ छवि विवरण दर्ज करें

उड़ान के दौरान लगाया गया बल हमेशा 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 के दौरान लगातार लागू किया जाता है , अगर हम वास्तव में हवाई प्रतिरोध को ध्यान में रखते हुए निर्णय लेते हैं (यह वैकल्पिक है) , क्या एक बिंदु मेरे चरित्र द्वारा उठाए गए वक्र से नीचे झूठ होगा?

मुझे सचमुच पता नहीं है कि गणना के साथ कहां से शुरू करना है और वास्तव में, मुझे जरूरी नहीं कि सटीक उत्तर में दिलचस्पी हो; एक अच्छी तरह से काम कर हैक / सन्निकटन एअर इंडिया के रूप में महान होगा कोई मतलब नहीं पूरी तरह से काम करने की जरूरत है।

संपादित करें: मैंने सिमुलेशन का उपयोग करके इसे हल करने का फैसला किया है क्योंकि जेसन का सुझाव है, लेकिन इस तरह के मामले को कैसे संभालना है? यहाँ छवि विवरण दर्ज करें

क्या मुझे C से D तक का सेगमेंट आकर्षित करना चाहिए और जांचना चाहिए कि वांछित बिंदु इस सेगमेंट के नीचे है या नहीं?

या क्या मुझे उस बिंदु की तलाश के लिए सी और डी के बीच टाइमस्टेप को बाइनरी खोजना चाहिए जो कि वांछित बिंदु तक क्षैतिज दूरी में पर्याप्त है, और उसके बाद ही ऊर्ध्वाधर अंतर की जांच करें? (मेरे लिए थोड़ा अधिक लगता है)


मुझे लगता है कि मुझे उस मामले के लिए एक समाधान मिला जहां हम हवा के प्रतिरोध को ध्यान में नहीं रखते हैं: gamedev.stackexchange.com/questions/37916/…
Patryk Czachurski

जवाबों:


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जगह tहम प्रक्षेपवक्र के साथ कहीं भी गणना कर सकते हैं। अब हम जानते हैं कि एक्स-पोजिशन एक द्विघात फ़ंक्शन है, इसलिए हम उस चरण के दौरान दो बार हल कर सकते ax*t^2 + bx*t + cx = targetxहैं (प्राप्त कर सकते हैं ) जिसमें चरित्र सीधे लक्ष्य से ऊपर या नीचे है। फिर हम कोई भी समाधान निकालते हैं जो सीमा में नहीं है [0,dt], क्योंकि ये वर्तमान टाइमस्टेप में नहीं हैं। (मजबूती के लिए, सीमा के सिरों पर एक छोटा सा स्थिरांक जोड़ें ताकि आपको समस्याएँ न हों।) अब हमारे पास (फ़िल्टर करने के बाद) कोई समाधान नहीं हो सकता है, जिस स्थिति में हम इस टाइमस्टेप को निशाना नहीं बनाते हैं। अन्यथा, हम ay*t^2 + by*t + cyसमाधानों का मूल्यांकन करते हैं, और इस y की तुलना करते हैं targety। ध्यान दें कि आप अपने प्रक्षेपवक्र में एक बिंदु पर लक्ष्य से ऊपर हो सकते हैं, और बाद में इसके नीचे (या इसके विपरीत)। आपको ऐसी स्थितियों की व्याख्या करने की आवश्यकता होगी जो आप करना चाहते हैं।

मूल समस्या का एक विश्लेषणात्मक समाधान खोजने की तुलना में टाइमस्टेप के एक झुंड पर विचार करना बहुत आसान है, और कहीं अधिक लचीला है क्योंकि आप गति मॉडल को बदल सकते हैं और यह अभी भी मोटे तौर पर काम करेगा।

चर चरणों का उपयोग करने के लिए बोनस अंक, उदाहरण के लिए, पहले सेकंड (दस अंक) के लिए 100ms, अगले दो के लिए 200ms (दस और अंक), 4 सेकंड से अधिक 400ms आदि, वास्तव में, जैसा कि आपका चरित्र टर्मिनल वेग में भिन्नता के रूप में आता है। प्रतिरोध कम हो जाता है, और आपको वैसे भी बड़े टाइमस्टेप की आवश्यकता नहीं होती है। इस तरह से आप बहुत अधिक प्रसंस्करण के बिना वास्तव में लंबी छलांग लगा सकते हैं, क्योंकि टी सेकंड के लिए जटिलता ओ (टी) के बजाय ओ (लॉग टी) है।

आप यह भी अनुकरण कर सकते हैं कि क्या होता है जब चरित्र अपनी कूद के माध्यम से भाग को बढ़ावा देना बंद कर देता है, या दूसरे तरीके से बढ़ावा देना शुरू कर देता है। उपरोक्त चाल के साथ जटिलता ओ ((लॉग टी) ^ 2) है, जो बहुत बुरा नहीं है।


+1, बढ़िया जवाब! मैं वास्तविक सिमुलेशन पर विचार कैसे नहीं कर सकता। क्या आप कृपया "परवलयिक सन्निकटन" पर विस्तार से बता सकते हैं (मुझे काफी समझ नहीं है)? क्या आपका मतलब केवल वेग को एकीकृत करने की विधि है, उदाहरण के लिए RK4 और यूलर? यदि हां, तो क्या आप इसे समझा सकते हैं या कम से कम कुछ जानकारी के लिंक कैसे कर सकते हैं?
पैट्रिक कज़ाचर्सकी

1
आम तौर पर आप करते हैं x'= x + v*dt। इसके बजाय उपयोग करें x' = x + v*dt + 1/2*a*dt*dt। जब dtछोटा dt^2होता है, तो छोटा होता है, इसलिए आमतौर पर इसे पारंपरिक यूलर इंटीग्रेशन इन गेम्स में छोड़ दिया जाता है। यहां dtछोटा नहीं है, इसलिए आपको त्वरण शब्द की आवश्यकता है। चूंकि dtदूसरी शक्ति के लिए उठाया गया है, यह एक द्विघात एकीकरण है, और पथ एक परवलय है, इसलिए पैराबोलिक सन्निकटन है। आरके 4 अनिवार्य रूप से उच्च डेरिवेटिव की गणना करता है, और इसलिए क्यूबिक, क्वार्टिक, क्विंटिक, आदि सन्निकटन दे सकता है। आरके 4 इसके लिए ओवरकिल है, सबसे अधिक संभावना है, क्योंकि स्थिरता महत्वपूर्ण नहीं है।

और मुझे लगता है कि वेग को पारंपरिक यूलर की तरह एकीकृत किया जाना चाहिए? v' = v + a*dt
पेट्रीक Czachurski

1
हां। आपके पास झटका नहीं है, आप इसे शून्य मान रहे हैं।

कृपया संपादन पर एक नज़र डालें।
पेट्रीक Czachurski

4

वाह! मैंने यह किया!

मैं सरल सिमुलेशन का उपयोग कर रहा हूं जो लक्ष्य बिंदु के ऊर्ध्वाधर अक्ष के पीछे उतरने के लिए पहली स्थिति लेता है - वहां से, मैं पिछली सिम्युलेटेड स्थिति लेता हूं और एक खंड बनाता हूं। अब मैं जांचता हूं कि क्या लक्ष्य बिंदु इस सेगमेंट से नीचे है। अगर यह है - हम वहाँ कूद सकते हैं।

यहाँ छवि विवरण दर्ज करें

यह जिफ़ पर एक खिलाड़ी-नियंत्रित चरित्र है। गुलाबी अनुमानित पथ है, पीले खंडों को बाद के कदम रखने की स्थिति की भविष्यवाणी की जाती है, और अंतिम खंड सफेद हो जाता है यदि लक्ष्य बिंदु इसके नीचे स्थित है, अन्यथा लाल। लाल वक्र वास्तविक उड़ान पथ है। भौतिकी राज्य के प्रक्षेप के कारण कुछ मामूली अशुद्धियाँ हैं।

गणना आश्चर्यजनक रूप से आसान हो गई, हालांकि मेरे वातावरण को उसी तरह से काम करना चाहिए जैसे ये शुद्ध गणना करते हैं ... बट में एक बड़ा दर्द था। कम से कम मैंने कुछ गंभीर बगों को हल किया, इसलिए यह एक उपयोगी अभ्यास था।

यहाँ 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

स्वीकार मुझे सही दिशा में स्थापित करने के लिए जेसन के पास जाता है! धन्यवाद!


2

आप उत्तर की "गणना" करना चाहते हैं, लेकिन मुझे यकीन है कि आपके "मुक्त पतन" भौतिकी के अत्यधिक संवादात्मक प्रकृति के कारण इसे प्राप्त करने के बाद आप इसे अपर्याप्त पाएंगे।

एक अलग दृष्टिकोण का उपयोग करने पर विचार करें: खोज। यहाँ यह सुपर मारियो AI के लिए कैसे किया जाता है: http://aigamedev.com/open/interview/mario-ai/

ए से बी तक जाने के लिए संभावित रास्तों की खोज करना मध्य-हवा में असीमित अन्तरक्रियाशीलता की अनुमति देता है जबकि अभी भी कम्प्यूटेशनल रूप से कुशल है।


1
यह केवल कुछ विशिष्ट दुनिया के लिए व्यावहारिक है। विशेष रूप से मारियो खोज ग्राफ के आकार को लगभग रेखीय होने के कारण सीमित करता है, सीमित संख्या में वेग होता है, और एक उत्कृष्ट हेयुरिस्टिक होता है। खेल के आधार पर, यह सच नहीं हो सकता है। इसके अलावा कम्प्यूटेशनल रूप से कुशल सापेक्ष है, क्योंकि यह AI संभवतः एक से अधिक वर्ण / शत्रु के लिए काम करेगा, जबकि मारियो में नियंत्रण के लिए सिर्फ एक है।
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.