StartCoroutine / यील्ड रिटर्न पैटर्न वास्तव में एकता में कैसे काम करता है?


134

मैं कोरटाइन्स के सिद्धांत को समझता हूं। मुझे पता है कि एकता में C # में काम करने के लिए मानक StartCoroutine/ yield returnपैटर्न कैसे प्राप्त किया जाता है, उदाहरण के लिए, एक विधि के IEnumeratorमाध्यम से लौटते हैं StartCoroutineऔर उस पद्धति में कुछ करते हैं, yield return new WaitForSeconds(1);एक सेकंड इंतजार करने के लिए करते हैं , फिर कुछ और करते हैं।

मेरा सवाल है: वास्तव में पर्दे के पीछे क्या चल रहा है? StartCoroutineवास्तव में क्या करता है? क्या IEnumeratorहै WaitForSecondsलौट रहे? StartCoroutine"कुछ और" नामक विधि पर नियंत्रण कैसे लौटता है? यह सब कैसे एकता के संगामिति मॉडल के साथ बातचीत करता है (जहां बहुत सारी चीजें एक ही समय में बिना कोरटाइन के उपयोग के चल रही हैं)?


3
C # कंपाइलर उन तरीकों को बदल देता है जो IEnumerator/ IEnumerable(या जेनेरिक समतुल्य) लौटाते हैं और जिसमें yieldकीवर्ड होता है। पुनरावृत्तियों को देखें।
डेमियन_इन_अनबेल्टर १ien

4
"स्टेट मशीन" के लिए एक पुनरावृत्त एक बहुत सुविधाजनक अमूर्त है। यह समझें कि पहले और आपको एकता कॉरआउट भी मिल जाएगी। en.wikipedia.org/wiki/State_machine
हंस पैजेंट

2
एकता टैग Microsoft यूनिटी द्वारा आरक्षित है। कृपया इसका दुरुपयोग न करें।
लेक्स ली

11
मुझे यह लेख बहुत रोशन करने वाला लगा: Unity3D coroutines in detail
Kay

5
@ केक - काश मैं तुम्हें एक बीयर खरीद सकता। वह लेख ठीक वही है जिसकी मुझे आवश्यकता थी। मैं अपनी पवित्रता पर सवाल उठाने लगा था क्योंकि ऐसा लग रहा था कि मेरा सवाल भी समझ में नहीं आया, लेकिन लेख सीधे मेरे सवाल का बेहतर जवाब देता है जिसकी मैंने कल्पना की थी। शायद आप इस लिंक के साथ एक उत्तर जोड़ सकते हैं जिसे मैं भविष्य के SO उपयोगकर्ताओं के लाभ के लिए स्वीकार कर सकता हूं?
गॉपर

जवाबों:


109

विस्तृत लिंक में thet संदर्भित Unity3D coroutines मृत है। चूंकि यह टिप्पणियों और उत्तर में वर्णित है, इसलिए मैं यहां लेख की सामग्री पोस्ट करने जा रहा हूं। यह सामग्री इस दर्पण से आती है ।


यूनिटी 3 डी कोरटाइन विस्तार से

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

जब भी आप एक ऐसी प्रक्रिया का निर्माण कर रहे हैं, जो कई फ़्रेमों में होगी - बिना मल्टीथ्रेडिंग के - आपको काम को तोड़ने के कुछ तरीके खोजने की आवश्यकता है जो कि एक-प्रति-फ्रेम चलाया जा सकता है। केंद्रीय लूप के साथ किसी भी एल्गोरिथ्म के लिए, यह काफी स्पष्ट है: एक ए * पाथफाइंडर, उदाहरण के लिए, इस तरह संरचित किया जा सकता है कि यह अपनी नोड सूचियों को अर्द्ध-स्थायी रूप से बनाए रखता है, खुली सूची में से केवल एक मुट्ठी भर नोड्स को संसाधित करना, बजाय कोशिश करना एक ही बार में सभी काम करना। विलंबता को प्रबंधित करने के लिए कुछ संतुलन होना चाहिए - आखिरकार, यदि आप 60 या 30 फ्रेम प्रति सेकंड पर अपने फ्रैमरेट को लॉक कर रहे हैं, तो आपकी प्रक्रिया में प्रति सेकंड केवल 60 या 30 कदम होंगे, और यह प्रक्रिया सिर्फ लेने का कारण बन सकती है। कुल मिलाकर बहुत लंबा। एक साफ डिजाइन एक स्तर पर काम की सबसे छोटी संभव इकाई की पेशकश कर सकता है - जैसे एकल ए * नोड - और शीर्ष पर परत को समूहीकृत करने के तरीके को एक साथ बड़ा हिस्सा बनाने की प्रक्रिया करें - जैसे एक्स मिलीसेकंड के लिए ए * नोड को संसाधित करते रहें। (कुछ लोग इसे 'टाइमसीलिंग' कहते हैं, हालांकि मैं नहीं)।

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

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

एकता - कई अन्य वातावरणों और भाषाओं के साथ - यह कोराटाइन्स के रूप में प्रदान करता है।

वो कैसे दिखते हैं? "यूनीस्क्रिप्ट" (जावास्क्रिप्ट) में:

function LongComputation()
{
    while(someCondition)
    {
        /* Do a chunk of work */

        // Pause here and carry on next frame
        yield;
    }
}

C # में:

IEnumerator LongComputation()
{
    while(someCondition)
    {
        /* Do a chunk of work */

        // Pause here and carry on next frame
        yield return null;
    }
}

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

बड़े सुराग C # संस्करण में हैं। सबसे पहले, ध्यान दें कि फ़ंक्शन के लिए वापसी प्रकार IEnumerator है। और दूसरी बात, ध्यान दें कि एक स्टेटमेंट यील्ड रिटर्न है। इसका मतलब यह है कि उपज एक कीवर्ड होना चाहिए, और जैसा कि एकता का C # समर्थन वेनिला C # 3.5 है, यह एक वेनिला C # 3.5 कीवर्ड होना चाहिए। दरअसल, यहाँ यह MSDN में है - 'इट्रेटर ब्लॉक' नामक किसी चीज़ के बारे में बात करना। तो क्या हो रहा है?

सबसे पहले, यह IEnumerator प्रकार है। IEnumerator प्रकार एक अनुक्रम पर एक कर्सर की तरह कार्य करता है, दो महत्वपूर्ण सदस्य प्रदान करता है: वर्तमान, जो आपको एक तत्व देता है जो कर्सर वर्तमान में खत्म हो गया है, और MoveNext (), एक फ़ंक्शन जो अनुक्रम में अगले तत्व पर जाता है। क्योंकि IEnumerator एक इंटरफ़ेस है, यह निर्दिष्ट नहीं करता है कि ये सदस्य कैसे कार्यान्वित किए जाते हैं; MoveNext () बस एक समवर्ती जोड़ सकता है, या यह एक फ़ाइल से नए मूल्य को लोड कर सकता है, या यह इंटरनेट से एक छवि डाउनलोड कर सकता है और इसे हैश कर सकता है और नए हैश को करंट में संग्रहीत कर सकता है ... या यह पहली के लिए एक काम भी कर सकता है। अनुक्रम में तत्व, और दूसरे के लिए पूरी तरह से अलग। यदि आप चाहें तो अनंत अनुक्रम उत्पन्न करने के लिए भी आप इसका उपयोग कर सकते हैं। MoveNext () अनुक्रम में अगले मान की गणना करता है (यदि कोई अधिक मान नहीं है तो गलत लौटता है),

आमतौर पर, यदि आप एक इंटरफ़ेस लागू करना चाहते हैं, तो आपको एक वर्ग लिखना होगा, सदस्यों को लागू करना होगा, और इसी तरह। Iterator ब्लॉक्स बिना किसी परेशानी के IEnumerator लागू करने का एक सुविधाजनक तरीका है - आप बस कुछ नियमों का पालन करते हैं, और IEnumerator कार्यान्वयन कंपाइलर द्वारा स्वचालित रूप से उत्पन्न होता है।

एक पुनरावृत्ति अवरोधक एक नियमित कार्य है जो (a) IEnumerator लौटाता है, और (b) उपज खोजशब्द का उपयोग करता है। तो उपज खोजशब्द वास्तव में क्या करता है? यह घोषणा करता है कि अनुक्रम में अगला मूल्य क्या है - या यह कि अधिक मूल्य नहीं हैं। जिस बिंदु पर कोड एक यील्ड रिटर्न एक्स या यील्ड ब्रेक का सामना करता है वह बिंदु है जिस पर IEnumerator.MoveNext () बंद होना चाहिए; एक यील्ड रिटर्न X, MoveNext () को सही रिटर्न करने का कारण बनता है और Current को वैल्यू X सौंपा जा सकता है, जबकि एक यील्ड ब्रेक के कारण MoveNext () गलत वापस आ जाता है।

अब, यहाँ चाल है। यह मायने नहीं रखता है कि अनुक्रम द्वारा लौटाए गए वास्तविक मूल्य क्या हैं। आप MoveNext () को बार-बार कॉल कर सकते हैं और करंट को अनदेखा कर सकते हैं; अभिकलन अभी भी प्रदर्शन किया जाएगा। हर बार MoveNext () को कहा जाता है, आपका पुनरावृत्ति अवरोधक अगले 'उपज' स्टेटमेंट पर चलता है, चाहे वह वास्तव में कोई भी पैदावार क्यों न हो। तो आप कुछ इस तरह लिख सकते हैं:

IEnumerator TellMeASecret()
{
  PlayAnimation("LeanInConspiratorially");
  while(playingAnimation)
    yield return null;

  Say("I stole the cookie from the cookie jar!");
  while(speaking)
    yield return null;

  PlayAnimation("LeanOutRelieved");
  while(playingAnimation)
    yield return null;
}

और जो आपने वास्तव में लिखा है वह एक इटैलर ब्लॉक है जो अशक्त मानों का एक लंबा अनुक्रम उत्पन्न करता है, लेकिन जो महत्वपूर्ण है वह उन गणनाओं के कार्य के साइड-इफेक्ट्स हैं। आप इस तरह से एक साधारण लूप का उपयोग करके इस कोरआउट को चला सकते हैं:

IEnumerator e = TellMeASecret();
while(e.MoveNext()) { }

या, अधिक उपयोगी रूप से, आप इसे अन्य कार्यों के साथ मिला सकते हैं:

IEnumerator e = TellMeASecret();
while(e.MoveNext()) 
{ 
  // If they press 'Escape', skip the cutscene
  if(Input.GetKeyDown(KeyCode.Escape)) { break; }
}

यह सभी समय में है जैसा कि आपने देखा है, प्रत्येक यील्ड रिटर्न स्टेटमेंट में एक अभिव्यक्ति (जैसे शून्य) प्रदान करनी चाहिए ताकि इटरेटर ब्लॉक के पास वास्तव में IEnumerator.Current को असाइन करने के लिए कुछ हो। नल का एक लंबा अनुक्रम बिल्कुल उपयोगी नहीं है, लेकिन हम दुष्प्रभावों में अधिक रुचि रखते हैं। क्या हम नहीं हैं?

वास्तव में उस अभिव्यक्ति के साथ हम कुछ काम कर सकते हैं। क्या होगा, अगर सिर्फ अशक्त करने और इसे नजरअंदाज करने के बजाय, हमने कुछ ऐसा किया जो संकेत दिया कि जब हमें अधिक काम करने की आवश्यकता होगी? अक्सर हमें अगले फ्रेम पर सीधे ले जाने की आवश्यकता होगी, निश्चित रूप से, लेकिन हमेशा नहीं: ऐसे बहुत से समय होंगे जहां हम एनीमेशन या साउंड बजने के बाद या किसी विशेष राशि के बीतने के बाद ले जाना चाहते हैं। जबकि (playAnimation) उपज वापसी अशक्त; निर्माण थकाऊ हैं, आपको नहीं लगता?

एकता YieldInstruction बेस प्रकार की घोषणा करती है, और कुछ ठोस व्युत्पन्न प्रकार प्रदान करती है जो विशेष प्रकार के प्रतीक्षा का संकेत देती हैं। आपको WaitForSeconds मिल गए हैं, जो निर्धारित समय बीत जाने के बाद कोरटाइन को फिर से शुरू करता है। आपको WaitForEndOfFrame मिला है, जो बाद में एक ही फ्रेम में एक विशेष बिंदु पर कोरआउट को फिर से शुरू करता है। आपको कोराटाइन प्रकार खुद ही मिल गया है, जो कि, जब करौटीन ए पैदावार कर लेता है, तो करौटीन ए तब तक रुकता है, जब तक कि कोरोटीन बी समाप्त नहीं हो जाता।

रनटाइम पॉइंट से यह कैसा दिखता है? जैसा कि मैंने कहा, मैं एकता के लिए काम नहीं करता, इसलिए मैंने उनका कोड कभी नहीं देखा; लेकिन मुझे लगता है कि यह इस तरह एक छोटा सा लग सकता है:

List<IEnumerator> unblockedCoroutines;
List<IEnumerator> shouldRunNextFrame;
List<IEnumerator> shouldRunAtEndOfFrame;
SortedList<float, IEnumerator> shouldRunAfterTimes;

foreach(IEnumerator coroutine in unblockedCoroutines)
{
    if(!coroutine.MoveNext())
        // This coroutine has finished
        continue;

    if(!coroutine.Current is YieldInstruction)
    {
        // This coroutine yielded null, or some other value we don't understand; run it next frame.
        shouldRunNextFrame.Add(coroutine);
        continue;
    }

    if(coroutine.Current is WaitForSeconds)
    {
        WaitForSeconds wait = (WaitForSeconds)coroutine.Current;
        shouldRunAfterTimes.Add(Time.time + wait.duration, coroutine);
    }
    else if(coroutine.Current is WaitForEndOfFrame)
    {
        shouldRunAtEndOfFrame.Add(coroutine);
    }
    else /* similar stuff for other YieldInstruction subtypes */
}

unblockedCoroutines = shouldRunNextFrame;

यह कल्पना करना मुश्किल नहीं है कि अन्य मामलों को संभालने के लिए YieldInstruction सबटाइप्स को कैसे जोड़ा जा सकता है - संकेतों के लिए इंजन-स्तर का समर्थन, उदाहरण के लिए, एक WaitForSignal ("सिग्नलनाम") के साथ जोड़ा जा सकता है YieldInstruction इसका समर्थन कर रहे हैं। अधिक YieldInstructions जोड़कर, स्वयं coroutines अधिक अभिव्यंजक बन सकते हैं - यील्ड रिटर्न नया WaitForSignal ("GameOver"), पढ़ने के लिए अच्छा है (! सिग्नल .asFired ("GameOver)") उपज वापसी, यदि आप मुझसे पूछें, तो काफी अलग! तथ्य यह है कि इंजन में इसे करना स्क्रिप्ट में करने से अधिक तेज हो सकता है।

गैर-स्पष्ट प्रभाव के एक जोड़े में इस सब के बारे में उपयोगी चीजें हैं जो लोगों को कभी-कभी याद आती हैं कि मुझे लगा कि मुझे इंगित करना चाहिए।

सबसे पहले, यील्ड रिटर्न सिर्फ एक अभिव्यक्ति है - किसी भी अभिव्यक्ति - और यील्डइंस्ट्रक्शन एक नियमित प्रकार है। इसका मतलब है कि आप इस तरह की चीजें कर सकते हैं:

YieldInstruction y;

if(something)
 y = null;
else if(somethingElse)
 y = new WaitForEndOfFrame();
else
 y = new WaitForSeconds(1.0f);

yield return y;

विशिष्ट लाइनों की उपज नए WaitForSeconds (), यील्ड रिटर्न नए WaitForEndOfFrame (), आदि आम हैं, लेकिन वे वास्तव में अपने आप में विशेष रूप नहीं हैं।

दूसरे, क्योंकि ये कॉरटॉयन्स सिर्फ इटरेटर ब्लॉक होते हैं, आप चाहें तो अपने आप पर इनरेट कर सकते हैं - आपको इसके लिए इंजन की जरूरत नहीं है। मैंने इससे पहले एक coroutine में रुकावट की स्थिति जोड़ने के लिए इसका उपयोग किया है:

IEnumerator DoSomething()
{
  /* ... */
}

IEnumerator DoSomethingUnlessInterrupted()
{
  IEnumerator e = DoSomething();
  bool interrupted = false;
  while(!interrupted)
  {
    e.MoveNext();
    yield return e.Current;
    interrupted = HasBeenInterrupted();
  }
}

तीसरे, तथ्य यह है कि आप अन्य कोरटाइन पर उपज कर सकते हैं आप अपने खुद के YieldInstructions को लागू करने की अनुमति दे सकते हैं, भले ही वे इंजन द्वारा कार्यान्वित नहीं किए गए हों। उदाहरण के लिए:

IEnumerator UntilTrueCoroutine(Func fn)
{
   while(!fn()) yield return null;
}

Coroutine UntilTrue(Func fn)
{
  return StartCoroutine(UntilTrueCoroutine(fn));
}

IEnumerator SomeTask()
{
  /* ... */
  yield return UntilTrue(() => _lives < 3);
  /* ... */
}

हालांकि, मैं वास्तव में इसकी सिफारिश नहीं करूंगा - एक कॉरआउट शुरू करने की लागत मेरी पसंद के लिए थोड़ी भारी है।

निष्कर्ष मुझे उम्मीद है कि यह थोड़ा स्पष्ट करता है कि वास्तव में क्या हो रहा है जब आप एकता में एक कॉरआउट का उपयोग करते हैं। C # के पुनरावृत्त ब्लॉक एक छोटे से निर्माण हैं, और यहां तक ​​कि अगर आप एकता का उपयोग नहीं कर रहे हैं, तो शायद आप उन्हें उसी तरह से लाभ उठाने के लिए उपयोगी पाएंगे।


2
यहाँ पुन: प्रस्तुत करने के लिए धन्यवाद। यह उत्कृष्ट है, और इससे मुझे काफी मदद मिली।
नाईक्रोवेक

96

नीचे पहला शीर्षक प्रश्न का सीधा उत्तर है। रोजमर्रा के प्रोग्रामर के लिए दो शीर्षकों के बाद अधिक उपयोगी है।

संभवतः बोरिंग कार्यान्वयन कोराटाइन का विवरण

कोराटाइन को विकिपीडिया और अन्य जगहों पर समझाया गया है। यहाँ मैं केवल व्यावहारिक दृष्टिकोण से कुछ विवरण प्रदान करूँगा। IEnumerator, yieldआदि सी # भाषा की विशेषताएं हैं जो एकता में एक अलग उद्देश्य के लिए उपयोग की जाती हैं।

इसे बहुत सरलता से IEnumeratorकहने के लिए, मूल्यों का एक संग्रह है जो आप एक के बाद एक अनुरोध कर सकते हैं, एक तरह का List। सी # में, एक हस्ताक्षर के साथ एक फ़ंक्शन को वापस IEnumeratorकरने के लिए वास्तव में एक बनाने और वापस करने की ज़रूरत नहीं है, लेकिन सी # को एक निहित प्रदान कर सकते हैं IEnumerator। यह कार्य IEnumeratorभविष्य में उस समय की सामग्री को आलसी अंदाज में yield returnबयान के माध्यम से प्रदान कर सकता है। हर बार कॉलर उस निहितार्थ से दूसरा मूल्य मांगता है IEnumerator, फ़ंक्शन अगले yield returnकथन तक निष्पादित होता है , जो अगले मूल्य प्रदान करता है। इसके उपोत्पाद के रूप में, फ़ंक्शन अगले मूल्य का अनुरोध करने तक रुक जाता है।

एकता में, हम भविष्य के मूल्यों को प्रदान करने के लिए इनका उपयोग नहीं करते हैं, हम इस तथ्य का फायदा उठाते हैं कि फ़ंक्शन रुक जाता है। इस शोषण के कारण, एकता में कोरटाइन के बारे में बहुत सारी चीजें समझ में नहीं आती हैं ( IEnumeratorकुछ भी करने के लिए क्या करना है? क्या है yield? क्यों new WaitForSeconds(3)? आदि)। "हुड के नीचे" क्या होता है, IEnumerator के माध्यम से आपके द्वारा प्रदान किए जाने वाले मानों का उपयोग StartCoroutine()यह तय करने के लिए किया जाता है कि अगले मूल्य के लिए कब पूछा जाए, जो निर्धारित करता है कि आपका कोरटाइन फिर से अनपॉज़ करेगा।

आपका यूनिटी गेम सिंगल थ्रेडेड (*) है

Coroutines धागे नहीं हैं । एकता का एक मुख्य लूप है और आपके द्वारा लिखे गए उन सभी कार्यों को क्रम में एक ही मुख्य सूत्र द्वारा बुलाया जा रहा है। आप इसे while(true);अपने किसी भी फंक्शन या कोरटाइन में डालकर सत्यापित कर सकते हैं । यह संपूर्ण चीज़ को, यहां तक ​​कि एकता संपादक को भी फ्रीज कर देगा। यह सबूत है कि सब कुछ एक मुख्य धागे में चलता है। यह लिंक जिसे Kay ने अपनी उपरोक्त टिप्पणी में उल्लेख किया है, वह भी एक महान संसाधन है।

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

खेल प्रोग्रामर के लिए कोराटाइन का एक व्यावहारिक विवरण

मूल रूप से, जब आप कॉल StartCoroutine(MyCoroutine()), यह वास्तव में करने के लिए एक नियमित रूप से समारोह कॉल की तरह है MyCoroutine(), पहले जब तक yield return Xहै, जहां Xकी तरह कुछ है null, new WaitForSeconds(3), StartCoroutine(AnotherCoroutine()), break, आदि यह वह जगह है, जब यह एक समारोह से भिन्न शुरू होता है। यूनिटी उस yield return Xलाइन पर "पॉज़" करती है, जो अन्य व्यवसाय के साथ चलती है और कुछ फ्रेम पास हो जाते हैं, और जब दोबारा समय होता है, तो यूनिटी उस लाइन के ठीक बाद उस फ़ंक्शन को फिर से शुरू करता है। यह फ़ंक्शन में सभी स्थानीय चर के लिए मानों को याद करता है। इस तरह, आपके पास एक forलूप हो सकता है जो हर दो सेकंड में लूप करता है, उदाहरण के लिए।

जब एकता फिर से शुरू होगी तो आपका कोरटाइन इस बात पर निर्भर करेगा कि Xआपके में क्या था yield return X। उदाहरण के लिए, यदि आप उपयोग करते हैं yield return new WaitForSeconds(3);, तो यह 3 सेकंड बीतने के बाद फिर से शुरू होता है। यदि आपने उपयोग किया है yield return StartCoroutine(AnotherCoroutine()), तो यह AnotherCoroutine()पूरी तरह से हो जाने के बाद फिर से शुरू हो जाता है, जो आपको समय में घोंसले के व्यवहार के लिए सक्षम बनाता है। यदि आपने अभी-अभी उपयोग किया है yield return null;, तो यह अगले फ्रेम में ठीक से शुरू होता है।


2
यह बहुत बुरा है, यूनिटीज अब तक कुछ समय के लिए नीचे लग रहे हैं। Reddit पर कुछ लोग पुरालेख का अंतिम संस्करण प्राप्त करने में कामयाब रहे: web.archive.org/web/20140702052051454/http://unitygems.com/…
ForceMagic

3
यह बहुत अस्पष्ट है और जोखिम गलत है। यहां बताया गया है कि कोड वास्तव में कैसे संकलित होता है और यह क्यों काम करता है। इसके अलावा, यह प्रश्न का उत्तर नहीं देता है। stackoverflow.com/questions/3438670/…
लुई होंग

हाँ, मुझे लगता है कि मैंने एक गेम प्रोग्रामर के दृष्टिकोण से "यूनिटी वर्क में कोराउटाइन कैसे करते हैं" समझाया। वास्तविक गढ़ पूछ रहा था कि हुड के नीचे क्या हो रहा है। यदि आप मेरे उत्तर के गलत भागों को इंगित कर सकते हैं तो मुझे इसे ठीक करने में खुशी होगी।
गाजीहान अलंकस

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

2
एकता है नहीं एकल थ्रेड fwiw। इसका एक मुख्य सूत्र है कि मोनोबेहेवियर जीवनचक्र विधियाँ-- में चलती हैं, लेकिन इसमें अन्य सूत्र भी हैं। आप अपने स्वयं के धागे बनाने के लिए भी स्वतंत्र हैं।
बंथेहुत

10

यह सरल नहीं हो सकता है:

एकता (और सभी खेल इंजन) फ्रेम आधारित हैं

पूरे पूरे बिंदु, एकता के पूरे raison d'etre, यह है कि यह फ्रेम आधारित है। इंजन आपके लिए "प्रत्येक फ्रेम" चीजें करता है। (एनिमेट करता है, वस्तुओं का प्रतिपादन करता है, भौतिकी करता है और इसी तरह।)

आप पूछ सकते हैं .. "ओह, यह बहुत अच्छा है। क्या होगा यदि मैं चाहता हूं कि इंजन मेरे लिए प्रत्येक फ्रेम के लिए कुछ करे? मैं कैसे इंजन को ऐसे-और-ऐसे करने के लिए कहता हूं?"

उत्तर है ...

यह वास्तव में एक "coroutine" है।

यह इतना आसान है।

और इस पर विचार करें…।

आप "अपडेट" फ़ंक्शन को जानते हैं। काफी बस, वहाँ कुछ भी आप हर फ्रेम में किया जाता है । यह वास्तव में बिल्कुल समान है, कोरटाइन-उपज सिंटैक्स से बिल्कुल भी कोई अंतर नहीं है।

void Update()
 {
 this happens every frame,
 you want Unity to do something of "yours" in each of the frame,
 put it in here
 }

...in a coroutine...
 while(true)
 {
 this happens every frame.
 you want Unity to do something of "yours" in each of the frame,
 put it in here
 yield return null;
 }

बिलकुल कोई फर्क नहीं है।

फुटनोट: जैसा कि सभी ने बताया है, एकता में बस कोई सूत्र नहीं है । एकता या किसी भी खेल इंजन में "फ्रेम" का किसी भी तरह से धागे से कोई संबंध नहीं है।

Coroutines / उपज बस आप कैसे एकता में फ्रेम का उपयोग कर रहे हैं। बस। (और वास्तव में, यह बिल्कुल एकता द्वारा प्रदान किए गए अद्यतन () फ़ंक्शन के समान है।) बस इतना ही है, यह इतना आसान है।


धन्यवाद! लेकिन आपका जवाब बताता है कि कोरटाइन का उपयोग कैसे किया जाता है - न कि कैसे वे पर्दे के पीछे काम करते हैं।
गॉपर 21

1
यह मेरी खुशी की बात है, धन्यवाद। मैं समझता हूं कि आपका क्या मतलब है - यह शुरुआती लोगों के लिए एक अच्छा जवाब हो सकता है जो हमेशा पूछ रहे हैं कि बिल्ली के कोरआउट क्या हैं। चीयर्स!
फेटी

1
वास्तव में - जवाब में से कोई भी, यहां तक ​​कि थोड़ा भी समझाएं कि "पर्दे के पीछे" क्या चल रहा है। (जो कि यह एक IEnumerator जो एक अनुसूचक के लिए खड़ी हो जाता है।)
fattie

आपने कहा "बिल्कुल कोई अंतर नहीं है।" फिर एकता ने कोरटाइन्स क्यों बनाए जब उनके पास पहले से ही एक सटीक कार्य कार्यान्वयन है Update()? मेरा मतलब है कि इन दो कार्यान्वयन और उनके उपयोग के मामलों के बीच कम से कम मामूली अंतर होना चाहिए जो कि काफी स्पष्ट है।
लिएंड्रो गेकोजो

हे @LeandroGecozo - मैं और कहूंगा, कि "अपडेट" एक तरह का ("मूर्खतापूर्ण") सरलीकरण है जो उन्होंने जोड़ा। (बहुत से लोग इसका उपयोग कभी नहीं करते हैं, बस कोरटाइन का उपयोग करें!) मुझे नहीं लगता कि आपके सवाल का कोई अच्छा जवाब है, यह सिर्फ एकता है।
फेटी

5

हाल ही में खुदाई की है, यहाँ एक पोस्ट लिखा है - http://eppz.eu/blog/understanding-ienumerator-in-unity-3d/ - जो कि आंतरिक (घने कोड उदाहरणों के साथ) पर प्रकाश डालती IEnumeratorहै, अंतर्निहित इंटरफ़ेस: और इसका उपयोग कोरटाइन के लिए कैसे किया जाता है।

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


0

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

  void func()

तुम लिखो

  IEnumerator func()

coroutines के लिए। और इसी तरह से आप कोड लाइनों जैसे सामान्य कार्यों में समय को नियंत्रित कर सकते हैं

  Time.deltaTime

जिस तरह से समय को नियंत्रित किया जा सकता है, उस पर एक विशिष्ट नियंत्रण है।

  yield return new WaitForSeconds();

हालाँकि यह केवल IEnumerator / Coroutine के अंदर करने के लिए संभव नहीं है, लेकिन यह उन उपयोगी चीजों में से एक है, जिनके लिए Coroutines का उपयोग किया जाता है। आपको Coroutines के अन्य विशिष्ट उपयोगों को जानने के लिए Unity की स्क्रिप्टिंग API पर शोध करना होगा।


0

StartCoroutine एक IEnumerator फ़ंक्शन को कॉल करने की एक विधि है। यह केवल एक साधारण शून्य फ़ंक्शन को कॉल करने के समान है, बस अंतर यह है कि आप इसे IEnumerator फ़ंक्शन पर उपयोग करते हैं। इस प्रकार का फ़ंक्शन अद्वितीय है क्योंकि यह आपको एक विशेष उपज फ़ंक्शन का उपयोग करने की अनुमति दे सकता है, ध्यान दें कि आपको कुछ वापस करना होगा। जहाँ तक मुझे पता है। यहाँ मैंने एकता में एक सरल झिलमिलाहट खेल ओवर टेक्स्ट विधि लिखी

    public IEnumerator GameOver()
{
    while (true)
    {
        _gameOver.text = "GAME OVER";
        yield return new WaitForSeconds(Random.Range(1.0f, 3.5f));
        _gameOver.text = "";
        yield return new WaitForSeconds(Random.Range(0.1f, 0.8f));
    }
}

फिर मैंने इसे IEnumerator से बाहर बुलाया

    public void UpdateLives(int currentlives)
{
    if (currentlives < 1)
    {
        _gameOver.gameObject.SetActive(true);
        StartCoroutine(GameOver());
    }
}

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.