क्वाड ट्री बनाम ग्रिड आधारित टक्कर का पता लगाने


27

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

क्या यह दूसरे से बढ़िया है ? या अधिक सुरुचिपूर्ण? मुझे वास्तव में यकीन नहीं है कि मुझे कौन सा उपयोग करना चाहिए।

किसी भी सलाह का स्वागत है। धन्यवाद।

जवाबों:


31

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

ग्रिड का पता लगाना केवल चलती वस्तुओं और स्थिर पृष्ठभूमि के बीच टकराव का पता लगाने के लिए लागू होता है। इसका सबसे बड़ा फायदा यह है कि स्थिर बैकग्राउंड को एक सन्निहित मेमोरी ऐरे के रूप में दर्शाया जाता है, और यदि आप कई रीड्स करना चाहते हैं, तो प्रत्येक टकराव लुकअप ओ (1) अच्छी लोकेशन के साथ होता है (क्योंकि इकाइयां ग्रिड में एक से अधिक सेल को कवर करती हैं)। नुकसान, अगर स्थैतिक पृष्ठभूमि बड़ी है, तो ग्रिड अंतरिक्ष के बजाय बेकार हो सकता है।

यदि इसके बजाय आप क्वाडट्री के रूप में स्टेटिक बैकग्राउंड का प्रतिनिधित्व करते हैं, तो व्यक्तिगत लुकअप की लागत बढ़ जाती है, लेकिन क्योंकि बैकग्राउंड के बड़े ब्लॉक थोड़ी मात्रा में जगह लेते हैं, मेमोरी आवश्यकताएं कम हो जाती हैं, और इसलिए बैकग्राउंड का अधिक हिस्सा अंदर बैठ सकता है। कैश। यहां तक ​​कि अगर इस तरह की संरचना में एक लुकअप करने के लिए 10 बार लगता है, अगर यह कैश में है, तो यह अभी भी कैश मिस के साथ सिंगल लुकअप की तुलना में 10 गुना तेज होगा।

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

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

foreach actor in actorList:
    foreach target in actorList:
        if (actor != target) and actor.boundingbox intersects target.boundingbox:
            actor.doCollision(target)

यह स्पष्ट रूप से O (n ^ 2) के आसपास प्रदर्शन करता है, n के साथ अभिनेताओं की संख्या जो वर्तमान में खेल में जीवित हैं, जिसमें बुलेट और स्पेसशिप और एलियंस शामिल हैं। इसमें छोटी स्थिर बाधाएँ भी शामिल हो सकती हैं।

यह काल्पनिक रूप से इतनी अच्छी तरह से काम करता है जब तक कि इस तरह की वस्तुओं की संख्या यथोचित रूप से छोटी होती है, लेकिन जब जांच करने के लिए कुछ सौ से अधिक ऑब्जेक्ट होते हैं, तो थोड़ा गरीब लगने लगता है। 10 वस्तुओं का परिणाम सिर्फ 100 टकराव की जाँच में होता है, 100 परिणाम 10,000 जाँच में। एक लाख चेक में 1000 परिणाम।

एक स्थानिक सूचकांक (जैसे quadtrees) ज्यामितीय रिश्तों के अनुसार एकत्र की जाने वाली वस्तुओं की कुशलता से गणना कर सकता है। यह टकराव एल्गोरिथ्म को कुछ इस तरह से बदल देगा:

foreach actor in actorList:
    foreach target in actorIndex.neighbors(actor.boundingbox):
       if (actor != target) and actor.boundingbox intersects target.boundingbox:
            actor.doCollision(target)

इसकी दक्षता (संस्थाओं के एक समान वितरण को मानते हुए): आमतौर पर O (n ^ 1.5 लॉग (n)) है, क्योंकि इंडेक्स लॉग के बारे में लेता है (n) ट्रैवर्स से तुलना, sqrt (n) पड़ोसियों की तुलना करने के बारे में होगा , और जाँच करने के लिए n अभिनेता हैं। वास्तविक रूप से, हालांकि, पड़ोसियों की संख्या हमेशा काफी सीमित होती है, क्योंकि यदि टक्कर होती है, तो अधिकांश समय वस्तुओं में से एक को हटा दिया जाता है, या टक्कर से दूर चला जाता है। इस प्रकार आपको सिर्फ O (n log (n)) मिलता है। 10 संस्थाओं के लिए, आप 10 (लगभग) 10 तुलना करते हैं, 100 के लिए, आप 200 करते हैं, 1000 के लिए आप 3000 करते हैं।

वास्तव में एक चतुर सूचकांक भी पड़ोसी खोज को थोक पुनरावृत्ति के साथ जोड़ सकता है, और प्रत्येक इंटरसेक्टिंग इकाई पर कॉलबैक कर सकता है। यह O (n) के बारे में एक प्रदर्शन देगा, क्योंकि सूचकांक को n के बजाय एक बार स्कैन किया जा रहा है।


मुझे यकीन नहीं है कि मुझे पता है कि जब आप "स्थिर पृष्ठभूमि" कहते हैं तो आप क्या उल्लेख कर रहे हैं। मैं एक 2 डी निशानेबाज के साथ काम कर रहा हूं, इसलिए यह अंतरिक्ष जहाजों और एलियंस, गोलियों और दीवारों के साथ टकराव का पता लगा रहा है।
dotminic

2
आपने सिर्फ मेरा निजी "शानदार जवाब" बैज अर्जित किया है!
फेलिक्सज

यह बेवकूफ लग सकता है लेकिन मैं वास्तव में अपनी क्वाडट्री का उपयोग कैसे कर सकता हूं कि किन वस्तुओं के साथ टकराव का परीक्षण करना चाहिए? मैं इस बारे में अनिश्चित हूं कि यह कैसे किया जाता है। जो एक दूसरा प्रश्न लाता है। कहो कि मेरे पास नोड में एक वस्तु है जो दूसरे नोड का पड़ोसी नहीं है, लेकिन यह वस्तु काफी बड़ी है कि यह कुछ नोड्स फैलाती है, मैं वास्तविक टक्कर के लिए कैसे जांच कर सकता हूं, क्योंकि मैं अनुमान लगा रहा हूं कि पेड़ यह नहीं मान सकता है। एक "दूर दूर" नोड में वस्तुओं से टकराने के लिए पर्याप्त है? क्या एक नोड में पूरी तरह से फिट नहीं होने वाली वस्तुओं को मूल नोड में रखा जाना चाहिए?
११:१० से

2
बाउंड-बॉक्स की ओवरलैपिंग के लिए क्वाट-ट्री स्वाभाविक रूप से उप-इष्टतम हैं। इसके लिए सबसे अच्छा विकल्प आमतौर पर एक आर-ट्री है। क्वाड-ट्रीज़ के लिए, यदि अधिकांश ऑब्जेक्ट लगभग बिंदु-समान हैं, तो हाँ, आंतरिक नोड्स पर ऑब्जेक्ट रखना उचित है, और फ़ज़ी पड़ोसी खोज पर सटीक टक्कर परीक्षण करना। यदि इंडेक्स की अधिकांश वस्तुएं टकराए बिना बड़ी और ओवरलैप होती हैं, तो A क्वाड ट्री शायद खराब विकल्प है। यदि आपके पास इस बारे में अधिक तकनीकी प्रश्न हैं, तो आपको उन्हें stackoverflow.com पर विचार करना चाहिए
SingleNegationElimination

यह सब बहुत भ्रामक है! जानकारी के लिए धन्यवाद।
dotminic

3

प्राचीन धागे को फिर से जीवित करने के लिए क्षमा करें, लेकिन इन मामलों के लिए IMHO सादे पुराने ग्रिड अक्सर उपयोग नहीं किए जाते हैं। वहाँ सेल प्रविष्टि में एक ग्रिड के लिए बहुत सारे फायदे हैं / हटाने से गंदगी सस्ती है। ग्रिड को विरल अभ्यावेदन के लिए अनुकूलित करने का कोई उद्देश्य नहीं होने से आपको किसी सेल को मुक्त करने से परेशान नहीं होना पड़ेगा। मेरा कहना है कि केवल एक क्वाड-ट्री की जगह एक ग्रिड से 1200ms से 20ms तक के लेगिस कोडबेस में तत्वों के एक समूह का चयन करने का समय कम हो गया है। हालांकि निष्पक्षता में, उस क्वाड-ट्री को वास्तव में खराब तरीके से लागू किया गया था, जो तत्वों के लिए एक अलग गतिशील सरणी प्रति पत्ती नोड को संग्रहीत करता है।

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

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

struct Node
{
    int32_t next;
    ...
};

struct Grid
{
     vector<int32_t> cells;
     vector<Node> nodes;
};

इस तरह:

यहाँ छवि विवरण दर्ज करें

ठीक है, तो विपक्ष पर। मैं इस पर गंभीरता से पूर्वाग्रह और ग्रिड के लिए वरीयता के साथ आ रहा हूं, लेकिन उनका मुख्य नुकसान यह है कि वे विरल नहीं हैं।

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

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

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

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

कहा कि सब के साथ, मुझे लगता है कि यह प्रोग्रामर पर निर्भर है। ग्रिड बनाम क्वाड-ट्री या ऑक्ट्री बनाम केडी-ट्री बनाम बीवीएच जैसी चीजों के साथ, मेरा वोट सबसे विपुल डेवलपर पर एक रिकॉर्ड है जिसमें बहुत कुशल समाधान बनाने के लिए कोई फर्क नहीं पड़ता कि वह डेटा संरचना का उपयोग करता है। सूक्ष्म स्तर पर भी बहुत कुछ है, जैसे मल्टीथ्रेडिंग, SIMD, कैश-फ्रेंडली मेमोरी लेआउट और एक्सेस पैटर्न। कुछ लोग उन सूक्ष्म पर विचार कर सकते हैं, लेकिन जरूरी नहीं कि उनका सूक्ष्म प्रभाव हो। इस तरह की चीजें एक समाधान से दूसरे में 100x अंतर कर सकती हैं। इसके बावजूद, अगर मुझे व्यक्तिगत रूप से कुछ दिन दिए गए थे और मुझे बताया गया था कि मुझे हर फ्रेम के चारों ओर घूमने वाले तत्वों के टकराव का पता लगाने में तेजी लाने के लिए एक डेटा संरचना को लागू करने की आवश्यकता है, मैं उस समय में एक ट्रैक्टर की तुलना में ग्रिड को लागू करने में बेहतर काम करूंगा। पेड़।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.