यदि इस प्रश्न पर आपका यह पहली बार है, तो मेरा सुझाव है कि पहले से नीचे के अद्यतन को पहले पढ़ें, फिर इस भाग को। यहाँ समस्या का संश्लेषण है, हालांकि:
मूल रूप से, मेरे पास ग्रिड स्थानिक विभाजन प्रणाली के साथ टकराव का पता लगाने और रिज़ॉल्यूशन इंजन है, जहां ऑर्डर-ऑफ़-टकराव और टकराव समूह मायने रखते हैं। एक समय में एक शरीर को चलना चाहिए, फिर टकराव का पता लगाना चाहिए, फिर टकराव को हल करना चाहिए। यदि मैं एक ही बार में सभी निकायों को स्थानांतरित करता हूं, तो संभव टकराव जोड़े उत्पन्न करते हैं, यह स्पष्ट रूप से तेज़ है, लेकिन रिज़ॉल्यूशन टूट जाता है क्योंकि ऑर्डर-ऑफ-टकराव का सम्मान नहीं किया जाता है। यदि मैं एक बार एक शरीर को स्थानांतरित करता हूं, तो मुझे टक्करों की जांच करने के लिए शरीर प्राप्त करने के लिए मजबूर किया जाता है, और यह ^ 2 समस्या बन जाता है। मिश्रण में समूह रखें, और आप कल्पना कर सकते हैं कि बहुत सारे शरीर के साथ यह बहुत धीमी गति से क्यों हो जाता है।
अद्यतन: मैंने इस पर वास्तव में कड़ी मेहनत की है, लेकिन कुछ भी अनुकूलन करने का प्रबंधन नहीं कर सका।
मैंने एक बड़ा मुद्दा भी खोजा : मेरा इंजन ऑर्डर-ऑफ-टकराव पर निर्भर है।
मैंने अद्वितीय टक्कर जोड़ी पीढ़ी के कार्यान्वयन की कोशिश की , जिसने निश्चित रूप से बहुत कुछ करके सब कुछ गति प्रदान की, लेकिन टकराव के क्रम को तोड़ दिया ।
मुझे समझाने दो:
मेरे मूल डिजाइन में (जोड़े पैदा नहीं), ऐसा होता है:
- एक एकल शरीर चलता है
- चले जाने के बाद, यह अपनी कोशिकाओं को ताज़ा करता है और उन शरीरों को प्राप्त करता है जो इसके खिलाफ टकराते हैं
- यदि यह एक निकाय को ओवरलैप करता है, तो इसके खिलाफ समाधान, टकराव को हल करने की आवश्यकता होती है
इसका मतलब यह है कि अगर कोई शरीर हिलता है, और एक दीवार (या किसी अन्य शरीर) से टकराता है, तो केवल शरीर जो स्थानांतरित हो गया है, उसकी टक्कर को हल करेगा और दूसरा शरीर अप्रभावित रहेगा।
यह वह व्यवहार है जिसकी मैं इच्छा करता हूं ।
मैं समझता हूं कि यह भौतिकी इंजनों के लिए सामान्य नहीं है, लेकिन रेट्रो शैली के खेल के लिए इसके बहुत सारे फायदे हैं ।
सामान्य ग्रिड डिजाइन में (अद्वितीय जोड़े पैदा करते हुए), ऐसा होता है:
- सभी निकाय चलते हैं
- सभी शरीर चले जाने के बाद , सभी कोशिकाओं को ताज़ा करें
- अद्वितीय टक्कर जोड़े उत्पन्न करते हैं
- प्रत्येक जोड़ी के लिए, टकराव का पता लगाने और संकल्प को संभालें
इस मामले में एक साथ चलने के परिणामस्वरूप दो निकायों का अतिव्यापी हो सकता है, और वे एक ही समय में हल करेंगे - यह प्रभावी रूप से निकायों को "एक दूसरे के चारों ओर धक्का" देता है, और कई निकायों के साथ टकराव की स्थिरता को तोड़ता है
यह व्यवहार भौतिकी इंजनों के लिए सामान्य है, लेकिन यह मेरे मामले में स्वीकार्य नहीं है ।
मुझे एक और मुद्दा भी मिला, जो प्रमुख है (भले ही यह वास्तविक दुनिया की स्थिति में होने की संभावना न हो):
- समूह ए, बी और डब्ल्यू के निकायों पर विचार करें
- एक टकराता है और डब्ल्यू और ए के खिलाफ हल करता है
- B टकराता है और W और B के विरुद्ध हल करता है
- A, B के विरुद्ध कुछ भी नहीं करता है
- B, A के विरुद्ध कुछ भी नहीं करता है
ऐसी स्थिति हो सकती है जहां बहुत सारे ए शरीर और बी बॉडी एक ही सेल पर कब्जा कर लेते हैं - उस स्थिति में, निकायों के बीच बहुत अधिक अनावश्यक पुनरावृत्ति होती है जो एक दूसरे पर प्रतिक्रिया नहीं करना चाहिए (या केवल टकराव का पता लगाएं, लेकिन उन्हें हल न करें) ।
एक ही सेल पर कब्जा करने वाले 100 निकायों के लिए, यह 100 ^ 100 पुनरावृत्तियों है! ऐसा इसलिए होता है क्योंकि अद्वितीय जोड़े उत्पन्न नहीं हो रहे हैं - लेकिन मैं अद्वितीय जोड़े उत्पन्न नहीं कर सकता , अन्यथा मुझे ऐसा व्यवहार मिलेगा जिसकी मुझे इच्छा नहीं है।
क्या इस तरह के टक्कर इंजन को अनुकूलित करने का एक तरीका है?
ये दिशानिर्देश हैं जिनका सम्मान किया जाना चाहिए:
टकराव का आदेश अत्यंत महत्वपूर्ण है!
- निकायों स्थानांतरित करना होगा एक समय में एक है, तो टकराव के लिए जाँच एक समय में एक , और संकल्प आंदोलन के बाद एक समय में एक ।
निकायों में 3 समूह बिटसेट होना चाहिए
- समूह : समूह जिनका संबंध शरीर से है
- GroupToCheck : समूहों को शरीर के खिलाफ टकराव का पता लगाना चाहिए
- GroupNoResolve : समूहों को शरीर के खिलाफ टकराव को हल नहीं करना चाहिए
- ऐसी परिस्थितियाँ हो सकती हैं जहाँ मैं केवल एक टकराव का पता लगाना चाहता हूँ लेकिन हल नहीं किया जाता है
पूर्व अद्यतन:
प्राक्कथन : मुझे पता है कि इस अड़चन का अनुकूलन एक आवश्यकता नहीं है - इंजन पहले से ही बहुत तेज है। हालांकि, मैं मज़ेदार और शैक्षिक उद्देश्यों के लिए, इंजन को और भी तेज़ बनाने का तरीका खोजना पसंद करूँगा।
मैं लचीलापन और गति पर जोर देने के साथ एक सामान्य-उद्देश्य C ++ 2D टकराव का पता लगाने / प्रतिक्रिया इंजन बना रहा हूं।
यहाँ इसकी वास्तुकला का एक बहुत ही मूल चित्र है:
मूल रूप से, मुख्य वर्ग है World
, जो (a ResolverBase*
, a SpatialBase*
और a ) की स्मृति का प्रबंधन करता है vector<Body*>
।
SpatialBase
एक शुद्ध आभासी वर्ग है जो व्यापक-चरण टक्कर का पता लगाने से संबंधित है।
ResolverBase
एक शुद्ध आभासी वर्ग है जो टकराव के समाधान से संबंधित है।
निकाय वस्तुओं के World::SpatialBase*
साथ संचार करते SpatialInfo
हैं, स्वयं निकायों द्वारा स्वामित्व में होते हैं।
वस्तुतः एक स्थानिक वर्ग है: Grid : SpatialBase
जो एक मूल निश्चित 2D ग्रिड है। यह अपने आप की जानकारी वर्ग है, GridInfo : SpatialInfo
।
यहां बताया गया है कि इसकी वास्तुकला कैसी है:
Grid
वर्ग के एक 2 डी सरणी का मालिक है Cell*
। Cell
वर्ग का एक संग्रह (स्वामित्व में नहीं) शामिल हैं Body*
: एक vector<Body*>
है जो सभी निकायों है कि सेल में होते हैं।
GridInfo
वस्तुओं में गैर-मालिक बिंदु भी होते हैं जो शरीर में होते हैं।
जैसा कि मैंने पहले कहा था, इंजन समूहों पर आधारित है।
Body::getGroups()
std::bitset
उन सभी समूहों का रिटर्न देता है , जो शरीर का हिस्सा है।Body::getGroupsToCheck()
std::bitset
उन सभी समूहों को लौटाता है , जिनके शरीर को टक्कर के खिलाफ जाँच करनी है।
निकाय एकल कोशिका से अधिक पर कब्जा कर सकते हैं। ग्रिडइंफो हमेशा गैर-स्वामित्व वाले पॉइंटर्स को कब्ज़े वाली कोशिकाओं में संग्रहीत करता है।
एक एकल शरीर के हिलने के बाद, टकराव का पता चलता है। मैं मानता हूं कि सभी निकाय अक्ष-संरेखित बाउंडिंग बॉक्स हैं।
कैसे व्यापक चरण टकराव का पता लगाने काम करता है:
भाग 1: स्थानिक जानकारी अद्यतन
प्रत्येक के लिए Body
body
:
- शीर्ष-बाईं ओर स्थित कक्ष और नीचे-दाईं ओर स्थित कोशिकाओं की गणना की जाती है।
- यदि वे पिछले कोशिकाओं से भिन्न होते हैं, तो उन्हें
body.gridInfo.cells
साफ़ कर दिया जाता है, और उन सभी कोशिकाओं से भर जाता है, जो शरीर में व्याप्त होती हैं (2 डी के लिए लूप शीर्ष-बाएं सेल से निचले-दाएं सेल में)।
body
अब यह जानने के लिए गारंटी दी जाती है कि यह किन कोशिकाओं पर कब्जा करता है।
भाग 2: वास्तविक टक्कर की जाँच
प्रत्येक के लिए Body
body
:
body.gridInfo.handleCollisions
कहा जाता है:
void GridInfo::handleCollisions(float mFrameTime)
{
static int paint{-1};
++paint;
for(const auto& c : cells)
for(const auto& b : c->getBodies())
{
if(b->paint == paint) continue;
base.handleCollision(mFrameTime, b);
b->paint = paint;
}
}
void Body::handleCollision(float mFrameTime, Body* mBody)
{
if(mBody == this || !mustCheck(*mBody) || !shape.isOverlapping(mBody->getShape())) return;
auto intersection(getMinIntersection(shape, mBody->getShape()));
onDetection({*mBody, mFrameTime, mBody->getUserData(), intersection});
mBody->onDetection({*this, mFrameTime, userData, -intersection});
if(!resolve || mustIgnoreResolution(*mBody)) return;
bodiesToResolve.push_back(mBody);
}
टक्कर तो हर शरीर के लिए हल है
bodiesToResolve
।बस।
इसलिए, मैं इस व्यापक-चरण टक्कर का पता लगाने के लिए काफी समय से प्रयास कर रहा हूं। हर बार जब मैं वर्तमान वास्तुकला / सेटअप की तुलना में कुछ और करने की कोशिश करता हूं, तो कुछ योजना के अनुसार नहीं होता है या मैं उस सिमुलेशन के बारे में धारणा बनाता हूं जो बाद में गलत साबित होते हैं।
मेरा सवाल है: मैं अपने टक्कर इंजन के व्यापक-चरण को कैसे अनुकूलित कर सकता हूं ?
क्या किसी प्रकार का जादू सी ++ अनुकूलन है जिसे यहां लागू किया जा सकता है?
क्या अधिक प्रदर्शन की अनुमति देने के लिए वास्तुकला को फिर से डिज़ाइन किया जा सकता है?
- वास्तविक कार्यान्वयन: SSVSCollsion
- Body.h , Body.cpp
- World.h , World.cpp
- Grid.h , Grid.cpp
- Cell.h , Cell.cpp
- GridInfo.h , GridInfo.cpp
नवीनतम संस्करण के लिए Callgrind आउटपुट: http://txtup.co/rLJgz
getBodiesToCheck()
को 5462334 बार कहा गया, और पूरे प्रोफाइलिंग समय का 35,1% लिया (निर्देश पढ़ने का समय)