যেকোনো 2D প্ল্যাটফর্মের মূল উপাদানগুলির মধ্যে একটি হল প্রধান চরিত্র। এটি যেভাবে চলে এবং নিয়ন্ত্রিত হয় তা গেমের বায়ুমণ্ডলকে উল্লেখযোগ্যভাবে আকার দেয় — তা একটি আরামদায়ক ওল্ড-স্কুল গেম হোক বা একটি গতিশীল স্ল্যাশার। অতএব, একটি ক্যারেক্টার কন্ট্রোলার তৈরি করা একটি প্ল্যাটফর্মার বিকাশের একটি গুরুত্বপূর্ণ প্রাথমিক স্তর।
এই নিবন্ধে, আমরা স্ক্র্যাচ থেকে একটি চরিত্র তৈরি করার প্রক্রিয়াটি পুঙ্খানুপুঙ্খভাবে পরীক্ষা করব, এটিকে পদার্থবিজ্ঞানের আইন মেনে চলার সময় স্তরে ঘুরতে শেখাবো। এমনকি যদি আপনার ইতিমধ্যেই ক্যারেক্টার কন্ট্রোলার তৈরির অভিজ্ঞতা থাকে, তবে আপনি ইউনিটি 2023-এর উদ্ভাবনগুলি সম্পর্কে জানতে আগ্রহী হবেন। আমার আশ্চর্যের বিষয়, Rigidbody2D
উপাদানটির জন্য একটি দীর্ঘ-প্রতীক্ষিত Slide
পদ্ধতি যোগ করা হয়েছে, যা একটি অক্ষর নিয়ন্ত্রক লেখাকে ব্যাপকভাবে সহজ করে তোলে কাইনেম্যাটিক মোডে Rigidbody2D
ব্যবহার আরও কার্যকরভাবে করার অনুমতি দেয়। পূর্বে, এই সমস্ত কার্যকারিতা ম্যানুয়ালি প্রয়োগ করা হত।
আপনি যদি শুধুমাত্র নিবন্ধটি পড়তে চান না তবে এটি অনুশীলনে চেষ্টা করতে চান, আমি GitHub সংগ্রহস্থল ট্রেজার হান্টার থেকে একটি স্তরের টেমপ্লেট ডাউনলোড করার পরামর্শ দিচ্ছি, যেখানে প্রয়োজনীয় সম্পদ এবং আপনার চরিত্র পরীক্ষা করার জন্য একটি প্রস্তুত স্তর ইতিমধ্যেই অন্তর্ভুক্ত রয়েছে।
আমাদের প্ল্যাটফর্মারের জন্য, আমরা একটি নিয়ম সেট করেছি যে গেমটিতে শুধুমাত্র উল্লম্ব এবং অনুভূমিক পৃষ্ঠ থাকবে এবং মাধ্যাকর্ষণ বল কঠোরভাবে নীচের দিকে পরিচালিত হবে। এটি প্রাথমিক পর্যায়ে প্ল্যাটফর্মের তৈরিকে উল্লেখযোগ্যভাবে সরল করে, বিশেষ করে যদি আপনি ভেক্টর গণিতের মধ্যে পড়তে না চান।
ভবিষ্যতে, আমি আমার প্রজেক্ট ট্রেজার হান্টার-এ এই নিয়মগুলি থেকে বিচ্যুত হতে পারি, যেখানে আমি ইউনিটিতে একটি 2D প্ল্যাটফর্মারের জন্য মেকানিক্স তৈরির অন্বেষণ করি। কিন্তু এটি অন্য নিবন্ধের বিষয় হবে।
আমাদের সিদ্ধান্তের উপর ভিত্তি করে যে আন্দোলনটি অনুভূমিক পৃষ্ঠের সাথে পরিচালিত হবে, আমাদের চরিত্রের ভিত্তিটি আয়তক্ষেত্রাকার হবে। ঝোঁকযুক্ত পৃষ্ঠগুলি ব্যবহার করার জন্য একটি ক্যাপসুল-আকৃতির কোলাইডার এবং স্লাইডিংয়ের মতো অতিরিক্ত মেকানিক্স তৈরি করতে হবে।
প্রথমে, দৃশ্যে একটি খালি বস্তু তৈরি করুন এবং এটির নাম দিন ক্যাপ্টেন — এটি হবে আমাদের প্রধান চরিত্র। বস্তুতে Rigidbody2D
এবং BoxCollider2D
উপাদান যোগ করুন। Rigidbody2D
টাইপটিকে Kinematic এ সেট করুন যাতে আমরা Unity-এর অন্তর্নির্মিত পদার্থবিদ্যার ক্ষমতা ব্যবহার করার সময় চরিত্রের গতিবিধি নিয়ন্ত্রণ করতে পারি। এছাড়াও, ফ্রিজ রোটেশন Z বিকল্পটি সক্রিয় করে Z-অক্ষ বরাবর অক্ষরের ঘূর্ণন লক করুন।
আপনার সেটআপটি নীচের চিত্রের মতো হওয়া উচিত।
এখন আমাদের অধিনায়ক একটি চেহারা যোগ করা যাক. সম্পদ/টেক্সচার/ট্রেজার হান্টার/ক্যাপ্টেন ক্লাউন নোজ/স্প্রাইটস/ক্যাপ্টেন ক্লাউন নোজ/ক্যাপ্টেন ক্লাউন নোজ উইথ সোর্ড/09-আইডল সোর্ড/আইডল সোর্ড 01.png-এ টেক্সচার খুঁজুন এবং এর পিক্সেল প্রতি ইউনিট মান 32 এ সেট করুন। এই কোর্সে এই মানটি ব্যবহার করুন কারণ আমাদের টেক্সচারগুলি এই রেজোলিউশনের সাথে তৈরি করা হয়েছে। স্প্রাইট মোডকে একক সেট করুন এবং সুবিধার জন্য, পিভটকে নীচে সেট করুন। প্রয়োগ বোতামে ক্লিক করে পরিবর্তনগুলি প্রয়োগ করতে ভুলবেন না। নিশ্চিত করুন যে সমস্ত সেটিংস সঠিকভাবে সম্পন্ন হয়েছে।
এই নিবন্ধে, আমরা অক্ষর অ্যানিমেশন স্পর্শ করব না, তাই আপাতত, আমরা শুধুমাত্র একটি স্প্রাইট ব্যবহার করব। ক্যাপ্টেন অবজেক্টে, অ্যাপিয়ারেন্স নামে একটি নেস্টেড অবজেক্ট তৈরি করুন এবং পূর্বে কনফিগার করা স্প্রাইট উল্লেখ করে এতে একটি স্প্রাইট রেন্ডারার উপাদান যোগ করুন।
যখন আমি ক্যাপ্টেনের ছবিটি জুম করেছিলাম, আমি লক্ষ্য করেছি যে এটি ভুল স্প্রাইট সেটিংসের কারণে বেশ ঝাপসা ছিল। এটি ঠিক করতে, প্রজেক্ট উইন্ডোতে Idle Sword 01 টেক্সচার নির্বাচন করুন এবং ফিল্টার মোডকে পয়েন্টে সেট করুন (কোন ফিল্টার নেই)। এখন ছবিটা অনেক ভালো লাগছে।
এই মুহুর্তে, কলাইডারের অবস্থান এবং আমাদের ক্যাপ্টেনের ইমেজ মেলে না এবং কলাইডারটি আমাদের প্রয়োজনের জন্য খুব বড় হয়ে উঠছে।
আসুন এটি ঠিক করি এবং নীচে অক্ষরের পিভটটিকে সঠিকভাবে অবস্থান করি। আকার নির্বিশেষে একই স্তরে তাদের বসানোর সুবিধার্থে আমরা গেমের সমস্ত বস্তুতে এই নিয়মটি প্রয়োগ করব।
এই প্রক্রিয়া পরিচালনার সহজতার জন্য, নীচে দেখানো হিসাবে, পিভট সেট সহ টগল টুল হ্যান্ডেল অবস্থান ব্যবহার করুন।
পরবর্তী পদক্ষেপটি হল আমাদের নায়কের কোলাইডারকে সামঞ্জস্য করা যাতে এর পিভট পয়েন্ট ঠিক কেন্দ্রের নীচে থাকে। কোলাইডারের আকার অক্ষরের মাত্রার সাথে হুবহু মেলে। প্রয়োজনীয় নির্ভুলতা অর্জনের জন্য কোলাইডারের অফসেট এবং সাইজ প্যারামিটারের পাশাপাশি নেস্টেড উপস্থিত অবজেক্টের অবস্থান সামঞ্জস্য করুন।
কোলাইডারের অফসেট.এক্স প্যারামিটারে বিশেষ মনোযোগ দিন: এর মান অবশ্যই 0 হতে হবে। এটি বস্তুর কেন্দ্রের সাপেক্ষে কোলাইডারের প্রতিসাম্য স্থাপন নিশ্চিত করবে, যা পরবর্তী অক্ষর বাম এবং ডানে ঘূর্ণনের জন্য অত্যন্ত গুরুত্বপূর্ণ, যেখানে মান Transform.Scale.X-এর -1 এবং 1-এ পরিবর্তিত হয়েছে৷ কোলাইডারটি যথাস্থানে থাকা উচিত এবং চাক্ষুষ ঘূর্ণন স্বাভাবিক হওয়া উচিত৷
প্রথম এবং সর্বাগ্রে, অক্ষর-প্রধান নায়ক, এনপিসি, বা শত্রু-ই হোক না কেন- ভৌত জগতের সাথে যোগাযোগ করতে শেখানো অপরিহার্য। উদাহরণস্বরূপ, চরিত্রগুলিকে সমতল পৃষ্ঠে হাঁটতে, এটি থেকে লাফ দিতে এবং মাধ্যাকর্ষণ শক্তির সাপেক্ষে তাদের মাটিতে ফিরিয়ে আনতে সক্ষম হওয়া উচিত।
ইউনিটিতে একটি অন্তর্নির্মিত পদার্থবিদ্যা ইঞ্জিন রয়েছে যা দেহের গতি নিয়ন্ত্রণ করে, সংঘর্ষ পরিচালনা করে এবং বস্তুর উপর বাহ্যিক শক্তির প্রভাব যোগ করে। এই সব Rigidbody2D
উপাদান ব্যবহার করে প্রয়োগ করা হয়. যাইহোক, অক্ষরের জন্য, এটি একটি আরও নমনীয় টুল থাকা দরকারী যা ভৌত জগতের সাথে যোগাযোগ করে যখন ডেভেলপারদের বস্তুর আচরণের উপর আরও নিয়ন্ত্রণ দেয়।
আমি আগেই বলেছি, ইউনিটির পূর্ববর্তী সংস্করণগুলিতে, বিকাশকারীদের এই সমস্ত যুক্তি নিজেরাই প্রয়োগ করতে হয়েছিল। যাইহোক, ইউনিটি 2023-এ, একটি নতুন পদ্ধতি Slide
Rigidbody2D
উপাদানে যোগ করা হয়েছিল, যা সঞ্চালিত আন্দোলন সম্পর্কে সমস্ত প্রয়োজনীয় তথ্য প্রদান করার সময় ভৌত বস্তুর নমনীয় নিয়ন্ত্রণের অনুমতি দেয়।
আসুন একটি CharacterBody
ক্লাস তৈরি করে শুরু করা যাক, যেটিতে ভৌত জগতে চলমান চরিত্রগুলির জন্য মৌলিক যুক্তি থাকবে। এই ক্লাসটি Kinematic
মোডে Rigidbody2D
ব্যবহার করবে, যা আমরা ইতিমধ্যে আমাদের চরিত্রে যোগ করেছি। সুতরাং, প্রথম জিনিস এই উপাদান একটি রেফারেন্স যোগ করা হয়.
public class CharacterBody : MonoBehaviour { [SerializeField] private Rigidbody2D _rigidbody; }
কখনও কখনও, চরিত্রের গতিশীলতার জন্য, মাধ্যাকর্ষণকে স্বাভাবিকের চেয়ে আরও শক্তিশালীভাবে কাজ করা প্রয়োজন। এটি অর্জন করতে, আমরা 1 এর প্রারম্ভিক মান সহ একটি অভিকর্ষ প্রভাব ফ্যাক্টর যোগ করব এবং এই ফ্যাক্টরটি 0 এর কম হতে পারে না।
[Min(0)] [field: SerializeField] public float GravityFactor { get; private set; } = 1f;
আমাদেরকেও সংজ্ঞায়িত করতে হবে কোন বস্তুগুলিকে দুর্গম পৃষ্ঠ হিসাবে বিবেচনা করা হবে। এটি করার জন্য, আমরা একটি ক্ষেত্র তৈরি করব যা আমাদের প্রয়োজনীয় স্তরগুলি নির্দিষ্ট করার অনুমতি দেয়।
[SerializeField] private LayerMask _solidLayers;
আমরা অক্ষরের গতি সীমিত করব যাতে তাদের অত্যধিক উচ্চ গতির বিকাশ থেকে বিরত রাখা যায়, উদাহরণস্বরূপ, মাধ্যাকর্ষণ সহ কিছু বাহ্যিক প্রভাবের কারণে। আমরা 30 এ প্রাথমিক মান সেট করি এবং পরিদর্শকের মধ্যে 0 এর কম মান সেট করার ক্ষমতা সীমিত করি।
[Min(0)] [SerializeField] private float _maxSpeed = 30;
যখন একটি পৃষ্ঠ বরাবর চলন্ত, আমরা চাই যে চরিত্রটি সর্বদা এটিকে আঁকড়ে রাখুক যদি তাদের মধ্যে দূরত্ব যথেষ্ট কম হয়।
[Min(0)] [SerializeField] private float _surfaceAnchor = 0.01f;
যদিও আমরা সিদ্ধান্ত নিয়েছি যে আমাদের গেমের পৃষ্ঠতলগুলি শুধুমাত্র অনুভূমিক বা উল্লম্ব হবে, ঠিক সেই ক্ষেত্রে, আমরা 45º এর প্রাথমিক মান সহ সারফেসের সর্বোচ্চ ঢাল কোণটি নির্দিষ্ট করব যার উপর অক্ষরটি স্থিরভাবে দাঁড়াতে পারে।
[Range(0, 90)] [SerializeField] private float _maxSlop = 45f;
পরিদর্শকের মাধ্যমে, আমি চরিত্রের বর্তমান গতি এবং তাদের অবস্থা দেখতে চাই, তাই আমি SerializeField
বৈশিষ্ট্যের সাথে দুটি ক্ষেত্র যোগ করব।
[SerializeField] private Vector2 _velocity; [field: SerializeField] public CharacterState State { get; private set; }
হ্যাঁ, এখানে আমি একটি নতুন, এখনো অনির্ধারিত সত্তা CharacterState
প্রবর্তন করেছি। আমরা এই বিষয়ে আরও আলোচনা করব।
আমাদের প্ল্যাটফর্মের বিকাশকে সহজ করার জন্য, আসুন শুধুমাত্র দুটি প্রধান চরিত্রের অবস্থা সংজ্ঞায়িত করি।
প্রথমটি হল গ্রাউন্ডেড , এমন একটি অবস্থা যেখানে চরিত্রটি সুরক্ষিতভাবে পৃষ্ঠের উপর দাঁড়িয়ে আছে। এই অবস্থায়, চরিত্রটি অবাধে পৃষ্ঠ বরাবর চলতে পারে এবং এটি থেকে লাফ দিতে পারে।
দ্বিতীয়টি হল এয়ারবর্ন , একটি মুক্ত পতনের অবস্থা, যেখানে চরিত্রটি বাতাসে রয়েছে। এই অবস্থায় চরিত্রের আচরণ প্ল্যাটফর্মারের নির্দিষ্টতার উপর নির্ভর করে পরিবর্তিত হতে পারে। বাস্তবতার কাছাকাছি একটি সাধারণ ক্ষেত্রে, চরিত্রটি প্রাথমিক গতির প্রভাবে চলে এবং তার আচরণকে প্রভাবিত করতে পারে না। যাইহোক, প্ল্যাটফর্মারগুলিতে, সুবিধা এবং গেমপ্লে গতিবিদ্যার পক্ষে পদার্থবিদ্যাকে প্রায়শই সরলীকৃত করা হয়: উদাহরণস্বরূপ, অনেক গেমে, এমনকি যখন বিনামূল্যে পড়ে, আমরা চরিত্রের অনুভূমিক গতিবিধি নিয়ন্ত্রণ করতে পারি। আমাদের ক্ষেত্রে, এটিও সম্ভব, পাশাপাশি ডাবল জাম্পের জনপ্রিয় মেকানিক, বাতাসে অতিরিক্ত লাফ দেওয়ার অনুমতি দেয়।
আসুন কোডে আমাদের চরিত্রের অবস্থাগুলি উপস্থাপন করি:
/// <summary> /// Describes the state of <see cref="CharacterBody"/>. /// </summary> public enum CharacterState { /// <summary> /// The character stays steady on the ground and can move freely along it. /// </summary> Grounded, /// <summary> /// The character is in a state of free fall. /// </summary> Airborne }
এটি লক্ষণীয় যে আরও অনেক রাজ্য থাকতে পারে। উদাহরণস্বরূপ, যদি গেমটিতে ঢালু পৃষ্ঠগুলি অন্তর্ভুক্ত থাকে, তাহলে চরিত্রটি একটি স্লাইডিং অবস্থায় থাকতে পারে যেখানে এটি অবাধে ডান বা বামে যেতে পারে না কিন্তু ঢালের নিচে স্লাইড করতে পারে। এই জাতীয় অবস্থায়, চরিত্রটিও লাফ দিতে পারে, ঢাল থেকে ধাক্কা মেরে, তবে কেবল ঢালের দিকে। আরেকটি সম্ভাব্য ক্ষেত্রে একটি উল্লম্ব প্রাচীর বরাবর স্লাইডিং, যেখানে মাধ্যাকর্ষণ প্রভাব দুর্বল হয়, এবং চরিত্রটি অনুভূমিকভাবে ধাক্কা দিতে পারে।
আমরা ইতিমধ্যে একটি ব্যক্তিগত ক্ষেত্র _velocity
সংজ্ঞায়িত করেছি, কিন্তু অক্ষরের সর্বোচ্চ গতি সীমিত করার সময় আমাদের বাইরে থেকে এই মানটি পেতে এবং সেট করতে সক্ষম হতে হবে। এর জন্য প্রদত্ত বেগ ভেক্টরকে সর্বাধিক অনুমোদিত গতির সাথে তুলনা করা প্রয়োজন।
এটি বেগ ভেক্টরের দৈর্ঘ্য গণনা করে বা গাণিতিক ভাষায়, এর মাত্রা নির্ণয় করে করা যেতে পারে। Vector2
কাঠামোতে ইতিমধ্যে একটি magnitude
বৈশিষ্ট্য রয়েছে যা আমাদের এটি করতে দেয়। এইভাবে, যদি পাস করা ভেক্টরের মাত্রা সর্বোচ্চ অনুমোদিত গতির চেয়ে বেশি হয়, তাহলে আমাদের ভেক্টরের দিক বজায় রাখা উচিত কিন্তু এর মাত্রা সীমিত করা উচিত। এর জন্য, আমরা _maxSpeed
স্বাভাবিকীকৃত বেগ ভেক্টর দ্বারা গুণ করি (একটি স্বাভাবিক ভেক্টর হল একই দিকের একটি ভেক্টর কিন্তু 1 এর মাত্রা)।
কোডে এটি দেখতে কেমন তা এখানে:
public Vector2 Velocity { get => _velocity; set => _velocity = value.magnitude > _maxSpeed ? value.normalized * _maxSpeed : value; }
এখন আসুন একটি ভেক্টরের মাত্রা কীভাবে গণনা করা হয় তা ঘনিষ্ঠভাবে দেখে নেওয়া যাক। এটি সূত্র দ্বারা সংজ্ঞায়িত করা হয়:
বর্গমূল গণনা করা একটি সম্পদ-নিবিড় অপারেশন। যদিও বেশিরভাগ ক্ষেত্রে গতি সর্বাধিকের বেশি হবে না, তবুও আমাদের প্রতি চক্রে অন্তত একবার এই তুলনা করতে হবে। যাইহোক, আমরা এই ক্রিয়াকলাপটিকে উল্লেখযোগ্যভাবে সহজ করতে পারি যদি আমরা ভেক্টরের মাত্রার বর্গকে সর্বোচ্চ গতির বর্গক্ষেত্রের সাথে তুলনা করি।
এর জন্য, আমরা সর্বাধিক গতির বর্গক্ষেত্র সংরক্ষণ করার জন্য একটি অতিরিক্ত ক্ষেত্র প্রবর্তন করি এবং আমরা এটি একবার Awake
পদ্ধতিতে গণনা করি:
private float _sqrMaxSpeed; private void Awake() { _sqrMaxSpeed = _maxSpeed * _maxSpeed; }
এখন গতি সেট করা আরও ভালভাবে সম্পাদন করা যেতে পারে:
public Vector2 Velocity { get => _velocity; set => _velocity = value.sqrMagnitude > _sqrMaxSpeed ? value.normalized * _maxSpeed : value; }
এইভাবে, আমরা অপ্রয়োজনীয় গণনা এড়াই এবং চরিত্রের গতিবিধি প্রক্রিয়াকরণের কর্মক্ষমতা উন্নত করি।
যেমনটি আমি আগে উল্লেখ করেছি, ইউনিটি একটি নতুন Slide()
পদ্ধতি যুক্ত করেছে, যা আমাদের CharacterBody
বিকাশকে ব্যাপকভাবে সহজ করবে। যাইহোক, এই পদ্ধতি ব্যবহার করার আগে, বস্তুটি মহাকাশে চলাচল করবে এমন নিয়মগুলি সংজ্ঞায়িত করা প্রয়োজন। এই আচরণটি Rigidbody2D.SlideMovement
কাঠামো দ্বারা সেট করা হয়েছে।
আসুন একটি নতুন ক্ষেত্র _slideMovement
প্রবর্তন করি এবং এর মান নির্ধারণ করি।
private Rigidbody2D.SlideMovement _slideMovement; private void Awake() { _sqrMaxSpeed = _maxSpeed * _maxSpeed; _slideMovement = CreateSlideMovement(); } private Rigidbody2D.SlideMovement CreateSlideMovement() { return new Rigidbody2D.SlideMovement { maxIterations = 3, surfaceSlideAngle = 90, gravitySlipAngle = 90, surfaceUp = Vector2.up, surfaceAnchor = Vector2.down * _surfaceAnchor, gravity = Vector2.zero, layerMask = _solidLayers, useLayerMask = true, }; }
এটি ব্যাখ্যা করা গুরুত্বপূর্ণ যে maxIterations
নির্ধারণ করে যে সংঘর্ষের ফলে একটি বস্তু কতবার দিক পরিবর্তন করতে পারে। উদাহরণস্বরূপ, যদি চরিত্রটি একটি প্রাচীরের পাশে বাতাসে থাকে এবং প্লেয়ার এটিকে ডানদিকে সরানোর চেষ্টা করে যখন মাধ্যাকর্ষণ এটিতে কাজ করে। এইভাবে, Slide()
পদ্ধতিতে প্রতিটি কলের জন্য, ডান এবং নিচে নির্দেশিত একটি বেগ ভেক্টর সেট করা হবে। দেয়ালে আঘাত করার পরে, গতি ভেক্টর পুনরায় গণনা করা হয় এবং বস্তুটি নীচের দিকে অগ্রসর হতে থাকবে।
এই ধরনের পরিস্থিতিতে, maxIterations
মান 1 এ সেট করা হলে, বস্তুটি দেয়ালে আঘাত করবে, থামবে এবং কার্যকরভাবে সেখানে আটকে যাবে।
maxIterations
এবং layerMask
মানগুলি পূর্বে সংজ্ঞায়িত করা হয়েছিল। অন্যান্য ক্ষেত্রে আরো বিস্তারিত তথ্যের জন্য, অফিসিয়াল কাঠামো ডকুমেন্টেশন দেখুন।
এখন সবকিছু ক্যাপ্টেন সরানোর জন্য প্রস্তুত. আমরা FixedUpdate
এ এটি করব — ইউনিটিতে একটি কলব্যাক যা পদার্থবিদ্যা পরিচালনার জন্য ডিজাইন করা হয়েছে। বিগত কয়েক বছরে, ইউনিটি টিম 2D পদার্থবিদ্যার পরিচালনায় উল্লেখযোগ্যভাবে উন্নতি করেছে। বর্তমানে, Update
কলব্যাকে প্রসেসিং করা যেতে পারে বা এমনকি আপনার নিজের থেকে প্রয়োজনীয় পদ্ধতিতে কল করেও।
যাইহোক, এই উদাহরণে, আমরা ঐতিহ্যগত এবং প্রমাণিত FixedUpdate
পদ্ধতি ব্যবহার করব। এগিয়ে যাওয়ার আগে, Time.fixedDeltaTime
এর মান সম্পর্কে কয়েকটি শব্দ উল্লেখ করা মূল্যবান।
গেম ফিজিক্সে ভবিষ্যদ্বাণী নিশ্চিত করতে, সিমুলেশনটি নির্দিষ্ট সময়ের ব্যবধানে পুনরাবৃত্তিতে সঞ্চালিত হয়। এটি গ্যারান্টি দেয় যে FPS বা ল্যাগের পরিবর্তন বস্তুর আচরণকে প্রভাবিত করে না।
প্রতিটি চক্রের শুরুতে, আমরা বস্তুর উপর মাধ্যাকর্ষণ প্রভাবের জন্য হিসাব করব। যেহেতু মাধ্যাকর্ষণ মুক্ত পতনের ত্বরণের ভেক্টর দ্বারা দেওয়া হয়, তাই আমরা সূত্রের মাধ্যমে Δt
সময়ের সাথে বস্তুর বেগের Δv
পরিবর্তন গণনা করতে পারি:
যেখানে a
হল বস্তুর ধ্রুবক ত্বরণ। আমাদের ক্ষেত্রে, এটি অভিকর্ষের কারণে ত্বরণ, আমরা যে সহগটি প্রবর্তন করেছি তা বিবেচনা করে — Physics2D.gravity * GravityFactor
। অতএব, Δv
নিম্নরূপ গণনা করা যেতে পারে:
Time.fixedDeltaTime * GravityFactor * Physics2D.gravity
চূড়ান্ত ফলাফল, যেখানে আমরা বেগ পরিবর্তন করি, এইরকম দেখায়:
Velocity += Time.fixedDeltaTime * GravityFactor * Physics2D.gravity;
এখন আমরা চরিত্রটির কঠোর বডি আন্দোলন করতে পারি:
var slideResults = _rigidbody.Slide( _velocity, Time.fixedDeltaTime, _slideMovement);
পরিবর্তনশীল slideResults
হল SlideResults
গঠনের একটি মান এবং আন্দোলনের ফলাফল সংরক্ষণ করে। আমাদের জন্য এই ফলাফলের প্রধান ক্ষেত্রগুলি হল slideHit
, আন্দোলনের সময় পৃষ্ঠের সাথে সংঘর্ষের ফলাফল এবং surfaceHit
- একটি নিম্নগামী কাস্টের ফলাফল, যা চরিত্রটি একটি স্থিতিশীল পৃষ্ঠের উপর দাঁড়িয়ে আছে কিনা তা নির্ধারণ করতে সাহায্য করবে৷
পৃষ্ঠের সাথে সংঘর্ষের সময়, সেই পৃষ্ঠের দিকে নির্দেশিত চরিত্রের গতি সীমিত করা অত্যন্ত গুরুত্বপূর্ণ। একটি সাধারণ উদাহরণ হল যদি চরিত্রটি মাটিতে স্থির থাকে তবে তাদের অভিকর্ষের প্রভাবে গতি অর্জন করা চালিয়ে যাওয়া উচিত নয়। প্রতিটি চক্রের শেষে, তাদের গতি শূন্য হওয়া উচিত। একইভাবে, উপরের দিকে যাওয়ার সময় এবং সিলিংয়ে আঘাত করার সময়, অক্ষরটিকে সমস্ত উল্লম্ব গতি হারাতে হবে এবং নীচের দিকে চলতে শুরু করতে হবে।
সংঘর্ষের ফলাফল, slideHit
এবং surfaceHit
, RaycastHit2D
কাঠামোর মান দ্বারা উপস্থাপিত হয়, যার মধ্যে সংঘর্ষের পৃষ্ঠের স্বাভাবিক অন্তর্ভুক্ত রয়েছে।
বেগ ভেক্টর থেকে স্বাভাবিক সংঘর্ষের উপর মূল বেগ ভেক্টরের অভিক্ষেপ বিয়োগ করে গতি সীমা গণনা করা যেতে পারে। এটি ডট পণ্য ব্যবহার করে করা হয়। আসুন একটি পদ্ধতি লিখি যা এই অপারেশনটি করবে:
private static Vector2 ClipVector(Vector2 vector, Vector2 hitNormal) { return vector - Vector2.Dot(vector, hitNormal) * hitNormal; }
এখন আমাদের FixedUpdate
এ এই পদ্ধতিটি সংহত করা যাক। এখানে, surfaceHit
এর জন্য, আমরা কেবলমাত্র গতি সীমিত করব যদি এটি নীচের দিকে পরিচালিত হয়, কারণ কাস্টটি নির্ধারণ করে যে বস্তুটি পৃষ্ঠের উপর আছে কিনা তা সর্বদা মাটির সাথে যোগাযোগ পরীক্ষা করার জন্য সঞ্চালিত হয়।
private void FixedUpdate() { Velocity += Time.fixedDeltaTime * GravityFactor * Physics2D.gravity; var slideResults = _rigidbody.Slide( _velocity, Time.fixedDeltaTime, _slideMovement); if (slideResults.slideHit) { _velocity = ClipVector(_velocity, slideResults.slideHit.normal); } if (_velocity.y <= 0 && slideResults.surfaceHit) { var surfaceHit = slideResults.surfaceHit; _velocity = ClipVector(_velocity, surfaceHit.normal); } }
এই বাস্তবায়নটি চরিত্রের গতিবিধি সঠিকভাবে পরিচালনা করতে, বিভিন্ন পৃষ্ঠের সাথে সংঘর্ষের সময় অবাঞ্ছিত ত্বরণ এড়াতে এবং গেমের অক্ষরগুলির গতিবিধি অনুমানযোগ্য এবং মসৃণ রাখার অনুমতি দেয়।
প্রতিটি চক্রের শেষে, অক্ষরটি একটি কঠিন পৃষ্ঠে (গ্রাউন্ডেড স্টেট) নাকি ফ্রি পতনে (অথবা, যেমন আমরা সংজ্ঞায়িত করেছি, নিয়ন্ত্রিত পতন—বায়ুবাহী অবস্থা) তা নির্ধারণ করা প্রয়োজন।
অক্ষরটিকে গ্রাউন্ডেড অবস্থায় বিবেচনা করার জন্য, প্রাথমিকভাবে, তাদের উল্লম্ব গতি অবশ্যই শূন্য বা ঋণাত্মক হতে হবে, যা আমরা _velocity.y
এর মান দ্বারা নির্ধারণ করি।
আরেকটি গুরুত্বপূর্ণ মাপকাঠি হল চরিত্রের পায়ের নীচে একটি পৃষ্ঠের উপস্থিতি, যা আমরা রিজিডবডি আন্দোলনের ফলাফল থেকে শনাক্ত করি, যেমন surfaceHit
উপস্থিতির মাধ্যমে।
তৃতীয় ফ্যাক্টর হল পৃষ্ঠের বাঁকের কোণ, যা আমরা এই পৃষ্ঠের স্বাভাবিকের উপর ভিত্তি করে বিশ্লেষণ করি, অর্থাৎ, surfaceHit.normal
এর মান। এই কোণটিকে _maxSlop
এর সাথে তুলনা করা প্রয়োজন - পৃষ্ঠের সর্বাধিক সম্ভাব্য কোণ যার উপর অক্ষরটি স্থিরভাবে দাঁড়াতে পারে।
একটি সম্পূর্ণ উল্লম্ব পৃষ্ঠের জন্য, স্বাভাবিকটি কঠোরভাবে অনুভূমিক হবে, অর্থাৎ, এর ভেক্টরের মান হবে (1, 0) বা (-1, 0)। একটি অনুভূমিক পৃষ্ঠের জন্য, স্বাভাবিকের মান হবে (0, 1)। প্রবণতার কোণ যত ছোট হবে, y
এর মান তত বেশি হবে। কোণ alpha
জন্য, এই মানটি হিসাবে গণনা করা যেতে পারে:
যেহেতু আমাদের কোণ ডিগ্রীতে দেওয়া হয়েছে, এবং $\cos$ ফাংশনের জন্য রেডিয়ান প্রয়োজন, সূত্রটি এতে রূপান্তরিত হয়েছে:
এর জন্য, আসুন একটি নতুন ক্ষেত্র প্রবর্তন করি এবং এটি Awake
পদ্ধতিতে গণনা করি।
private float _minGroundVertical; private void Awake() { _minGroundVertical = Mathf.Cos(_maxSlop * Mathf.PI / 180f); //... }
এখন FixedUpdate
আমাদের কোড আপডেট করা যাক, উপরের সমস্ত শর্তগুলি পরীক্ষা করে দেখুন।
if (_velocity.y <= 0 && slideResults.surfaceHit) { var surfaceHit = slideResults.surfaceHit; Velocity = ClipVector(_velocity, surfaceHit.normal); State = surfaceHit.normal.y >= _minGroundVertical ? CharacterState.Grounded : CharacterState.Airborne; } else { State = CharacterState.Airborne; }
এই যুক্তি আমাদের সঠিকভাবে নির্ধারণ করতে অনুমতি দেবে যখন চরিত্রটি মাটিতে থাকবে এবং তাদের অবস্থার পরিবর্তনের জন্য সঠিকভাবে প্রতিক্রিয়া জানাবে।
এখন যেহেতু আমাদের ক্যারেক্টারবডি উপাদান প্রস্তুত, চূড়ান্ত পদক্ষেপ হল এটিকে আমাদের ক্যাপ্টেনে যুক্ত করা। দৃশ্যে, ক্যাপ্টেন অবজেক্টটি নির্বাচন করুন এবং এতে CharacterBody
উপাদান যোগ করুন।
উপরের চিত্রে দেখানো হিসাবে Rigidbody কনফিগার করতে ভুলবেন না। গ্র্যাভিটি ফ্যাক্টর 3 এ সেট করুন এবং সলিড লেয়ারের জন্য ডিফল্ট বিকল্প নির্বাচন করুন।
এখন আপনি গেমটি শুরু করতে পারেন এবং আমাদের চরিত্রটি দৃশ্যের চারপাশে কীভাবে চলে তা পর্যবেক্ষণ করতে বেগের জন্য বিভিন্ন মান সেট করে পরীক্ষা করতে পারেন।
অবশ্যই, আমাদের এখনও অক্ষর নিয়ন্ত্রণ যোগ করতে হবে। যাইহোক, এই নিবন্ধটি ইতিমধ্যে বেশ দীর্ঘ হয়ে গেছে, তাই আমি পরবর্তী নিবন্ধে নতুন ইনপুট সিস্টেম ব্যবহার করে অক্ষর নিয়ন্ত্রণের বিশদ বিবরণ দেব: "একতাতে একটি অক্ষর নিয়ন্ত্রক 2D তৈরি করা: পার্ট 2।"
আপনি এখানে এই নিবন্ধে বর্ণিত সম্পূর্ণ প্রকল্পটি ডাউনলোড করতে পারেন: ট্রেজার হান্টার এবং আপনি যদি কোনও অসুবিধার সম্মুখীন হন তবে অনুশীলনে সবকিছু পরীক্ষা করুন। একটি চরিত্র নিয়ন্ত্রক বিকাশ একটি 2D প্ল্যাটফর্ম তৈরির একটি মূল দিক, কারণ এটি গেমটির আরও বিকাশ নির্ধারণ করে। প্রধান নায়ক বা শত্রুদের আচরণে কত সহজে নতুন বৈশিষ্ট্য যোগ করা হবে তা প্রভাবিত করে। অতএব, স্বাধীনভাবে আপনার নিজস্ব গেম বিকাশ করতে সক্ষম হওয়ার জন্য মৌলিক বিষয়গুলি বোঝা খুবই গুরুত্বপূর্ণ।