Box2d: टक्करों की शारीरिक रचना

टक्कर क्या हैं?

Box2D में, यह आमतौर पर स्वीकार किया जाता है कि शरीर एक-दूसरे से टकराते हैं, लेकिन वास्तव में जुड़नार का उपयोग टकराव की गणना में किया जाता है (जुड़नार, शब्द अनुवाद मौजूद हैं , लेकिन मुझे यकीन नहीं है कि उनके बीच एक स्थापित है)। ऑब्जेक्ट्स अलग-अलग तरीकों से टकरा सकते हैं, इसलिए लाइब्रेरी बड़ी मात्रा में स्पष्ट जानकारी प्रदान करती है जिसका उपयोग गेम लॉजिक में किया जा सकता है। उदाहरण के लिए, आप निम्नलिखित जानना चाह सकते हैं:


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

हमारे परिदृश्य में, शून्य गुरुत्वाकर्षण वाले दुनिया में दो बहुभुज जुड़नार टकराएंगे (प्रक्रिया को बेहतर ढंग से नियंत्रित करने के लिए)। पहला स्थिरता एक स्थिर वर्ग है, दूसरा एक त्रिकोण है जो क्षैतिज रूप से वर्ग की ओर बढ़ता है।



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

टकराव की जानकारी

टकराव की जानकारी प्रकार b2Contact की एक वस्तु में निहित है। इससे आप ठीक से पता लगा सकते हैं कि कौन से जुड़नार टकरा रहे हैं, और परिणामस्वरूप दालों की उनकी स्थिति और दिशा निर्धारित करते हैं। Box2D में b2Contact ऑब्जेक्ट प्राप्त करने के दो तरीके हैं। पहला प्रत्येक ऑब्जेक्ट बॉडी की वर्तमान संपर्क सूची पर पुनरावृति करना है, दूसरा संपर्क श्रोता का उपयोग करना है। हम उनमें से प्रत्येक पर विचार करेंगे ताकि यह अधिक स्पष्ट हो कि दांव पर क्या है।

अपनी संपर्क सूची की जाँच करना

किसी भी समय, आप दुनिया के सभी संपर्कों के माध्यम से छाँट सकते हैं (मतलब b2World )

for (b2Contact* contact = world->GetContactList(); contact; contact = contact->GetNext()) contact->... //do something with the contact 

या किसी विशिष्ट वस्तु के शरीर से संपर्क करें

 for (b2ContactEdge* edge = body->GetContactList(); edge; edge = edge->next) edge->contact->... //do something with the contact 

यदि यह दृष्टिकोण चुना जाता है, तो यह याद रखना बहुत महत्वपूर्ण है कि इन सूचियों में एक संपर्क की उपस्थिति का मतलब यह नहीं है कि जुड़नार संपर्क में हैं - इसका मतलब केवल यह है कि उनका एएबीबी प्रतिच्छेद। यदि आप यह सुनिश्चित करना चाहते हैं कि जुड़नार स्वयं संपर्क में हैं, तो IsTouching () विधि का उपयोग करें। हम इस मुद्दे पर लौटेंगे।

श्रोताओं से संपर्क करें

अपनी संपर्क सूची की जाँच करना उन स्थितियों में अप्रभावी हो जाता है जहाँ टकराव अक्सर और बड़ी संख्या में होते हैं। संपर्क श्रोताओं को सेट करके, आप बॉक्स 2 डी को निर्देश दे रहे हैं कि जब कोई टकराव की शुरुआत और अंत को मैन्युअल रूप से मॉनिटर करने के बजाय, कुछ दिलचस्प हो तो आपको सूचित करें। एक संपर्क श्रोता b2ContactListener वर्ग का एक ऑब्जेक्ट है, जिनके कुछ कार्यों को यदि आवश्यक हो तो बदल दिया जाता है।

  void BeginContact(b2Contact* contact); void EndContact(b2Contact* contact); void PreSolve(b2Contact* contact, const b2Manifold* oldManifold); void PostSolve(b2Contact* contact, const b2ContactImpulse* impulse); 

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

सामान्य तौर पर, मैं संपर्क श्रोता विधि की सलाह देता हूं। पहली नज़र में, यह कुछ अजीब लग सकता है, लेकिन लंबे समय में यह अधिक प्रभावी और उपयोगी है। अब तक, मुझे ऐसी स्थिति का सामना नहीं करना पड़ा है जहाँ संपर्क सूची की जाँच करने से एक ठोस लाभ मिलेगा।

संपर्क प्राप्त करने की विधि के बावजूद, उनमें समान जानकारी होती है। अतिव्यापी जुड़नार पर सबसे महत्वपूर्ण डेटा निम्नानुसार प्राप्त किया जा सकता है

 b2Fixture* a = contact->GetFixtureA(); b2Fixture* b = contact->GetFixtureB(); 

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

क्लैश स्टेप बाई स्टेप।

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

आइए उस स्थिति से शुरू करें जब AABB जुड़नार ओवरलैप नहीं करते हैं, इसलिए हम इतिहास को पूरी तरह से ट्रेस कर सकते हैं। प्रत्येक स्थिरता के चारों ओर बैंगनी आयताकार क्षेत्रों को देखने के लिए "AABBs" चेकबॉक्स पर क्लिक करें।



AABB जुड़नार ओवरलैप करना शुरू करते हैं

इस तथ्य के बावजूद कि जुड़नार स्वयं अभी तक पार नहीं हुए हैं, इस स्तर पर b2Contact का एक उदाहरण पहले से ही बनाया गया है और दुनिया की संपर्क सूची और प्रत्येक निकाय की सूची में जोड़ा गया है। यदि आप इन सूचियों को देखते हैं, तो b2Contact वस्तुओं की उपस्थिति से आप यह अनुमान लगा सकते हैं कि, सिद्धांत रूप में, संपर्क संभव है, हालांकि जुड़नार जरूरी नहीं थे।

परिणाम: संपर्क मौजूद है, लेकिन IsTouching () गलत है

हम जुड़नार को सीधे इंटरसेक्ट करने तक सिमुलेशन जारी रखते हैं ...

फिक्स्चर पार करने लगते हैं।



वर्ग के ऊपरी कोने को सन्निकट करके, आप निम्न छवियों के अनुसार, संक्रमण देख सकते हैं

चरण n


चरण n + 1 (बुलेट ऑब्जेक्ट नहीं)


यह सब सिमुलेशन के एक चरण में होता है, जिसका अर्थ है कि हम वास्तविक चौराहे के बिंदु (जिसमें ऊपर की ओर आंकड़े में धराशायी रेखा है) पर छोड़ दिया गया है। ऐसा इसलिए है क्योंकि Box2D पहले सभी निकायों को स्थानांतरित करता है और उसके बाद ही चौराहों की जांच करता है, कम से कम यह इसका डिफ़ॉल्ट व्यवहार है। यदि आपको संपर्क के एक वास्तविक बिंदु की आवश्यकता है, तो निम्न कार्य करें

 bodyDef.bullet = true; //before creating body, or body->SetBullet(true); //after creating body 

चरण n + 1 (त्रिकोण - बुलेट ऑब्जेक्ट)


बुलेट-बॉडी गणना के लिए अधिक प्रोसेसर समय का उपभोग करते हैं और अधिकांश अनुप्रयोगों के लिए आवश्यक नहीं होते हैं। बस याद रखें कि सामान्य सेटिंग्स के साथ, कभी-कभी टकरावों को छोड़ दिया जा सकता है - हमारे उदाहरण में, यदि त्रिकोण तेजी से पर्याप्त स्थानांतरित हो गया, तो यह टकराव की शुरुआत किए बिना वर्ग के कोने के माध्यम से उड़ सकता है। यदि आपके पास बहुत तेज़ गति वाले निकाय हैं, जिनके संपर्क मिस नहीं होने चाहिए, उदाहरण के लिए, mmmm ... बुलेट्स :) तो उन्हें बुलेट-ऑब्जेक्ट के रूप में घोषित करने की आवश्यकता है। गैर-बुलेट निकायों के लिए आगे की प्रस्तुति की जाएगी।

परिणाम:

टकराव अंक और सामान्य

इस बिंदु पर, हमारे संपर्क में वास्तविक संपर्क है, जो लेख की शुरुआत में कुछ सवालों के जवाब नहीं देना संभव बनाता है। आरंभ करने के लिए, आइए सामान्य और स्पर्श बिंदु प्राप्त करें। यह माना जाता है कि सूची से संपर्क प्राप्त करने के बाद निम्नलिखित कोड को या तो संपर्क श्रोता की शुरुआत विधि से या आपकी विधि से कहा जाता है।

संपर्क वस्तु में टकराने वाली वस्तुओं के पिंडों के स्थानीय निर्देशांक में टकराव के बारे में जानकारी होती है, और यह ठीक वैसा नहीं है जैसा हमें चाहिए। हालाँकि, आप संपर्क से अधिक उपयोगी b2WorldManifold संरचना का अनुरोध कर सकते हैं, जिसमें विश्व निर्देशांक में टकराव की स्थिति शामिल है।

  int numPoints = contact->GetManifold()->pointCount; //...world manifold is helpful for getting locations b2WorldManifold worldManifold; contact->GetWorldManifold( &worldManifold ); //draw collision points glBegin(GL_POINTS); for (int i = 0; i < numPoints; i++) glVertex2f(worldManifold.points[i].x, worldManifold.points[i].y); glEnd(); 



परिणामी बिंदुओं का उपयोग बॉक्स 2 डी द्वारा टकराव की गणना के लिए प्रतिक्रिया की गणना करने के लिए किया जाएगा जो विपरीत दिशाओं में जुड़नार को निर्देशित करेगा। ये जुड़नार के गलत संपर्क बिंदु होंगे (जब तक कि आपने बुलेट ऑब्जेक्ट का उपयोग नहीं किया है), हालांकि व्यवहार में वे आमतौर पर गेम लॉजिक में टकराव की गणना करने के लिए पर्याप्त हैं।

अगला, सामान्य टकराव पर विचार करें, जो कि स्थिरता A से B तक निर्देशित है:

  float normalLength = 0.1f; b2Vec2 normalStart = worldManifold.points[0] - normalLength * worldManifold.normal; b2Vec2 normalEnd = worldManifold.points[0] + normalLength * worldManifold.normal; glBegin(GL_LINES); glColor3f(1,0,0);//red glVertex2f( normalStart.x, normalStart.y ); glColor3f(0,1,0);//green glVertex2f( normalEnd.x, normalEnd.y ); glEnd(); 

ऐसा लगता है कि इस टक्कर के लिए अतिव्यापी वस्तुओं से छुटकारा पाने का सबसे तेज़ तरीका है त्रिकोण के कोने को ऊपर और बाएं, और वर्ग को नीचे और दाईं ओर धकेलना। मैं इस तथ्य पर आपका ध्यान आकर्षित करना चाहूंगा कि सामान्य केवल एक दिशा है, यह संपर्क के किसी भी बिंदु से जुड़ा नहीं है - मैंने इसे सुविधा के लिए अंक [0] से गुजरते हुए चित्रित किया।



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



फिर जुड़नार को विभाजित करने का सबसे तेज़ तरीका त्रिकोण को ऊपर और दाईं ओर धकेलना है। तो वस्तुओं के बीच के कोण की गणना करने के लिए सामान्य का उपयोग करना अव्यावहारिक है। यदि आपको उन दिशाओं को जानना होगा जिसमें आंकड़े विभाजित होंगे, तो आप निम्नलिखित कोड का उपयोग कर सकते हैं:

 b2Vec2 vel1 = triangleBody->GetLinearVelocityFromWorldPoint( worldManifold.points[0] ); b2Vec2 vel2 = squareBody->GetLinearVelocityFromWorldPoint( worldManifold.points[0] ); b2Vec2 impactVelocity = vel1 - vel2; 

यह आपको टकराने वाले निकायों के सभी बिंदुओं की सापेक्ष प्रतिक्रिया दर प्राप्त करने की अनुमति देगा। हमारे सरल मामले में, हम अपने आप को त्रिकोण के रैखिक वेग तक सीमित कर सकते हैं, क्योंकि यह ज्ञात है कि वर्ग स्थिर है और त्रिकोण घूमता नहीं है। लेकिन उपरोक्त कोड उन मामलों को ध्यान में रखेगा जब दोनों चीजें चलती हैं या घूमती हैं।

यह भी ध्यान दिया जाना चाहिए कि हर टक्कर में टक्कर के दो बिंदु बिल्कुल नहीं होंगे। मैं जानबूझकर एक जटिल उदाहरण पर बस गया था, जब बहुभुज के दो कोने ओवरलैप होते हैं, लेकिन अधिक बार ऐसा केवल एक बिंदु होता है। यहां टकराव के कुछ उदाहरण हैं जिनके लिए एक बिंदु पर्याप्त है







इसलिए, हमने केवल इस बात की जांच की कि बिंदुओं और टकराव की सामान्यता का निर्धारण कैसे किया जाए, जिसके आधार पर Box2D ओवरलैप के लिए क्षतिपूर्ति करने के उद्देश्य से प्रतिक्रिया की गणना करेगा। अब वापस घटनाओं के क्रम पर।

टकराव की प्रतिक्रिया
((b2Contact :: Update, b2Island :: रिपोर्ट))
Shag_stolknoveniya


क्लैश_स्टेप + १


क्लैश_स्टेप + १


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

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

स्पष्टता के लिए, मैं प्रिंटफ का आउटपुट देता हूं, जिसे स्टेप फ़ंक्शन और प्रत्येक संपर्क श्रोता विधि में रखा जाता है:
  ... Step Step BeginContact PreSolve PostSolve Step PreSolve PostSolve Step PreSolve PostSolve Step EndContact Step Step ... 

परिणाम: PreSolve और PostSolve को कई बार कहा जाता है।

PreSolve और PostSolve

इन दोनों तरीकों को एक पैरामीटर के रूप में b2Contact के लिए एक संकेतक मिलता है, जिससे कि हम BeginContact के रूप में अंक और मानदंडों के बारे में समान जानकारी तक पहुंच पाते हैं। PreSolve आपको टक्कर की प्रतिक्रिया की गणना करने से पहले संपर्क की विशेषताओं को बदलने और यहां तक ​​कि प्रतिक्रिया को पूरी तरह से रद्द करने का अवसर देता है। PostSolve गणना की गई प्रतिक्रिया के बारे में जानकारी प्रदान करता है।

PreSolve में, आप किसी संपर्क ऑब्जेक्ट के लिए निम्न सेटिंग्स कर सकते हैं:
  void SetEnabled(bool flag);//non-persistent - need to set every time step //these available from v2.2.1 void SetFriction(float32 friction);//persists for duration of contact void SetRestitution(float32 restitution);//persists for duration of contact 

SetEnabled (गलत) कॉलिंग संपर्क को निष्क्रिय कर देती है, जिसका अर्थ है कि टकराव की प्रतिक्रिया की गणना नहीं की जाएगी। यह आवश्यक हो सकता है जब वस्तुओं को अस्थायी रूप से एक दूसरे के माध्यम से उड़ान भरने की अनुमति देना आवश्यक हो। एक क्लासिक उदाहरण एक तरफा दीवार या मंच है, जहां एक खिलाड़ी कुछ शर्तों के तहत एक सामान्य रूप से अगम्य वस्तु के माध्यम से जा सकता है जिसे केवल निष्पादन के दौरान जांचा जा सकता है - उदाहरण के लिए, खिलाड़ी की स्थिति या आंदोलन की दिशा।

यह याद रखना महत्वपूर्ण है कि अगले पुनरावृत्ति पर, संपर्क फिर से सक्रिय हो जाता है , इसलिए यदि आपको इसे लंबे समय तक अक्षम करने की आवश्यकता है, तो आपको इसे हर कदम पर करना होगा।

संपर्क के लिंक के अलावा, PreSolve में एक दूसरा पैरामीटर है जिसमें से आप पिछले मॉडलिंग चरण से टकराव (अंक और सामान्य) की विशेषताएं प्राप्त कर सकते हैं। अगर किसी को पता है कि यह क्यों काम आ सकता है - मुझे बताओ: डी

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

टकराव परिदृश्य पर वापस

जुड़नार अब ओवरलैप नहीं होते हैं
(b2Contact :: अपडेट)
AABB अभी भी ओवरलैप करते हैं, ताकि संपर्क दुनिया और शरीर की प्रासंगिक सूची में बना रहे।



(ज़ूम इन)


परिणाम:

यह तब तक जारी रहता है जब तक AABBs ओवरलैप नहीं होते।

AABB जुड़नार ओवरलैप नहीं करते हैं
(b2ContactManager :: Collide)


परिणाम: संपर्क दुनिया और शरीर की संपर्क सूची से हटा दिया जाता है।

EndContact विधि को b2Contact के लिए एक संकेतक मिलता है जब जुड़नार अब संपर्क में नहीं होते हैं, ताकि यह प्रासंगिक जानकारी नहीं रह जाए। फिर भी, एंडकॉन्टैक्ट संपर्क श्रोता का एक अभिन्न तत्व है, क्योंकि यह आपको नियंत्रित करने की अनुमति देता है जब गेम ऑब्जेक्ट संपर्क क्षेत्र से बाहर निकलते हैं।

परिणाम

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

Source: https://habr.com/ru/post/In213549/


All Articles