मैं एक इकाई प्रणाली संस्करण लागू कर रहा हूं जिसमें:
एक इकाई जो एक आईडी से थोड़ा अधिक है जो घटकों को एक साथ बांधती है
घटक कक्षाओं का एक गुच्छा जिसमें "घटक तर्क" नहीं है, केवल डेटा है
सिस्टम कक्षाओं का एक गुच्छा (उर्फ "सबसिस्टम", "प्रबंधक")। ये सभी इकाई लॉजिक प्रोसेसिंग करते हैं। अधिकांश बुनियादी मामलों में, सिस्टम केवल उन संस्थाओं की सूची को गढ़ता है, जिनमें वे रुचि रखते हैं और उनमें से प्रत्येक पर एक कार्रवाई करते हैं
एक MessageChannel क्लास ऑब्जेक्ट जो सभी गेम सिस्टम द्वारा साझा किया जाता है। प्रत्येक प्रणाली को सुनने के लिए विशिष्ट प्रकार के संदेशों की सदस्यता ले सकते हैं और अन्य प्रणालियों को संदेश प्रसारित करने के लिए चैनल का उपयोग भी कर सकते हैं
सिस्टम संदेश हैंडलिंग का प्रारंभिक संस्करण कुछ इस तरह था:
- क्रमिक रूप से प्रत्येक गेम सिस्टम पर एक अद्यतन चलाएँ
यदि कोई सिस्टम किसी कंपोनेंट के लिए कुछ करता है और वह एक्शन अन्य प्रणालियों के लिए रूचि का हो सकता है, तो सिस्टम एक उपयुक्त संदेश भेजता है (उदाहरण के लिए, सिस्टम कॉल
messageChannel.Broadcast(new EntityMovedMessage(entity, oldPosition, newPosition))
जब भी किसी इकाई को स्थानांतरित किया जाता है)
प्रत्येक सिस्टम जो विशिष्ट संदेश के लिए सब्सक्राइब किया जाता है, उसे संदेश हैंडलिंग विधि कहा जाता है
यदि कोई सिस्टम किसी ईवेंट को हैंडल कर रहा है, और ईवेंट प्रोसेसिंग लॉजिक को प्रसारित करने के लिए एक अन्य संदेश की आवश्यकता होती है, तो संदेश तुरंत प्रसारित हो जाता है और मैसेज प्रोसेसिंग विधियों की एक और श्रृंखला को कॉल किया जाता है
यह संस्करण तब तक ठीक था जब तक कि मैंने टकराव का पता लगाने वाली प्रणाली का अनुकूलन शुरू नहीं कर दिया था (यह वास्तव में धीमी हो रही थी क्योंकि संस्थाओं की संख्या बढ़ गई थी)। सबसे पहले, यह एक सरल जानवर बल एल्गोरिथ्म का उपयोग करके प्रत्येक इकाई जोड़ी को पुनरावृत्त करेगा। फिर मैंने एक "स्थानिक सूचकांक" जोड़ा जिसमें कोशिकाओं का एक ग्रिड होता है जो एक विशिष्ट सेल के क्षेत्र के अंदर संस्थाओं को संग्रहीत करता है, इस प्रकार केवल पड़ोसी कोशिकाओं में संस्थाओं पर जांच करने की अनुमति देता है।
हर बार जब कोई इकाई चलती है, तो टकराव प्रणाली जांचती है कि इकाई नई स्थिति में किसी चीज से टकरा रही है या नहीं। यदि यह है, तो एक टक्कर का पता लगाया जाता है। और अगर दोनों टकराने वाली संस्थाएं "भौतिक वस्तुएं" हैं (वे दोनों रिगिडबॉडी घटक हैं और एक दूसरे को दूर करने के लिए हैं ताकि एक ही स्थान पर कब्जा न हो), एक समर्पित कठोर शरीर पृथक्करण प्रणाली आंदोलन प्रणालियों को कुछ को स्थानांतरित करने के लिए कहती है। विशिष्ट स्थिति जो उन्हें अलग करेगी। बदले में यह परिवर्तन प्रणाली की स्थिति के बारे में सूचित संदेश भेजने के लिए आंदोलन प्रणाली का कारण बनता है। टक्कर का पता लगाने की प्रणाली प्रतिक्रिया करने के लिए होती है क्योंकि इसे स्थानिक सूचकांक को अपडेट करने की आवश्यकता होती है।
कुछ मामलों में यह एक समस्या का कारण बनता है क्योंकि सेल की सामग्री (सी # में इकाई वस्तुओं की एक सामान्य सूची) संशोधित हो जाती है, जबकि वे इससे अधिक पुनरावृत्त हो रहे हैं, इस प्रकार एक अपवाद को इट्रेटर द्वारा फेंक दिया जाता है।
तो ... मैं टकराव की जाँच के दौरान टकराव प्रणाली को बाधित होने से कैसे रोक सकता हूं?
बेशक, मैं कुछ "चतुर" / "मुश्किल" तर्क जोड़ सकता हूं जो यह सुनिश्चित करता है कि सेल सामग्री सही तरीके से खत्म हो जाए, लेकिन मुझे लगता है कि समस्या टकराव प्रणाली में ही निहित है (मुझे अन्य प्रणालियों में भी ऐसी ही समस्या थी), लेकिन तरीका सिस्टम से सिस्टम तक यात्रा करते समय संदेश संभाले जाते हैं। मुझे यह सुनिश्चित करने के लिए किसी तरह की आवश्यकता है कि एक विशिष्ट ईवेंट हैंडलिंग विधि बिना किसी रुकावट के काम करती है।
मैंने क्या कोशिश की है:
- आवक संदेश कतारों । हर बार जब कोई सिस्टम किसी संदेश को प्रसारित करता है, तो संदेश उस सिस्टम की संदेश कतार में जुड़ जाता है जो उसमें रुचि रखते हैं। जब सिस्टम अपडेट को प्रत्येक फ़्रेम कहा जाता है, तो ये संदेश संसाधित हो जाते हैं। समस्या : यदि सिस्टम A, सिस्टम B की कतार में एक संदेश जोड़ता है, तो यह अच्छी तरह से काम करता है यदि सिस्टम B सिस्टम A (बाद में उसी गेम फ़्रेम) से अपडेट होने के लिए है; अन्यथा यह अगले गेम फ्रेम को संसाधित करने के लिए संदेश का कारण बनता है (कुछ प्रणालियों के लिए वांछनीय नहीं)
- आउटगोइंग मैसेज की कतार । जबकि एक सिस्टम एक घटना को संभाल रहा है, किसी भी संदेश को प्रसारित करता है जो आउटगोइंग संदेश कतार में जोड़ा जाता है। संदेशों को संसाधित होने के लिए सिस्टम अपडेट की प्रतीक्षा करने की आवश्यकता नहीं है: प्रारंभिक संदेश हैंडलर के काम खत्म होने के बाद वे "तुरंत" संभाले जाते हैं। यदि संदेशों को संभालने के कारण अन्य संदेशों को प्रसारित किया जाता है, तो वे भी एक आउटगोइंग कतार में जुड़ जाते हैं, इसलिए सभी संदेश एक ही फ्रेम से संभाले जाते हैं। समस्या: यदि इकाई जीवनकाल प्रणाली (मैंने एक प्रणाली के साथ इकाई जीवनकाल प्रबंधन लागू किया है) एक इकाई बनाता है, तो यह इसके बारे में ए और बी कुछ प्रणालियों को सूचित करता है। जब सिस्टम A संदेश को संसाधित करता है, तो यह संदेशों की एक श्रृंखला का कारण बनता है जो अंततः बनाई गई इकाई को नष्ट करने का कारण बनता है (उदाहरण के लिए, एक बुलेट इकाई सही बनाई गई जहां यह कुछ बाधा से टकराती है, जिससे गोली आत्म विनाश का कारण बनती है)। जबकि संदेश श्रृंखला को हल किया जा रहा है, सिस्टम B को इकाई निर्माण संदेश नहीं मिलता है। इसलिए, यदि सिस्टम बी को इकाई विनाश संदेश में भी रुचि है, तो यह इसे प्राप्त करता है, और केवल "श्रृंखला" समाप्त होने के बाद, यह प्रारंभिक इकाई निर्माण संदेश प्राप्त करता है। यह विनाश संदेश को अनदेखा करने का कारण बनता है, सृजन संदेश "स्वीकार" किया जाता है:
संपादित करें - प्रश्नों के उत्तर, टिप्पणियाँ:
- सेल की सामग्री को संशोधित करता है, जबकि टकराव प्रणाली उन पर पुनरावृत्ति करता है?
जबकि टकराव प्रणाली कुछ इकाई पर टकराव की जांच कर रही है और यह पड़ोसी है, एक टकराव का पता लगाया जा सकता है और इकाई प्रणाली एक संदेश भेज देगी जिसे अन्य प्रणालियों द्वारा तुरंत प्रतिक्रिया दी जाएगी। संदेश की प्रतिक्रिया के कारण अन्य संदेश बन सकते हैं और उन्हें तुरंत संभाला जा सकता है। तो कुछ अन्य सिस्टम एक संदेश बना सकते हैं कि टक्कर सिस्टम को तुरंत प्रक्रिया करने की आवश्यकता होगी (उदाहरण के लिए, एक इकाई को स्थानांतरित किया गया ताकि टकराव प्रणाली को इसे स्थानिक सूचकांक अपडेट करने की आवश्यकता हो), भले ही पहले टकराव की जांच अभी तक समाप्त नहीं हुई थी।
- क्या आप एक वैश्विक आउटगोइंग संदेश कतार के साथ काम नहीं कर सकते?
मैंने हाल ही में एक वैश्विक कतार की कोशिश की। यह नई समस्याओं का कारण बनता है। समस्या: मैं एक टैंक इकाई को दीवार इकाई में स्थानांतरित करता हूं (टैंक कीबोर्ड के साथ नियंत्रित होता है)। फिर मैं टैंक की दिशा बदलने का फैसला करता हूं। टैंक और दीवार को प्रत्येक फ्रेम को अलग करने के लिए, CollidingRigidBodySeparationSystem टैंक को दीवार से सबसे छोटी राशि से दूर ले जाता है। जुदाई दिशा को टैंक की गति की दिशा के विपरीत होना चाहिए (जब खेल ड्राइंग शुरू होता है, तो टैंक को ऐसा दिखना चाहिए जैसे कि वह दीवार में कभी नहीं गया)। लेकिन दिशा नई दिशा के विपरीत हो जाती है, इस प्रकार टैंक को दीवार की तुलना में एक अलग तरफ स्थानांतरित करना शुरू में था। समस्या क्यों होती है: यह इस प्रकार है कि संदेश अब संभाला जाता है (सरलीकृत कोड):
public void Update(int deltaTime)
{
m_messageQueue.Enqueue(new TimePassedMessage(deltaTime));
while (m_messageQueue.Count > 0)
{
Message message = m_messageQueue.Dequeue();
this.Broadcast(message);
}
}
private void Broadcast(Message message)
{
if (m_messageListenersByMessageType.ContainsKey(message.GetType()))
{
// NOTE: all IMessageListener objects here are systems.
List<IMessageListener> messageListeners = m_messageListenersByMessageType[message.GetType()];
foreach (IMessageListener listener in messageListeners)
{
listener.ReceiveMessage(message);
}
}
}
कोड इस तरह बहता है (मान लें कि यह पहला गेम फ्रेम नहीं है):
- सिस्टम TimePassedMessage को संसाधित करना प्रारंभ करता है
- InputHandingSystem कुंजी प्रेस को इकाई कार्रवाई में परिवर्तित करता है (इस मामले में, एक बाएं तीर MoveWest कार्रवाई में बदल जाता है)। एक्शन एक्सिक्यूटर घटक में एंटिटी एक्शन संग्रहीत है
- ActionExecutionSystem , निकाय कार्रवाई की प्रतिक्रिया में, एक मूवमेंट जोड़ता है DirectChangeRequestedMessage संदेश कतार के अंत में
- MovementSystem वेलोसिटी घटक डेटा के आधार पर इकाई स्थिति को स्थानांतरित करता है और कतार के अंत में स्थिति-परिवर्तनशील संदेश जोड़ता है। आंदोलन पिछले फ्रेम के आंदोलन की दिशा / वेग का उपयोग करके किया जाता है (चलो उत्तर कहते हैं)
- सिस्टम TimePassedMessage को संसाधित करना बंद कर देता है
- सिस्टम मूवमेंट शुरू करते हैं MoveDDirectionChangeRequestedMessage
- मूवमेंट सिस्टम अनुरोध के अनुसार इकाई वेग / चालन दिशा बदलता है
- सिस्टम मूवमेंट को रोकते हैं MoveDDirectionChangeRequestedMessage
- सिस्टम स्थिति निर्धारण की प्रक्रिया शुरू करते हैं
- CollisionDetectionSystem का पता चलता है कि क्योंकि एक इकाई चली गई, यह दूसरी इकाई में चला गया (टैंक एक दीवार के अंदर चला गया)। यह कतार में एक CollisionOccuredMessage जोड़ता है
- सिस्टम पॉजिशन को बंद कर देता है
- सिस्टम CollisionOccuredMessage को प्रोसेस करना शुरू करते हैं
- CollidingRigidBodySeparationSystem टैंक और दीवार को अलग करके टकराव की प्रतिक्रिया करता है। चूंकि दीवार स्थिर है, केवल टैंक को स्थानांतरित किया गया है। टैंकों की आवाजाही दिशा का उपयोग एक संकेतक के रूप में किया जाता है कि टंकी कहाँ से आई है। यह एक विपरीत दिशा में ऑफसेट है
बग: जब टैंक ने इस फ्रेम को स्थानांतरित किया, तो यह पिछले फ्रेम से आंदोलन की दिशा का उपयोग कर गया, लेकिन जब इसे अलग किया जा रहा था, तो THIS फ्रेम से आंदोलन की दिशा का उपयोग किया गया था, भले ही यह पहले से ही अलग था। यह काम नहीं करना चाहिए!
इस बग को रोकने के लिए, पुराने आंदोलन की दिशा को कहीं और सहेजने की आवश्यकता है। मैं इसे केवल इस विशिष्ट बग को ठीक करने के लिए कुछ घटक में जोड़ सकता हूं, लेकिन क्या यह मामला संदेशों को संभालने के कुछ मूल रूप से गलत तरीके का संकेत नहीं देता है? पृथक्करण प्रणाली को क्यों ध्यान रखना चाहिए कि वह किस दिशा दिशा का उपयोग करती है? मैं इस समस्या को कैसे हल कर सकता हूं?
- आप Gamadu.com/artemis को पढ़ना चाह सकते हैं, यह देखने के लिए कि उन्होंने पहलू के साथ क्या किया है, जो आपके द्वारा देखी जा रही समस्याओं में से कुछ को चरणबद्ध करता है।
दरअसल, मैं आर्टेमिस से काफी समय से परिचित हूं। जांच की गई कि यह स्रोत कोड है, मंचों को पढ़ें, लेकिन मैंने "पहलू" को केवल कुछ ही स्थानों पर उल्लिखित किया है और, जहां तक मैं इसे समझता हूं, उनका मूल रूप से "सिस्टम" है। लेकिन मैं यह नहीं देख सकता कि आर्टेमिस ने मेरी कुछ समस्याओं को कैसे हल किया। यह संदेशों का उपयोग भी नहीं करता है।
- इसे भी देखें: "इकाई संचार: संदेश कतार बनाम प्रकाशन / सदस्यता लें सिग्नल / स्लॉट्स"
मैं पहले से ही सभी gamedev.stackexchange प्रश्नों को इकाई सिस्टम के बारे में पढ़ चुका हूँ। यह उन समस्याओं पर चर्चा नहीं करता है जो मैं सामना कर रहा हूं। क्या मैं कुछ भूल रहा हूँ?
- दो मामलों को अलग-अलग तरीके से हैंडल करें, ग्रिड को अपडेट करना आंदोलन के संदेशों पर भरोसा करने की आवश्यकता नहीं है क्योंकि यह टकराव प्रणाली का हिस्सा है
मुझे नहीं पता तुम्हारा क्या मतलब है। CollisionDetectionSystem के पुराने कार्यान्वयन सिर्फ एक अपडेट पर टकरावों की जांच करेंगे (जब एक TimePassedMessage हैंडल किया गया था), लेकिन मुझे प्रदर्शन के कारण जितना हो सके चेक को कम करना पड़ा। इसलिए जब एक इकाई चलती है (मेरे खेल में अधिकांश इकाइयाँ स्थिर होती हैं) तो मैंने टक्कर की जाँच पर स्विच किया।