বিশদ লিঙ্কে 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ক্যের একজন কর্টিন ব্যবহার করেন তখন সত্যিই কী ঘটছে তার কিছুটা এটি পরিষ্কার করে দেয়। সি # এর পুনরাবৃত্তকারী ব্লকগুলি হ'ল গ্রুভি লিটল কন্সট্রাক্ট এবং আপনি যদি ইউনিটি ব্যবহার করছেন না, তবে আপনি সম্ভবত সেভাবেই তাদের সুবিধা গ্রহণ করা দরকারী বলে মনে করেন।
IEnumerator
/IEnumerable
(বা জেনেরিক সমতুল্য) এবংyield
কীওয়ার্ড ধারণ করে । পুনরুক্তিকারী অনুসন্ধান করুন।