दो खेल राज्यों के बीच इंटरपोल कैसे करें?


24

एक सिस्टम बनाने के लिए सबसे अच्छा पैटर्न क्या है जो सभी ऑब्जेक्ट्स को दो अद्यतन राज्यों के बीच प्रक्षेपित किया जाता है?

अपडेट हमेशा एक ही आवृत्ति पर चलेगा, लेकिन मैं किसी भी एफपीएस में प्रस्तुत करना चाहता हूं। इसलिए रेंडरिंग जितना संभव हो उतना आसान होगा, चाहे फ्रेम प्रति सेकंड, चाहे वह कम हो, या अपडेट फ्रिक्वेंसी से अधिक हो।

मैं वर्तमान फ्रेम से भविष्य के फ्रेम में 1 फ्रेम को भविष्य के प्रक्षेप में अद्यतन करना चाहता हूं। इस उत्तर में एक लिंक है जो ऐसा करने की बात करता है:

सेमी-फिक्स्ड या फुल-फिक्स्ड टाइमस्टेप?

संपादित करें: मैं प्रक्षेप में अंतिम और वर्तमान गति का उपयोग कैसे कर सकता हूं? उदाहरण के लिए सिर्फ रैखिक प्रक्षेप के साथ, यह पदों के बीच समान गति से आगे बढ़ेगा। मुझे दो बिंदुओं के बीच की स्थिति को प्रक्षेपित करने के लिए एक तरीके की आवश्यकता है, लेकिन प्रक्षेप के लिए प्रत्येक बिंदु पर गति को ध्यान में रखना चाहिए। यह कण प्रभावों की तरह कम दर सिमुलेशन के लिए उपयोगी होगा।


2
टिक्स लॉजिक टिक्स? तो आपका अपडेट एफपीएस <प्रतिपादन एफपीएस?
द कम्युनिस्ट डक

मैंने पद बदल दिया। लेकिन हाँ तर्क टिक जाता है। और नहीं, मैं अपडेट से रेंडरिंग को पूरी तरह से मुक्त करना चाहता हूं, इसलिए गेम 120HZ या 22.8HZ पर रेंडर कर सकता है और अपडेट अभी भी उसी गति से चलेगा, बशर्ते उपयोगकर्ता सिस्टम आवश्यकताओं को पूरा करता हो।
अटैकिंगहोबा

यह वास्तव में मुश्किल हो सकता है क्योंकि आपके सभी ऑब्जेक्ट पोज़िशन को रेंडर करते समय भी स्थिर रहना चाहिए (रेंडर प्रक्रिया के दौरान उन्हें बदलने से कुछ अनुचित व्यवहार हो सकता है)
Ali1S232

इंटरपोलेशन राज्य की गणना एक समय में 2 पहले से गणना किए गए अपडेट फ़्रेम के बीच करेगा। क्या यह अंतिम अद्यतन फ्रेम के बाद एक समय के लिए राज्य की गणना, एक्सट्रपलेशन के बारे में नहीं है ? चूँकि अगला अपडेट अभी तक कैप्लिकेटेड नहीं है।
माईक सेमर

मुझे लगता है कि यदि उसके पास केवल एक थ्रेड अद्यतन / प्रतिपादन है, तो यह केवल रेंडरिंग स्थिति को फिर से अपडेट करने के लिए नहीं हो सकता है। आप बस GPU पर स्थिति भेजते हैं और फिर अपडेट करते हैं।
zacharmarz

जवाबों:


22

आप अपडेट (लॉजिक टिक) को अलग करना चाहते हैं और टिक को रेंडर (रेंडर करना) करते हैं।

आपके अपडेट से दुनिया की सभी वस्तुओं की स्थिति तैयार की जाएगी।

मैं यहां दो अलग-अलग संभावनाओं को कवर करूंगा, एक जो आपने अनुरोध किया था, एक्सट्रपलेशन, और एक अन्य विधि, प्रक्षेप।

1।

एक्सट्रैपलेशन वह जगह है जहां हम अगले फ्रेम में ऑब्जेक्ट की (अनुमानित) स्थिति की गणना करेंगे, और फिर वर्तमान ऑब्जेक्ट की स्थिति के बीच इंटरपोलेट करेंगे और वह स्थिति जो ऑब्जेक्ट अगले फ्रेम में होगी।

ऐसा करने के लिए, खींची जाने वाली प्रत्येक वस्तु में एक संबद्ध velocityऔर होना चाहिए position। उस स्थिति को खोजने के लिए कि ऑब्जेक्ट अगले फ्रेम पर होगा, हम बस velocity * draw_timestepऑब्जेक्ट की वर्तमान स्थिति में जोड़ते हैं, अगले फ्रेम की अनुमानित स्थिति का पता लगाने के लिए। draw_timestepपिछली रेंडर टिक (उर्फ पिछले ड्रॉ कॉल) के बाद से जितना समय बीत चुका है, उतनी बार है।

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

2।

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


मेरी राय में, आप शायद इंटरपोलेशन चाहते हैं जैसा कि मैंने इसे वर्णित किया है, क्योंकि इसे लागू करने के लिए दो का आसान है, और आपके वर्तमान अद्यतन स्थिति के पीछे एक दूसरे (जैसे 1/60 सेकंड) के एक छोटे से अंश को खींचना ठीक है।


संपादित करें:

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

जब आप एक आकर्षित करने योग्य वस्तु बनाते हैं, तो वह खींची जाने वाली आवश्यक संपत्तियों को संग्रहीत करेगा (यानी, इसे खींचने के लिए आवश्यक राज्य जानकारी)।

इस उदाहरण के लिए, हम स्थिति और रोटेशन को स्टोर करेंगे। आप रंग या बनावट समन्वय स्थिति (जैसे कि एक बनावट स्क्रॉल) जैसे अन्य गुणों को संग्रहीत करना चाहते हैं।

रेंडर थ्रेड को आरेखित करते समय डेटा को संशोधित करने से रोकने के लिए, (रेंडर थ्रेड ड्रॉ करते समय एक वस्तु का स्थान बदल जाता है, लेकिन अन्य सभी अभी तक अपडेट नहीं किए गए हैं), हमें कुछ प्रकार के डबल बफ़रिंग को लागू करने की आवश्यकता है।

एक वस्तु इसकी दो प्रतियाँ संग्रहीत करती है previous_state। मैं उन्हें एक सरणी में डाल दिया जाएगा और के रूप में उन्हें previous_state[0]और previous_state[1]। इसी तरह इसकी दो प्रतियाँ चाहिए current_state

डबल बफर की किस कॉपी का उपयोग किया जाता है, इस पर नज़र रखने के लिए हम एक वैरिएबल स्टोर करते हैं state_index, जो अपडेट और ड्रा थ्रेड दोनों के लिए उपलब्ध है।

अपडेट थ्रेड पहले किसी ऑब्जेक्ट के सभी गुणों की गणना करता है, यह स्वयं का डेटा (कोई भी डेटा संरचनाएं जो आप चाहते हैं) का उपयोग कर रहा है। फिर, यह प्रतियां current_state[state_index]करने के लिए previous_state[state_index]प्रतियां, और ड्राइंग, के लिए नए डेटा प्रासंगिक positionऔर rotationमें current_state[state_index]। फिर यह state_index = 1 - state_indexडबल बफर की वर्तमान में इस्तेमाल की गई कॉपी को फ्लिप करने के लिए करता है ।

ऊपर दिए गए पैराग्राफ में सब कुछ एक लॉक आउट के साथ किया जाना है current_state। अद्यतन और ड्रा थ्रेड्स दोनों इस लॉक को बाहर निकालते हैं। लॉक केवल राज्य की जानकारी की प्रतिलिपि की अवधि के लिए निकाला जाता है, जो तेज है।

रेंडर थ्रेड में, आप तब स्थिति और रोटेशन पर एक रेखीय प्रक्षेप करते हैं जैसे:

current_position = Lerp(previous_state[state_index].position, current_state[state_index].position, elapsed/update_tick_length)

कहाँ elapsedसमय है कि धागा प्रस्तुत करना में बीत चुका है, अंतिम अद्यतन टिक के बाद से की राशि है, और update_tick_lengthहै कि अपने तय अद्यतन दर टिक प्रति लेता है (उदाहरण के लिए 20fps अपडेट पर, समय की राशि है update_tick_length = 0.05)।

यदि आपको नहीं पता है कि Lerpउपरोक्त कार्य क्या है, तो इस विषय पर विकिपीडिया के लेख को चेकआउट करें: लीनियर इंटरपोलेशन । हालाँकि, यदि आप नहीं जानते हैं कि लेरपिंग क्या है, तो आप शायद इंटरपोलेटेड ड्राइंग के साथ अपडाउन्ड अपडेट / ड्राइंग को लागू करने के लिए तैयार नहीं हैं।


1
+1 समान अभिविन्यास / घुमाव और अन्य सभी राज्यों के लिए किया जाना चाहिए जो समय के साथ बदलते हैं, जैसे कि कण प्रणालियों में सामग्री एनिमेशन आदि
Maik Semder

1
अच्छा बिंदु माईक, मैंने सिर्फ एक उदाहरण के रूप में स्थिति का उपयोग किया। यदि आप एक्सट्रपलेशन का उपयोग करना चाहते हैं, तो आपको किसी भी संपत्ति के "वेग" को संग्रहीत करने की आवश्यकता है (यानी उस संपत्ति के समय में परिवर्तन की दर)। अंत में, मैं वास्तव में ऐसी स्थिति के बारे में नहीं सोच सकता, जहां एक्सट्रपलेशन की तुलना में एक्सट्रपलेशन बेहतर हो, मैंने इसे केवल इसलिए शामिल किया क्योंकि पूछने वाले का सवाल इसे पसंद करता है। मैं प्रक्षेप का उपयोग करता हूं। प्रक्षेप के साथ, हमें किसी भी गुण के वर्तमान और पिछले अद्यतन परिणामों को संग्रहीत करने की आवश्यकता है, जैसा आपने कहा था।
ओल्होव्स्की

यह समस्या की व्याख्या है और प्रक्षेप और एक्सट्रपलेशन के बीच अंतर है; इसका उत्तर नहीं है।

1
मेरे उदाहरण में मैंने राज्य में स्थिति और रोटेशन को संग्रहीत किया। आप केवल राज्य में वेग (या गति) को भी स्टोर कर सकते हैं। फिर आप ठीक उसी तरह से गति के बीच दुबक जाते हैं ( Lerp(previous_speed, current_speed, elapsed/update_tick_length))। आप इसे राज्य में किसी भी संख्या में संग्रहीत करना चाहते हैं। लेरपिंग केवल आपको दो मानों के बीच एक मान देता है, जिसे एक lerp फ़ैक्टर दिया गया है।
ओलहोवस्की

1
कोणीय आंदोलन के प्रक्षेप के लिए इसे एलईआरपी के बजाय स्लरप का उपयोग करने की सिफारिश की जाती है। सबसे आसान दोनों राज्यों के quaternions और उनके बीच थप्पड़ मारना होगा। अन्यथा कोणीय वेग और कोणीय त्वरण के लिए समान नियम लागू होते हैं। क्या आपके पास कंकाल एनीमेशन के लिए एक परीक्षण-मामला है?
माईक सेमर

-2

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

मान लीजिए कि आपके पास स्थिति x पर एक बंदर है। अब आपके पास एक "एडएक्स" भी है जिसमें आप कीबोर्ड या किसी अन्य नियंत्रण के आधार पर प्रति फ्रेम बंदर की स्थिति में जोड़ते हैं। यह तब तक काम करेगा जब तक आपके पास गारंटी फ्रेम दर है। मान लीजिए कि आपका x 100 है और आपका AddX 10 है। 10 फ्रेम के बाद, आपका x + = addX 200 तक जमा होना चाहिए।

अब, addX के बजाय, जब आपके पास एक चर फ्रेम दर है, तो आपको वेग और त्वरण के संदर्भ में सोचना चाहिए। मैं आपको इस अंकगणित के बारे में बताऊंगा लेकिन यह बहुत ही सरल है। हम जानना चाहते हैं कि आप प्रति मिलीसेकंड (1/1000 वें सेकंड में) कितनी दूर यात्रा करना चाहते हैं

यदि आप 30 एफपीएस के लिए शूटिंग कर रहे हैं, तो आपका वेलएक्स एक सेकंड का 1 / 3rd होना चाहिए (30 एफपीएस पर अंतिम उदाहरण से 10 फ्रेम) और आप जानते हैं कि आप उस समय में 100 'x' यात्रा करना चाहते हैं, इसलिए अपना वेलएक्स सेट करें 100 दूरी / 10 एफपीएस या प्रति फ्रेम 10 दूरी। मिलीसेकंड में, वह 1 दूरी x प्रति 3.3 मिलीसेकंड या 0.3 'x' प्रति मिलीसेकंड पर काम करता है।

अब, जब आप अपडेट करते हैं, तो आपको बस इतना करना होगा कि बीते हुए समय का पता लगा लें। चाहे 33 एमएस उत्तीर्ण हुए हों (एक सेकंड का 1/30 वां) या जो भी हो, आप सिर्फ मिलीसेकंड पास की संख्या से दूरी 0.3 गुणा करते हैं। इसका मतलब है कि आपको एक टाइमर की आवश्यकता है जो आपको ms (मिलीसेकंड) सटीकता देता है लेकिन अधिकांश टाइमर आपको यह देते हैं। बस कुछ इस तरह से करें:

var startTime = getTimeInMillisecond ()

... बाद में ...

var time = getTimeInMillisecond ()

var elapsedTime = समय-प्रारंभ समय

startTime = समय

... अब अपनी सभी दूरी की गणना करने के लिए इस elapsedTime का उपयोग करें।


1
उसके पास वैरिएबल अपडेट रेट नहीं है। उसकी एक निश्चित अद्यतन दर है। सच कहूं तो, मैं वास्तव में नहीं जानता कि आप किस बिंदु को यहां बनाने की कोशिश कर रहे हैं: /
ओलहोव्स्की

1
??? -1। यह पूरी बात है, मैं एक गारंटीकृत अद्यतन दर कर रहा हूं, लेकिन एक चर रेंडर दर है, और मैं चाहता हूं कि यह बिना किसी रूकावट के सुचारू हो।
अटैकिंगहोबो

परिवर्तनीय अद्यतन दरें नेटवर्क गेम, प्रतिस्पर्धी गेम्स, रीप्ले सिस्टम, या किसी अन्य चीज़ के साथ अच्छी तरह से काम नहीं करती हैं जो गेम-प्ले पर नियतात्मक होने पर निर्भर करती हैं।
अटैकिंगहोबो

1
निश्चित अद्यतन भी छद्म घर्षण के आसान एकीकरण की अनुमति देता है। उदाहरण के लिए, यदि आप अपनी गति को 0.9 प्रत्येक फ्रेम से गुणा करना चाहते हैं, तो आप यह कैसे पता लगा सकते हैं कि यदि आपके पास एक तेज या धीमा फ्रेम है तो कितना गुणा करना है? निश्चित अद्यतन को कभी-कभी बहुत पसंद किया जाता है - वस्तुतः सभी भौतिकी सिमुलेशन एक निश्चित अद्यतन दर का उपयोग करते हैं।
ओलहोवस्की

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