किसी भी 2D प्लेटफ़ॉर्मर के मुख्य तत्वों में से एक मुख्य पात्र है। जिस तरह से वह चलता है और नियंत्रित होता है, वह खेल के माहौल को महत्वपूर्ण रूप से आकार देता है - चाहे वह एक आरामदायक पुराने स्कूल का खेल हो या एक गतिशील स्लेशर। इसलिए, एक प्लेटफ़ॉर्मर विकसित करने में एक कैरेक्टर कंट्रोलर बनाना एक महत्वपूर्ण प्रारंभिक चरण है।
इस लेख में, हम स्क्रैच से एक चरित्र बनाने की प्रक्रिया की पूरी तरह से जांच करेंगे, इसे भौतिकी के नियमों का पालन करते हुए स्तर पर घूमना सिखाएंगे। भले ही आपके पास पहले से ही कैरेक्टर कंट्रोलर बनाने का अनुभव हो, फिर भी आपको यूनिटी 2023 में नवाचारों के बारे में जानने में दिलचस्पी होगी। मेरे आश्चर्य के लिए, Rigidbody2D
घटक के लिए एक लंबे समय से प्रतीक्षित Slide
विधि जोड़ी गई है, जो किनेमेटिक मोड में Rigidbody2D
के अधिक प्रभावी ढंग से उपयोग की अनुमति देकर एक चरित्र नियंत्रक लिखना बहुत सरल बनाता है। पहले, यह सभी कार्यक्षमता मैन्युअल रूप से लागू की जानी थी।
यदि आप न केवल लेख पढ़ना चाहते हैं, बल्कि इसे व्यवहार में भी आज़माना चाहते हैं, तो मैं GitHub रिपॉजिटरी ट्रेजर हंटर्स से एक स्तर टेम्पलेट डाउनलोड करने की सलाह देता हूं, जहां आपके चरित्र के परीक्षण के लिए आवश्यक संपत्ति और एक तैयार स्तर पहले से ही शामिल हैं।
हमारे प्लेटफ़ॉर्मर के लिए, हमने एक नियम निर्धारित किया है कि गेम में केवल ऊर्ध्वाधर और क्षैतिज सतहें होंगी, और गुरुत्वाकर्षण बल को सख्ती से नीचे की ओर निर्देशित किया जाएगा। यह प्रारंभिक चरण में प्लेटफ़ॉर्मर के निर्माण को काफी सरल बनाता है, खासकर यदि आप वेक्टर गणित में तल्लीन नहीं होना चाहते हैं।
भविष्य में, मैं अपने प्रोजेक्ट ट्रेजर हंटर्स में इन नियमों से हट सकता हूँ, जहाँ मैं यूनिटी पर 2D प्लेटफ़ॉर्मर के लिए मैकेनिक्स के निर्माण का पता लगाऊँगा। लेकिन यह किसी दूसरे लेख का विषय होगा।
हमारे इस निर्णय के आधार पर कि आंदोलन क्षैतिज सतहों के साथ किया जाएगा, हमारे चरित्र का आधार आयताकार होगा। झुकी हुई सतहों का उपयोग करने के लिए एक कैप्सूल के आकार का कोलाइडर और स्लाइडिंग जैसे अतिरिक्त यांत्रिकी विकसित करने की आवश्यकता होगी।
सबसे पहले, सीन पर एक खाली ऑब्जेक्ट बनाएं और उसका नाम कैप्टन रखें - यह हमारा मुख्य पात्र होगा। ऑब्जेक्ट में Rigidbody2D
और BoxCollider2D
घटक जोड़ें। Rigidbody2D
प्रकार को Kinematic पर सेट करें ताकि हम यूनिटी की अंतर्निहित भौतिकी क्षमताओं का उपयोग करते हुए भी चरित्र की गति को नियंत्रित कर सकें। साथ ही, Freeze Rotation Z विकल्प को सक्रिय करके Z-अक्ष के साथ चरित्र के रोटेशन को लॉक करें।
आपका सेटअप नीचे दिए गए चित्र जैसा दिखना चाहिए।
अब आइए अपने कैप्टन में एक रूप जोड़ें। Assets/Textures/Treasure Hunters/Captain Clown Nose/Sprites/Captain Clown Nose/Captain Clown Nose with Sword/09-Idle Sword/Idle Sword 01.png में टेक्सचर ढूंढें और इसका पिक्सेल प्रति यूनिट मान 32 पर सेट करें। हम इस कोर्स में अक्सर इस मान का उपयोग करेंगे क्योंकि हमारे टेक्सचर इसी रिज़ॉल्यूशन के साथ बनाए गए हैं। स्प्राइट मोड को सिंगल पर सेट करें और सुविधा के लिए, पिवट को बॉटम पर सेट करें। अप्लाई बटन पर क्लिक करके बदलाव लागू करना न भूलें। सुनिश्चित करें कि सभी सेटिंग्स सही तरीके से की गई हैं।
इस लेख में, हम चरित्र एनीमेशन पर बात नहीं करेंगे, इसलिए अभी के लिए, हम केवल एक स्प्राइट का उपयोग करेंगे। कैप्टन ऑब्जेक्ट में, Appearance नामक एक नेस्टेड ऑब्जेक्ट बनाएँ और उसमें पहले से कॉन्फ़िगर किए गए स्प्राइट को निर्दिष्ट करते हुए एक स्प्राइट रेंडरर घटक जोड़ें।
जब मैंने कैप्टन की छवि पर ज़ूम इन किया, तो मैंने पाया कि गलत स्प्राइट सेटिंग के कारण यह काफी धुंधली थी। इसे ठीक करने के लिए, प्रोजेक्ट विंडो में आइडल स्वॉर्ड 01 टेक्सचर चुनें और फ़िल्टर मोड को पॉइंट (कोई फ़िल्टर नहीं) पर सेट करें। अब छवि बहुत बेहतर दिखती है।
फिलहाल, कोलाइडर की स्थिति और हमारे कैप्टन की छवि मेल नहीं खाती, और कोलाइडर हमारी जरूरतों के लिए बहुत बड़ा हो जाता है।
आइए इसे ठीक करें और साथ ही नीचे की ओर कैरेक्टर की धुरी को सही तरीके से रखें। हम इस नियम को गेम में सभी ऑब्जेक्ट पर लागू करेंगे ताकि आकार की परवाह किए बिना उन्हें एक ही स्तर पर रखना आसान हो।
इस प्रक्रिया के प्रबंधन में आसानी के लिए, पिवट सेट के साथ टॉगल टूल हैंडल पोजीशन का उपयोग करें, जैसा कि नीचे दिखाया गया है।
अगला कदम हमारे हीरो के कोलाइडर को इस तरह से समायोजित करना है कि उसका पिवट पॉइंट बिल्कुल बीच के निचले हिस्से में हो। कोलाइडर का आकार बिल्कुल चरित्र के आयामों से मेल खाना चाहिए। आवश्यक परिशुद्धता प्राप्त करने के लिए कोलाइडर के ऑफसेट और आकार मापदंडों के साथ-साथ नेस्टेड अपीयरेंस ऑब्जेक्ट की स्थिति को समायोजित करें।
कोलाइडर के Offset.X पैरामीटर पर विशेष ध्यान दें: इसका मान पूर्णतः 0 होना चाहिए। यह ऑब्जेक्ट के केंद्र के सापेक्ष कोलाइडर के सममित स्थान को सुनिश्चित करेगा, जो कि बाद के वर्ण घुमावों के लिए अत्यंत महत्वपूर्ण है, जहां Transform.Scale.X का मान -1 और 1 में बदल जाता है। कोलाइडर को अपने स्थान पर बने रहना चाहिए, और दृश्य घुमाव प्राकृतिक दिखना चाहिए।
सबसे पहले और सबसे महत्वपूर्ण बात यह है कि पात्रों को भौतिक दुनिया के साथ बातचीत करना सिखाना ज़रूरी है - चाहे वे मुख्य नायक हों, एनपीसी हों या दुश्मन हों। उदाहरण के लिए, पात्रों को समतल सतह पर चलने, उससे कूदने और गुरुत्वाकर्षण बल के अधीन होने में सक्षम होना चाहिए, जो उन्हें वापस ज़मीन पर खींचता है।
यूनिटी में एक अंतर्निहित भौतिकी इंजन है जो निकायों की गति को नियंत्रित करता है, टकरावों को संभालता है, और वस्तुओं पर बाहरी बलों के प्रभाव को जोड़ता है। यह सब Rigidbody2D
घटक का उपयोग करके कार्यान्वित किया जाता है। हालाँकि, पात्रों के लिए, एक अधिक लचीला उपकरण होना उपयोगी है जो भौतिक दुनिया के साथ बातचीत करता है जबकि डेवलपर्स को ऑब्जेक्ट के व्यवहार पर अधिक नियंत्रण देता है।
जैसा कि मैंने पहले बताया, यूनिटी के पिछले संस्करणों में, डेवलपर्स को यह सारा तर्क खुद ही लागू करना पड़ता था। हालाँकि, यूनिटी 2023 में, Rigidbody2D
घटक में एक नई विधि Slide
जोड़ी गई, जो प्रदर्शन की गई गति के बारे में सभी आवश्यक जानकारी प्रदान करते हुए भौतिक वस्तु के लचीले नियंत्रण की अनुमति देती है।
आइए 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 घटक तैयार है, अंतिम चरण इसे हमारे Captain में जोड़ना है। दृश्य पर, Captain ऑब्जेक्ट का चयन करें और CharacterBody
घटक को इसमें जोड़ें।
ऊपर दिए गए चित्र में दिखाए अनुसार Rigidbody को कॉन्फ़िगर करना न भूलें। Gravity Factor को 3 पर सेट करें और Solid Layer के लिए डिफ़ॉल्ट विकल्प चुनें।
अब आप खेल शुरू कर सकते हैं और वेग के लिए अलग-अलग मान सेट करके प्रयोग कर सकते हैं, ताकि आप देख सकें कि हमारा पात्र दृश्य में किस प्रकार घूमता है।
बेशक, हमें अभी भी कैरेक्टर कंट्रोल जोड़ने की ज़रूरत है। हालाँकि, यह लेख पहले से ही काफी लंबा हो चुका है, इसलिए मैं अगले लेख में नए इनपुट सिस्टम का उपयोग करके कैरेक्टर के नियंत्रण के बारे में विस्तार से बताऊंगा: "यूनिटी में कैरेक्टर कंट्रोलर 2D बनाना: भाग 2।"
आप इस लेख में वर्णित संपूर्ण प्रोजेक्ट को यहाँ से डाउनलोड कर सकते हैं: ट्रेजर हंटर्स और यदि आपको कोई कठिनाई आती है तो व्यवहार में सब कुछ जाँच लें। 2D प्लेटफ़ॉर्मर बनाने में कैरेक्टर कंट्रोलर विकसित करना एक महत्वपूर्ण पहलू है, क्योंकि यह गेम के आगे के विकास को निर्धारित करता है। यह प्रभावित करता है कि मुख्य नायक या दुश्मनों के व्यवहार में कितनी आसानी से नई सुविधाएँ जोड़ी जाएँगी। इसलिए, स्वतंत्र रूप से अपना खुद का गेम विकसित करने में सक्षम होने के लिए मूल बातें समझना बहुत महत्वपूर्ण है।