কীভাবে স্টার্টকরোটিন / ফলন ফেরতের প্যাটার্নটি ইউনিটিতে কাজ করে?


134

আমি Coroutines নীতি বুঝতে। আমি জানি যে কীভাবে ইউনিটিতে সি # তে কাজ করার জন্য স্ট্যান্ডার্ড StartCoroutine/ yield returnপ্যাটার্ন পাবেন, উদাহরণস্বরূপ কোনও পদ্ধতির IEnumeratorমাধ্যমে ফিরে আসার অনুরোধ করুন StartCoroutineএবং সেই পদ্ধতিতে কিছু করুন, yield return new WaitForSeconds(1);দ্বিতীয় অপেক্ষা করার জন্য করুন, তারপরে অন্য কিছু করুন something

আমার প্রশ্ন: সত্যি কি ঘটছে পর্দার পিছনে? কী StartCoroutineসত্যিই কি? কি IEnumeratorহয় WaitForSecondsফিরে? StartCoroutineডাকা পদ্ধতির "অন্য কিছু" অংশে কীভাবে ফিরে আসবে নিয়ন্ত্রণ? এই সমস্ত কীভাবে ইউনিটির সম্মতিযুক্ত মডেলটির সাথে যোগাযোগ করে (যেখানে কর্টাইন ব্যবহার না করে একই সাথে প্রচুর জিনিস চলছে)?


3
সি # সংকলক এমন পদ্ধতিগুলিকে রূপান্তর করে যা IEnumerator/ IEnumerable(বা জেনেরিক সমতুল্য) এবং yieldকীওয়ার্ড ধারণ করে । পুনরুক্তিকারী অনুসন্ধান করুন।
ড্যামিয়েন_ও_বিশ্বাসীরা

4
একটি "স্টেট মেশিন" এর জন্য একটি পুনরুক্তি করা খুব সুবিধাজনক বিমূর্ততা। এটি প্রথমে বুঝতে হবে এবং আপনি ইউনিটি কর্টাইনগুলিও পাবেন। en.wikedia.org/wiki/State_machine
হান্স প্যাস্যান্ট

2
Unityক্য ট্যাগ মাইক্রোসফ্ট ইউনিটি দ্বারা সংরক্ষিত। দয়া করে এটির অপব্যবহার করবেন না।
লেক্স লি

11
আমি এই নিবন্ধটি বেশ আলোকিত করেছি: ityক্য 3 ডি কর্টাইনগুলি বিশদে
কায়

5
@ কে - আমি আশা করি আমি আপনাকে একটি বিয়ার কিনতে পারতাম। এই নিবন্ধটি ঠিক আমার প্রয়োজন ছিল। আমি আমার বোধগম্যতা নিয়ে প্রশ্ন শুরু করছিলাম কারণ মনে হচ্ছিল আমার প্রশ্নটিও বোধগম্য হয়নি, তবে নিবন্ধটি সরাসরি আমার প্রশ্নের উত্তরের কল্পনা করতে পারে তার চেয়ে ভাল। ভবিষ্যতের এসও ব্যবহারকারীদের সুবিধার্থে আপনি এই লিঙ্কটি দিয়ে একটি উত্তর যুক্ত করতে পারেন যা আমি গ্রহণ করতে পারি?
ঘোপার 21

উত্তর:


109

বিশদ লিঙ্কে Thet রেফারেন্স ইউনিটি 3 ডি কর্টাইন মারা গেছে। যেহেতু এটি মন্তব্য এবং উত্তরগুলিতে উল্লেখ করা হয়েছে আমি এখানে নিবন্ধের বিষয়বস্তু পোস্ট করতে যাচ্ছি। এই বিষয়বস্তুটি এই আয়না থেকে এসেছে ।


ইউনিটি 3 ডি কর্টাইন বিস্তারিতভাবে

গেমগুলিতে অনেকগুলি প্রক্রিয়া একাধিক ফ্রেমের কোর্সটিতে ঘটে। আপনি প্যাথফাইন্ডিংয়ের মতো 'ঘন' প্রক্রিয়া পেয়েছেন, যা প্রতিটি ফ্রেমে কঠোর পরিশ্রম করে তবে একাধিক ফ্রেমে বিভক্ত হয় যাতে ফ্রেমরেটকে খুব বেশি প্রভাবিত করতে না পারে। গেমপ্লে ট্রিগারগুলির মতো আপনি 'স্পারস' প্রক্রিয়া পেয়েছেন যা বেশিরভাগ ফ্রেম কিছুই করে না, তবে মাঝে মাঝে সমালোচনামূলক কাজ করার আহ্বান জানানো হয়। এবং আপনি দুজনের মধ্যে বিভিন্ন ধরণের প্রক্রিয়া পেয়েছেন।

যখনই আপনি এমন একটি প্রক্রিয়া তৈরি করছেন যা একাধিক ফ্রেমের উপরে সঞ্চালিত হবে - মাল্টিথ্রেডিং ছাড়াই - আপনাকে কাজটি খণ্ডগুলিতে ভাঙার কিছু উপায় খুঁজে বের করতে হবে যা এক-প্রতি-ফ্রেমে চালানো যেতে পারে। কেন্দ্রীয় লুপযুক্ত যে কোনও অ্যালগরিদমের জন্য এটি মোটামুটি সুস্পষ্ট: একটি এ * প্যাথফাইন্ডারটি এমনভাবে কাঠামোযুক্ত করা যেতে পারে যে এটি তার নোডের তালিকাগুলি অর্ধ-স্থায়ীভাবে বজায় রাখে, প্রতিটি ফ্রেমে খালি তালিকা থেকে কেবলমাত্র কয়েকটি মুষ্টি নোডের প্রক্রিয়াকরণ না করে সমস্ত কাজ একসাথে করতে। বিলম্বিতা ব্যবস্থাপনার জন্য কিছু ভারসাম্য বজায় রাখতে হবে - সর্বোপরি যদি আপনি নিজের ফ্রেমরেট প্রতি সেকেন্ডে 60 বা 30 ফ্রেমে লক করে থাকেন তবে আপনার প্রক্রিয়াটি প্রতি সেকেন্ডে কেবল 60 বা 30 টি পদক্ষেপ নিতে পারে এবং এর ফলে প্রক্রিয়াটি কেবল গ্রহণ করতে পারে সামগ্রিকভাবে অনেক দীর্ঘ একটি ঝরঝরে নকশা এক স্তরে কাজের ক্ষুদ্রতম সম্ভাব্য ইউনিট সরবরাহ করতে পারে - যেমন একক এ * নোড প্রক্রিয়া করুন - এবং শীর্ষে একত্রে বৃহত্তর অংশগুলিতে গ্রুপিংয়ের কাজ করুন - উদাহরণস্বরূপ এক্স মিলিসেকেন্ডের জন্য এ * নোডগুলি প্রক্রিয়াকরণ করতে থাকুন। (কিছু লোক একে 'টাইমসিলিং' বলে, যদিও আমি তা করি না)।

তবুও, এইভাবে কাজটি ভাঙতে দেওয়া মানে আপনাকে একটি ফ্রেম থেকে অন্য ফ্রেমে স্থিতি স্থানান্তর করতে হবে। যদি আপনি একটি পুনরাবৃত্ত অ্যালগরিদম ভেঙে চলেছেন, তবে আপনাকে পুনরাবৃত্তিতে ভাগ করা সমস্ত রাজ্য সংরক্ষণ করতে হবে, পাশাপাশি পরবর্তী পুনরুক্তিটি সম্পাদন করার জন্য ট্র্যাকিংয়ের একটি উপায়ও রয়েছে। এটি সাধারণত খুব খারাপ হয় না - একটি 'এ * পাথফাইন্ডার ক্লাস' এর নকশা মোটামুটি সুস্পষ্ট - তবে অন্যান্য ক্ষেত্রেও খুব কম আনন্দদায়ক হয়। কখনও কখনও আপনি দীর্ঘ গণনার মুখোমুখি হবেন যা ফ্রেম থেকে ফ্রেমে বিভিন্ন ধরণের কাজ করছে; তাদের রাজ্য ক্যাপচারকারী অবজেক্টটি এক ফ্রেম থেকে অন্য ফ্রেমে ডেটা দেওয়ার জন্য রাখা আধা-দরকারী 'স্থানীয়দের' একটি বৃহত্তর গণ্ডগোলের সাথে শেষ হতে পারে। এবং যদি আপনি একটি বিরল প্রক্রিয়া নিয়ে কাজ করে থাকেন তবে আপনি প্রায়শই কাজটি কখন করা উচিত তা ট্র্যাক করার জন্য একটি ক্ষুদ্র রাষ্ট্র মেশিন প্রয়োগ করে চলেছেন।

এটি কি খুব সুন্দর হবে না, যদি একাধিক ফ্রেম জুড়ে এই সমস্ত রাজ্যকে স্পষ্টভাবে ট্র্যাক করার পরিবর্তে, এবং বহুগঠিত হওয়া এবং সিঙ্ক্রোনাইজেশন এবং লকিং ইত্যাদির ব্যবস্থা না করে আপনি কেবল কোডের একক অংশ হিসাবে আপনার ফাংশনটি লিখতে পারেন এবং নির্দিষ্ট জায়গাগুলি চিহ্নিত করুন যেখানে ফাংশনটি 'বিরতি' দেওয়া উচিত এবং পরবর্তী সময়ে চালানো উচিত?

Ityক্য - সহ অনেক অন্যান্য পরিবেশ এবং ভাষা - এটি কর্টাইনগুলির আকারে সরবরাহ করে।

তারা দেখতে কেমন? "ইউনিটিস্ক্রিপ্ট" (জাভাস্ক্রিপ্ট) এ:

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

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

সি # তে:

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

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

তারা কিভাবে কাজ করে? আমাকে কেবল তাড়াতাড়ি বলতে দাও যে আমি ইউনিটি টেকনোলজিসের জন্য কাজ করি না। আমি ইউনিটির উত্স কোডটি দেখিনি। আমি কখনও ইউনিটির কর্টিন ইঞ্জিনের সাহস দেখিনি। তবে, যদি তারা এটিকে এমনভাবে প্রয়োগ করে যা আমি বর্ণনা করতে যাচ্ছি তার থেকে একেবারে আলাদা, তবে আমি বেশ অবাক হব। যদি ইউটিউর থেকে থাকা কেউ যদি চিমিয়াম করতে চান এবং এটি কীভাবে বাস্তবে কাজ করে সে সম্পর্কে কথা বলতে চান, তবে তা দুর্দান্ত।

বড় চিহ্নগুলি সি # সংস্করণে রয়েছে। প্রথমত, নোট করুন যে ফাংশনটির জন্য রিটার্ন টাইপটি আইইনুমরেটর। এবং দ্বিতীয়ত, নোট করুন যে বিবৃতিগুলির একটি হ'ল ফলন ফেরত। এর অর্থ হ'ল ফলন অবশ্যই একটি কীওয়ার্ড হওয়া উচিত এবং ইউনিটির সি # সমর্থনটি ভ্যানিলা সি # 3.5 হিসাবে, এটি অবশ্যই একটি ভ্যানিলা সি # 3.5 কীওয়ার্ড হওয়া উচিত। প্রকৃতপক্ষে, এটি এমএসডিএন-তে রয়েছে - 'ইেট্রেটর ব্লকস' নামে পরিচিত এমন কিছু বিষয়ে কথা বলছে। তো কেমন যাচ্ছে?

প্রথমত, এই আইনিমরেটর টাইপ আছে। আইনিউমরেটর টাইপটি একটি ক্রস হিসাবে কার্সরের মতো কাজ করে, দুটি উল্লেখযোগ্য সদস্য সরবরাহ করে: কারেন্ট, যা আপনাকে এমন একটি উপাদান দেয় যা কার্সারটি বর্তমানে শেষ হয়ে গেছে এবং মুভনেক্সট () ক্রমটির পরবর্তী উপাদানগুলিতে চলে আসে moves আইইনুমরেটর একটি ইন্টারফেস, কারণ এই সদস্যগুলি কীভাবে প্রয়োগ করা হয় তা নির্দিষ্ট করে না; মুভনেক্সট () কেবলমাত্র একটি টু করন্ট যোগ করতে পারে, বা এটি কোনও ফাইল থেকে নতুন মান লোড করতে পারে, বা এটি ইন্টারনেট থেকে একটি চিত্র ডাউনলোড করে এটি হ্যাশ করতে পারে এবং নতুন হ্যাশটিকে কারেন্টে সংরক্ষণ করতে পারে ... বা এটি প্রথমটির জন্য একটি জিনিসও করতে পারে ক্রমের উপাদান এবং দ্বিতীয়টির জন্য সম্পূর্ণ আলাদা different এমনকি আপনি যদি চান তবে এটি একটি অসীম ক্রম উত্পাদন করতে ব্যবহার করতে পারেন। মুভ নেক্সট () অনুক্রমের পরবর্তী মান গণনা করে (আরও মান না থাকলে মিথ্যা প্রত্যাবর্তন),

সাধারণভাবে, আপনি যদি কোনও ইন্টারফেস বাস্তবায়ন করতে চান, আপনাকে একটি ক্লাস লিখতে হবে, সদস্যদের বাস্তবায়ন করতে হবে, ইত্যাদি। আইট্রেটর ব্লকগুলি সমস্ত ঝামেলা ছাড়াই আইনিমরেটর বাস্তবায়নের একটি সুবিধাজনক উপায় - আপনি কেবল কয়েকটি নিয়ম অনুসরণ করেন এবং আইনুমেটর বাস্তবায়ন সংকলক দ্বারা স্বয়ংক্রিয়ভাবে উত্পন্ন হয়।

একটি পুনরুদ্ধারকারী ব্লক হ'ল একটি নিয়মিত ফাংশন যা (ক) আইনিমরেটর প্রদান করে এবং (খ) ফলন কীওয়ার্ডটি ব্যবহার করে। সুতরাং ফলন কীওয়ার্ডটি আসলে কী করে? এটি ক্রমটির পরবর্তী মানটি কী তা ঘোষণা করে - বা আরও মান নেই। কোডটি যে বিন্দুতে একটি ফলন ফেরতের এক্স বা ফলন বিরতির মুখোমুখি হয় সেই বিন্দুটি যেখানে আইএনিউমরেটর.মোভেক্সট () থামানো উচিত; একটি ফলন ফেরতের এক্সের ফলে মুভনেক্সট () সত্যটি ফিরে আসে এবং বর্তমানের মান X নির্ধারিত হয়, যখন একটি ফলন বিরতি মুভনেেক্সটকে () মিথ্যা ফেরত দেয়।

এখন, কৌশলটি এখানে। ক্রম দ্বারা আসল মানগুলি কী তা বিবেচনা করে তা বিবেচ্য নয়। আপনি মুভনেক্সট () বারবার কল করতে পারেন এবং বর্তমানকে উপেক্ষা করতে পারেন; গণনা এখনও সঞ্চালিত হবে। প্রতিবার মুভনেক্সট () ডাকা হলে, আপনার পুনরুক্তিকারী ব্লকটি পরবর্তী 'ফলন' বিবৃতিতে চালিত হয়, তা আসলে তা কোন ফলপ্রসূ ফল দেয় না। সুতরাং আপনি যেমন কিছু লিখতে পারেন:

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; }
}

এটি সমস্ত সময়সীমার মধ্যে যেমনটি আপনি দেখেছেন, প্রতিটি ফলন ফেরতের বিবৃতি অবশ্যই একটি এক্সপ্রেশন প্রদান করতে পারে (নালার মতো) যাতে পুনরুক্তিকারী ব্লকে আইইনিউমরেটরকে কিছু বরাদ্দ করতে পারে urrent নালগুলির একটি দীর্ঘ ক্রম হুবহু কার্যকর নয়, তবে আমরা পার্শ্ব-প্রতিক্রিয়াগুলিতে আরও আগ্রহী। আমরা না?

প্রকৃতপক্ষে, এই অভিব্যক্তিটি সহ আমরা কিছু করতে সুবিধাজনক কাজ করতে পারি। কী, যদি কেবল শূন্যতা অর্জন এবং এটিকে উপেক্ষা করার পরিবর্তে, আমরা এমন কিছু ফল পেয়েছি যা আমাদের আরও কাজ করার জন্য প্রত্যাশার সময় নির্দেশ করে? প্রায়শই আমাদের পরবর্তী ফ্রেমে সোজা বহন করা প্রয়োজন, অবশ্যই, তবে সবসময় নয়: এমন অনেক সময় আসবে যেখানে আমরা অ্যানিমেশন বা শব্দ বাজানোর পরে বা একটি নির্দিষ্ট সময় পেরিয়ে যাওয়ার পরে চালিয়ে যেতে চাই। যারা যখন (প্লে অ্যানিমেশন) ফলন শূন্য; নির্মাণগুলি কিছুটা ক্লান্তিকর, আপনি কি ভাবেন না?

ইউনিটি ইয়েলডিলস্ট্রাকশন বেস প্রকারটি ঘোষণা করে এবং কয়েকটি কংক্রিট উদ্ভূত প্রকার সরবরাহ করে যা নির্দিষ্ট ধরণের অপেক্ষার ইঙ্গিত দেয়। আপনি ওয়েটফরসেকেন্ড পেয়েছেন, যা নির্ধারিত সময় পার হওয়ার পরে কর্টিন পুনরায় শুরু করে। আপনি ওয়েটফরএন্ডঅফফ্রেম পেয়েছেন, যা পরে নির্দিষ্ট ফ্রেমে একই ফ্রেমে কর্টিন পুনরায় শুরু করে। আপনি Coroutine টাইপ নিজেই পেয়েছেন, যা, যখন কর্টিন A যখন কর্টিন বি দেয় তখন কর্টিন A শেষ না হওয়া অবধি Coroutine A বিরাম দেয়।

রানটাইম দৃষ্টিকোণ থেকে এটি দেখতে কেমন? যেমনটি আমি বলেছি, আমি ityক্যের পক্ষে কাজ করি না, তাই আমি তাদের কোডটি কখনও দেখিনি; তবে আমি ভাবতে পারি যে এটি কিছুটা এ রকম দেখাচ্ছে:

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;

অন্যান্য ক্ষেত্রে পরিচালনা করতে আরও কীভাবে ইয়েলডিলস্ট্রাকশন সাব-টাইপ যুক্ত করা যায় তা কল্পনা করা কঠিন নয় - সংকেতের জন্য ইঞ্জিন-স্তরের সমর্থন, উদাহরণস্বরূপ, ওয়েটফরসিগনাল ("সিগন্যালনাম") ইয়েলিলস্ট্রাকশন সমর্থন করে এটি যুক্ত করা যেতে পারে। আরও ইয়েলিড ইনস্ট্রাকশনস যুক্ত করে, কর্টাইনগুলি নিজেরাই আরও উদ্বেগপূর্ণ হয়ে উঠতে পারে - নতুন রিটার্ন রিটার্ন ওয়েটফোর্ডসাইনাল ("গেমওভার") পড়ার চেয়ে ভাল হয় (! সিগন্যালস.হ্যাসফায়ার্ড ("গেমওভার")) ফলন শূন্য, যদি আপনি আমাকে জিজ্ঞাসা করেন তবে বেশ কিছুটা বাদ দিয়ে ইঞ্জিনে এটি করা স্ক্রিপ্টে করার চেয়ে দ্রুততর হতে পারে fact

অ-সুস্পষ্ট ছদ্মবেশগুলির একটি দম্পতি এই সমস্ত সম্পর্কে বেশ কয়েকটি দরকারী জিনিস রয়েছে যা লোকেরা মাঝে মাঝে মিস করে যে আমি ভেবেছিলাম যে আমার উচিত।

প্রথমত, ফলন ফেরত কেবল একটি অভিব্যক্তি দেয় - যে কোনও অভিব্যক্তি - এবং ইয়েলডিস্ট্রাকশন একটি নিয়মিত প্রকার। এর অর্থ আপনি এই জাতীয় জিনিসগুলি করতে পারেন:

YieldInstruction y;

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

yield return y;

নির্দিষ্ট লাইনগুলি নতুন ওয়েটফোরসেকেন্ডগুলি () প্রদান করে, নতুন ওয়েটফরএন্ডফ্রেম () ইত্যাদি প্রদান করে, তবে এগুলি তাদের নিজস্ব অধিকার হিসাবে বিশেষ রূপ নয়।

দ্বিতীয়ত, যেহেতু এই কর্টিনগুলি কেবল পুনরাবৃত্তকারী ব্লক, আপনি নিজেরাই নিজের উপর পুনরাবৃত্তি করতে পারেন - আপনার ইঞ্জিনটি এটি করার দরকার নেই। আমি এটি আগে কোনও কর্টিনে বিঘ্নিত শর্ত যুক্ত করার জন্য ব্যবহার করেছি:

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

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

তৃতীয়ত, আপনি অন্যান্য করোটিনগুলিতে উত্সাহ অর্জন করতে পারবেন তা আপনার নিজের ইয়েলড ইন্সট্রাকশনগুলি কার্যকরভাবে সাজানোর সুযোগ দিতে পারে, যদিও এগুলি ইঞ্জিন দ্বারা প্রয়োগ করা হয়েছিল তা পারফরম্যান্সের মতো নয়। উদাহরণ স্বরূপ:

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

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

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

তবে, আমি সত্যিই এটির সুপারিশ করব না - একটি কর্টিন শুরু করার জন্য ব্যয় করা আমার পছন্দ অনুসারে কিছুটা ভারী।

উপসংহার আমি আশা করি আপনি যখন ityক্যের একজন কর্টিন ব্যবহার করেন তখন সত্যিই কী ঘটছে তার কিছুটা এটি পরিষ্কার করে দেয়। সি # এর পুনরাবৃত্তকারী ব্লকগুলি হ'ল গ্রুভি লিটল কন্সট্রাক্ট এবং আপনি যদি ইউনিটি ব্যবহার করছেন না, তবে আপনি সম্ভবত সেভাবেই তাদের সুবিধা গ্রহণ করা দরকারী বলে মনে করেন।


2
এখানে এটি পুনরুত্পাদন করার জন্য আপনাকে ধন্যবাদ। এটি দুর্দান্ত, এবং আমাকে উল্লেখযোগ্যভাবে সাহায্য করেছে।
নাইক্রভেক

96

নীচের প্রথম শিরোনামটি প্রশ্নের সরাসরি উত্তর। এর পরে দুটি শিরোনাম দৈনন্দিন প্রোগ্রামারটির জন্য আরও কার্যকর।

কোরিটাইনগুলির সম্ভবত বোরিং বাস্তবায়নের বিশদ

কর্টিনগুলি উইকিপিডিয়া এবং অন্য কোথাও ব্যাখ্যা করা হয়েছে । এখানে আমি ব্যবহারিক দৃষ্টিকোণ থেকে কিছু বিশদ সরবরাহ করব। IEnumerator, yieldইত্যাদি ইত্যাদি সি # ভাষার বৈশিষ্ট্য যা ইউনিটির কিছুটা ভিন্ন উদ্দেশ্যে ব্যবহৃত হয়।

এটি খুব সহজভাবে বলতে গেলে, একটি IEnumeratorমানগুলির সংগ্রহের দাবি রয়েছে যা আপনি একে একে অনুরোধ করতে পারেন, এ জাতীয় ধরণের List। সি # তে, একটি স্বাক্ষরের সাথে একটি ফাংশনটি ফিরিয়ে আনার IEnumeratorজন্য আসলে একটিটি তৈরি করতে এবং ফেরত দিতে হয় না, তবে সি # কে একটি অন্তর্নিহিত সরবরাহ করতে দেয় IEnumerator। ফাংশন তারপরে স্টেটমেন্টের IEnumeratorমাধ্যমে অলস ফ্যাশনে ফিরে আসা সামগ্রীর সরবরাহ করতে পারে yield return। প্রতিবার কলকারী অন্তর্ভুক্ত থেকে অন্য কোনও মান জিজ্ঞাসা করে IEnumerator, ফাংশনটি পরবর্তী yield returnবিবৃতি পর্যন্ত কার্যকর করে , যা পরবর্তী মান সরবরাহ করে। এর উপ-উত্পাদক হিসাবে, পরবর্তী মানটির অনুরোধ না করা পর্যন্ত ফাংশনটি বিরতি দেয়।

Ityক্যে, আমরা এগুলি ভবিষ্যতের মান সরবরাহ করতে ব্যবহার করি না, আমরা ফাংশনটি বিরতি দেয় এই সত্যটি কাজে লাগাই। এই শোষণের কারণে, ইউনিটিতে কর্টিনগুলি সম্পর্কে প্রচুর পরিমাণে কোনও ধারণা নেই (কোনও IEnumeratorকিছুর সাথে কী করার আছে? কী কী yield? কেন new WaitForSeconds(3)? ইত্যাদি)। "হুডের নীচে" কী ঘটে তা হ'ল, আপনি আইনিমরেটারের মাধ্যমে প্রদত্ত মানগুলি StartCoroutine()পরবর্তী মানটি কখন জিজ্ঞাসা করবেন তা সিদ্ধান্ত নিতে ব্যবহার করা হয়, এটি নির্ধারণ করে যে কখন আপনার কর্টিন পুনরায় বিরতি দেবে।

আপনার ityক্য গেমটি একক থ্রেডেড (*)

Coroutines থ্রেড নয় । ইউনিটির একটি প্রধান লুপ রয়েছে এবং আপনি যে সমস্ত ফাংশন লিখেছেন সেগুলি একই মূল থ্রেডকে ক্রমে ডাকা হচ্ছে। আপনি আপনার যে while(true);কোনও ফাংশন বা কর্টিনগুলিতে একটি রেখে এটি যাচাই করতে পারেন । এটি পুরো জিনিসটি স্থির করে দেবে, এমনকি ইউনিটির সম্পাদককেও। এটি প্রমাণ যে সমস্ত কিছুই একটি মূল থ্রেডে চলে। এই লিঙ্কটি যে কাই তার উপরের মন্তব্যে উল্লেখ করেছেন এটি একটি দুর্দান্ত সংস্থানও।

(*) Ityক্য আপনার ফাংশনগুলিকে এক থ্রেড থেকে কল করে। সুতরাং, আপনি নিজেই কোনও থ্রেড তৈরি না করলে আপনি যে কোডটি লিখেছেন তা একক থ্রেডযুক্ত। অবশ্যই ityক্য অন্য থ্রেডগুলিকে নিয়োগ করে এবং আপনি যদি চান তবে আপনি নিজেই থ্রেড তৈরি করতে পারেন।

গেম প্রোগ্রামারদের জন্য করোটাইনগুলির একটি বাস্তব বর্ণনা

মূলত, আপনি কল যখন StartCoroutine(MyCoroutine()), এটা ঠিক করার জন্য একটি নিয়মিত ফাংশন কল মত MyCoroutine(), প্রথম পর্যন্ত yield return X, যেখানে Xভালো কিছু হয় null, new WaitForSeconds(3), StartCoroutine(AnotherCoroutine()), break, ইত্যাদি এই যখন এটি একটি ফাংশন থেকে ভিন্ন শুরু। ইউনিটি সেই yield return Xলাইনে ঠিক সেই ফাংশনটিকে "বিরতি দেয়" , অন্য ব্যবসায়ের সাথে চলে এবং কিছু ফ্রেম পাস হয় এবং যখন আবার সময় আসে, ইউনিটি সেই লাইনের ঠিক পরে সেই ফাংশনটি পুনরায় শুরু করে। এটি ফাংশনে সমস্ত স্থানীয় ভেরিয়েবলের মানগুলি মনে রাখে। এইভাবে, আপনি একটি forলুপ রাখতে পারেন যা প্রতি দুই সেকেন্ডে লুপ করে, উদাহরণস্বরূপ।

যখন ityক্য আপনার কর্টিন পুনরায় শুরু করবে আপনার উপর যা আছে Xতার উপর নির্ভর করে yield return X। উদাহরণস্বরূপ, আপনি যদি ব্যবহার করেন তবে yield return new WaitForSeconds(3);3 সেকেন্ড পেরিয়ে যাওয়ার পরে এটি পুনরায় শুরু হয়। আপনি যদি ব্যবহার করেন তবে yield return StartCoroutine(AnotherCoroutine())এটি AnotherCoroutine()সম্পূর্ণরূপে সম্পন্ন হওয়ার পরে পুনরায় শুরু হয়, যা আপনাকে সময়মতো নীড়ের আচরণে সক্ষম করে। আপনি যদি সবেমাত্র একটি ব্যবহার yield return null;করেন তবে এটি পরবর্তী ফ্রেমে ডান আবার শুরু হয়।


2
এটি খুব খারাপ, ityক্যজিমেস এখনই কিছুক্ষণের জন্য নিচে নেমেছে বলে মনে হচ্ছে। রেডডিট-এ থাকা কিছু লোক সংরক্ষণাগারটির শেষ সংস্করণটি পরিচালনা করতে পেরেছিল: web.archive.org/web/20140702051454/http://unegems.com/…
ফোর্স ম্যাজিক

3
এটি খুব অস্পষ্ট এবং ঝুঁকিটি ভুল। কোডটি আসলে সংকলন করে এবং কেন এটি কাজ করে তা এখানে। এছাড়াও, এটি প্রশ্নেরও উত্তর দেয় না। stackoverflow.com/questions/3438670/…
লুই হংক

হ্যাঁ আমি অনুমান করি আমি গেম প্রোগ্রামারের দৃষ্টিকোণ থেকে "ইউনিটিতে কীভাবে কর্টিনগুলি কাজ করে" ব্যাখ্যা করেছি। আসল কোয়েশন জিজ্ঞাসা করছিল হুডের নীচে কী হচ্ছে। আপনি যদি আমার উত্তরের ভুল অংশগুলি চিহ্নিত করতে পারেন তবে আমি এটি ঠিক করতে পেরে খুশি হব।
গাজীহান অ্যালানকাস

4
আমি ফলন প্রত্যাখ্যান মিথ্যা সম্পর্কে একমত, আমি এটিকে যুক্ত করেছি কারণ কেউ আমার উত্তর না পাওয়ার জন্য সমালোচনা করেছে এবং আমি তা এমনকি দরকারী কিনা তা পর্যালোচনা করার জন্য তাড়াহুড়ো করেছিলাম এবং লিঙ্কটি কেবল যুক্ত করেছিলাম। আমি এখন এটি অপসারণ। তবে আমি মনে করি যে ityক্য একক থ্রেডযুক্ত এবং এর সাথে কর্টাইনগুলি কীভাবে ফিট করে তা সবার কাছে সুস্পষ্ট নয়। প্রচুর শুরুর ইউনিটির প্রোগ্রামারদের সাথে আমি পুরো বিষয়টি সম্পর্কে খুব অস্পষ্ট বোঝার জন্য কথা বলেছিলাম এবং এ জাতীয় ব্যাখ্যা থেকে উপকৃত হয়। আমি প্রশ্নের উত্তরসূচক উত্তর দিতে আমার উত্তর সম্পাদনা করেছি। পরামর্শ স্বাগত জানাই।
গাজিহান অ্যালানকাস

2
ঐক্য হচ্ছে না একক থ্রেডেড fwiw। এটির একটি মূল থ্রেড রয়েছে যা মনোোহাইভির লাইফেসাইক্যাল পদ্ধতিগুলি চালিত হয় - তবে এটিতে অন্যান্য থ্রেডও রয়েছে। এমনকি আপনি নিজের থ্রেড তৈরি করতে মুক্ত।
benthehutt

10

এটি সহজ হতে পারে না:

ইউনিটি (এবং সমস্ত গেম ইঞ্জিন) ফ্রেম ভিত্তিক

সম্পূর্ণ পুরো পয়েন্ট, raক্যজোটের পুরো রেসন ডি'টি এটি হ'ল এটি ফ্রেম ভিত্তিক। ইঞ্জিন আপনার জন্য "প্রতিটি ফ্রেম" জিনিস করে। (অ্যানিমেটস, পদার্থ সরবরাহ করে, পদার্থবিদ্যা ইত্যাদি করে))

আপনি জিজ্ঞাসা করতে পারেন .. "ওহ, দুর্দান্ত। আমি যদি প্রতিটি ফ্রেমে ইঞ্জিনটি আমার জন্য কিছু করতে চাই, তবে আমি কীভাবে ইঞ্জিনকে একটি ফ্রেমে এ জাতীয়-যেমন করতে বলব?"

উত্তরটা হচ্ছে ...

"কর্টিন" এর জন্য ঠিক এটি।

এটা ঠিক যে সহজ।

এবং এই বিবেচনা করুন ....

আপনি "আপডেট" ফাংশন জানেন। বেশ সহজভাবে, আপনি সেখানে যা কিছু রেখেছেন তা প্রতিটি ফ্রেম হয়ে যায় । এটি আক্ষরিক হুবহু হ'ল, করউটিন-ফলন বাক্য গঠন থেকে কোনও পার্থক্য নেই।

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;
 }

একেবারে কোনও পার্থক্য নেই।

পাদটীকা: যেমনটি সবাই দেখিয়েছে, ityক্যের কোনও থ্রেড নেই । ইউনিটিতে বা কোনও গেম ইঞ্জিনের "ফ্রেমগুলি" কোনওভাবেই থ্রেডের সাথে পুরোপুরি কোনও সংযোগ নেই।

Coroutines / উত্পাদন কেবল আপনি ইউনিটিতে ফ্রেমগুলিতে অ্যাক্সেস করেন। এটাই. (এবং প্রকৃতপক্ষে, এটি ityক্য দ্বারা সরবরাহিত আপডেট () ফাংশনের মতো একেবারে সমান।


ধন্যবাদ! তবে আপনার উত্তরটি ব্যাখ্যা করে যে কর্টাইনগুলি কীভাবে ব্যবহার করতে হয় - না কীভাবে তারা পর্দার পিছনে কাজ করে।
ঘোপার 21

1
এটা আমার আনন্দ, ধন্যবাদ। আমি যা বোঝাতে চাইছি তা বুঝতে পেরেছি - এটি এমন শুরুর জন্য উত্তম উত্তর হতে পারে যারা সর্বদা হ্যাক কর্টিনগুলি কী তা জিজ্ঞাসা করছেন। চিয়ার্স!
ফ্যাটি

1
প্রকৃতপক্ষে - কোনও উত্তর, এমনকি সামান্যও, "পর্দার আড়ালে" কি চলছে তা ব্যাখ্যা করুন। (কোনটি এটি একটি আইইনিউমরেটর যা কোনও শিডিয়ুলারের কাছে স্ট্যাক করা হয়))
ফ্যাটি

আপনি বলেছিলেন "একেবারেই কোনও পার্থক্য নেই।" তারপরে ইউনিটি কেন কার্টাইন তৈরি করেছে যখন তাদের ইতিমধ্যে যথাযথভাবে বাস্তবায়ন হচ্ছে Update()? আমার অর্থ এই দুটি বাস্তবায়ন এবং তাদের ব্যবহারের ক্ষেত্রে কমপক্ষে কিছুটা পার্থক্য থাকা উচিত যা মোটামুটি সুস্পষ্ট।
লিয়েনড্রো গেকোজো

আরে @ লিন্ড্রোজেকোজো - আমি আরও বলব যে "আপডেট" কেবলমাত্র এক ধরণের ("মূর্খ") সরলকরণ যা তারা যুক্ত করেছে। (অনেক লোক কখনই এটি ব্যবহার করে না, কেবল করোটিনগুলি ব্যবহার করে!) আমি মনে করি না যে আপনার প্রশ্নের কোনও ভাল উত্তর আছে, এটি ঠিক কতটা Unক্য।
ফ্যাটি

5

ইদানীং এটি খনন করতে হবে, এখানে একটি পোস্ট লিখেছেন - http://eppz.eu/blog/undersজ্ঞ-ienumerator - in - unity - 3d/ - যা অন্তর্নিহিত IEnumeratorইন্টারফেস, (ঘন কোড উদাহরণ সহ) উপর আলোকপাত করে , এবং এটি কর্টাইনগুলির জন্য কীভাবে ব্যবহৃত হয়।

এই উদ্দেশ্যে সংগ্রহের গণক ব্যবহার করা এখনও আমার কাছে কিছুটা অদ্ভুত বলে মনে হচ্ছে। এটি গণ্যকারীর জন্য ডিজাইন করা মনে হয় এর বিপরীত। গণ্যকারীর বিন্দু হ'ল প্রতিটি অ্যাক্সেসের ফেরত মান, তবে Coroutines এর বিন্দু হ'ল মান ফেরতের মধ্যবর্তী কোড। প্রকৃত প্রত্যাবর্তিত মান এই প্রসঙ্গে অর্থহীন।


0

ইউনিটিতে যে ভিত্তি ফাংশন আপনি স্বয়ংক্রিয়ভাবে পান সেটি হ'ল স্টার্ট () ফাংশন এবং আপডেট () ফাংশন, সুতরাং কর্টিন মূলত স্টার্ট () এবং আপডেট () ফাংশনের মতোই মূলত ফাংশন। যে কোনও পুরানো ফাংশন ফানকে () বলা যেতে পারে ঠিক একইভাবে কর্টিন বলা যেতে পারে। ইউনিটি স্পষ্টতই করোটাইনগুলির জন্য নির্দিষ্ট সীমানা নির্ধারণ করেছে যা নিয়মিত ক্রিয়াকলাপের চেয়ে আলাদা করে তোলে। পরিবর্তে একটি পার্থক্য

  void func()

তুমি লেখ

  IEnumerator func()

কর্টাইন জন্য। এবং একইভাবে আপনি কোড লাইনের মতো স্বাভাবিক ফাংশনে সময় নিয়ন্ত্রণ করতে পারেন can

  Time.deltaTime

সময় নিয়ন্ত্রণ করা যায় সেই পথে একটি করোটিনের একটি নির্দিষ্ট হ্যান্ডেল থাকে।

  yield return new WaitForSeconds();

যদিও কোনও আইনিউমেটর / কর্টিনের অভ্যন্তরে এটি করা সম্ভব নয়, এটি কর্টাইনগুলির জন্য ব্যবহৃত দরকারী জিনিসগুলির মধ্যে একটি of Coroutines এর অন্যান্য নির্দিষ্ট ব্যবহার শিখতে আপনাকে ইউনিটির স্ক্রিপ্টিং এপিআই গবেষণা করতে হবে।


0

স্টার্টকরোটাইন আইইনুমরেটর ফাংশন কল করার একটি পদ্ধতি। এটি কেবল একটি সাধারণ অকার্যকর ফাংশন কল করার অনুরূপ, কেবলমাত্র পার্থক্য হ'ল আপনি এটি আইএনমিটার ফাংশনে ব্যবহার করেন। এই জাতীয় ফাংশনটি অনন্য কারণ এটি আপনাকে একটি বিশেষ ফলন ফাংশন ব্যবহার করতে দেয় , নোট করুন যে আপনাকে অবশ্যই কিছু ফিরিয়ে দিতে হবে। যতদূর আমি জানি। এখানে আমি একসাথে টেক্সট পদ্ধতিতে একটি সাধারণ ঝলকানো গেম লিখেছি

    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));
    }
}

এরপরে আমি এটিকে আইএনএমরেটর থেকেই বললাম

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

আপনি দেখতে পাচ্ছেন যে আমি কীভাবে স্টার্টকরোটিন () পদ্ধতিটি ব্যবহার করেছি। আশা করি আমি কিছুটা সাহায্য করেছি। আমি নিজেই একজন শিক্ষানবিশ, সুতরাং আপনি যদি আমাকে সংশোধন করেন, বা আমার প্রশংসা করেন তবে যে কোনও ধরণের প্রতিক্রিয়া দুর্দান্ত।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.