नेटवर्क-रीयल टाइम गेम के लिए गेम-स्टेट स्नैपशॉट सिस्टम कैसे लागू किया जाएगा?


12

मैं अपनी नेटवर्किंग क्लास के लिए प्रोजेक्ट के रूप में एक साधारण क्लाइंट-सर्वर रियल-टाइम मल्टीप्लेयर गेम बनाना चाहता हूं।

मैंने वास्तविक समय मल्टीप्लेयर नेटवर्क मॉडल के बारे में बहुत कुछ पढ़ा है और मैं क्लाइंट और सर्वर और लैग-मुआवजा तकनीकों के बीच संबंधों को समझता हूं।

मैं जो करना चाहता हूं वह क्वेक 3 नेटवर्क मॉडल के समान है: मूल रूप से, सर्वर पूरे गेम स्थिति का एक स्नैपशॉट संग्रहीत करता है; ग्राहकों से इनपुट प्राप्त करने पर, सर्वर परिवर्तनों को दर्शाते हुए एक नया स्नैपशॉट बनाता है। फिर, यह नए स्नैपशॉट और पिछले एक के बीच के अंतरों की गणना करता है और उन्हें क्लाइंट को भेजता है, ताकि वे सिंक में हो सकें।

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

हालाँकि, मैं स्नैपशॉट सिस्टम को लागू करने का एक अच्छा तरीका नहीं खोज सकता। मुझे एकल-खिलाड़ी प्रोग्रामिंग आर्किटेक्चर से दूर जाना वास्तव में कठिन लगता है और सोचता हूं कि मैं गेम स्टेट को इस तरह से कैसे स्टोर कर सकता हूं:

  • सभी डेटा को तर्क से अलग किया गया है
  • गेम स्टेट्स के स्नैपशॉट के बीच अंतर की गणना की जा सकती है
  • खेल संस्थाओं को अभी भी आसानी से कोड के माध्यम से हेरफेर किया जा सकता है

एक स्नैपशॉट वर्ग कैसे लागू किया जाता है? संस्थाओं और उनके डेटा को कैसे संग्रहीत किया जाता है? क्या हर क्लाइंट इकाई के पास एक आईडी है जो सर्वर पर एक आईडी से मेल खाती है?

स्नैपशॉट अंतर की गणना कैसे की जाती है?

सामान्य तौर पर: एक गेम-स्टेट स्नैपशॉट सिस्टम कैसे लागू किया जाएगा?


4
+1। यह किसी एक प्रश्न के लिए थोड़ा अधिक व्यापक है, लेकिन IMO यह एक दिलचस्प विषय है जिसे उत्तर में मोटे तौर पर कवर किया जा सकता है।
Kromster

आप सिर्फ 1 स्नैपशॉट (वास्तविक दुनिया) को स्टोर क्यों नहीं करते हैं, इस नियमित रूप से विश्व-राज्य में आने वाले हर परिवर्तन को सहेजते हैं और सूची या कुछ में बदलाव को संग्रहीत करते हैं। फिर, जब सभी ग्राहकों को परिवर्तन भेजने का समय आता है, तो केवल उन सभी को सूची की सामग्री भेजें और सूची को खाली करें, शून्य (परिवर्तन) से शुरू करें। शायद यह 2 स्नैपशॉट स्टोर करने के लिए उतना अच्छा नहीं है लेकिन इस दृष्टिकोण के साथ आपको एल्गोरिदम के बारे में चिंता करने की ज़रूरत नहीं है कि 2 स्नैपशॉट कैसे तेजी से फैल सकते हैं।
tkausl 15

क्या आपने इसे पढ़ा है: fabiensanglard.net/quake3/network.php - भूकंप 3 नेटवर्क मॉडल की समीक्षा में कार्यान्वयन पर चर्चा शामिल है।
स्टीवन

किस तरह के खेल के निर्माण का प्रयास कर रहे हैं? नेटवर्क सेटअप आपके द्वारा किए जा रहे गेम के प्रकार पर बहुत अधिक निर्भर करता है। एक RTS नेटवर्किंग के मामले में FPS की तरह व्यवहार नहीं करता है।
AturSams

जवाबों:


3

आप स्नैपशॉट डेल्टा (इसकी पिछली सिंक की गई स्थिति में परिवर्तन) की गणना दो स्नैपशॉट उदाहरणों को रखकर कर सकते हैं: वर्तमान एक और अंतिम सिंक किए गए।

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

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

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

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

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


2

सबसे पहले आपको यह जानना होगा कि प्रोटोकॉल संगत तरीके से अपने प्रासंगिक डेटा का प्रतिनिधित्व कैसे करें। यह गेम से संबंधित डेटा पर निर्भर करता है। मैं एक उदाहरण के रूप में एक आरटीएस खेल का उपयोग करूंगा।

नेटवर्किंग के उद्देश्यों के लिए, खेल में सभी संस्थाओं की गणना की जाती है (जैसे पिकअप, यूनिट, भवन, प्राकृतिक संसाधन, विनाशकारी)।

खिलाड़ियों को उनके लिए प्रासंगिक डेटा (उदाहरण के लिए सभी दृश्य इकाइयों) की आवश्यकता है:

  • क्या वे जीवित हैं या मृत हैं?
  • वे किस प्रकार के हैं?
  • उनके पास कितना स्वास्थ्य है?
  • वर्तमान स्थिति, रोटेशन, वेग (गति + दिशा), निकट भविष्य में पथ ...
  • गतिविधि: हमला करना, चलना, निर्माण करना, ठीक करना, उपचार, आदि ...
  • बफ़ / डेबफ़ स्थिति प्रभाव
  • और संभवतः अन्य आँकड़े जैसे मान, ढाल और क्या नहीं?

खेल में प्रवेश करने से पहले खिलाड़ी को पूर्ण राज्य प्राप्त करना चाहिए (या वैकल्पिक रूप से उस खिलाड़ी के लिए प्रासंगिक सभी जानकारी)।

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

सर्वर वर्तमान वैश्विक स्थिति को संग्रहीत करता है। प्रत्येक खिलाड़ी के सबसे हाल ही में अद्यतन किए गए राज्य को एक पॉइंटर द्वारा listहाल के परिवर्तनों का संकेत दिया जाता है (पॉइंटर के बाद सभी परिवर्तन अभी तक उस खिलाड़ी को नहीं भेजे गए हैं)। परिवर्तन listतब होते हैं जब वे होते हैं। एक बार जब सर्वर को अंतिम अपडेट भेजने के साथ किया जाता है, तो यह सूची पर पुनरावृति करना शुरू कर सकता है: सर्वर खिलाड़ी की पॉइंटर को उसकी पूंछ की सूची में ले जाता है, रास्ते में सभी परिवर्तनों को इकट्ठा करता है और उन्हें एक बफर में भेज देता है जिसे भेजा जाएगा खिलाड़ी (यानी प्रोटोकॉल का प्रारूप कुछ इस तरह हो सकता है: Unit_id; attr_id; new_value) नई इकाइयों को परिवर्तनों के रूप में भी माना जाता है और उन्हें प्राप्त करने वाले खिलाड़ियों को उनके सभी विशेषता मानों के साथ भेजा जाता है।

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

कुछ सवाल जो आपने नहीं उठाए और मुझे लगता है कि दिलचस्प हैं:

  1. ग्राहकों को पहली जगह में सभी डेटा के साथ एक स्नैपशॉट प्राप्त करना चाहिए? उनकी दृष्टि के बाहर की वस्तुओं के बारे में क्या? आरटीएस खेल में युद्ध के कोहरे के बारे में क्या? यदि आप सभी डेटा भेजते हैं, तो क्लाइंट को डेटा प्रदर्शित करने के लिए हैक किया जा सकता है जो खिलाड़ी के लिए उपलब्ध नहीं होना चाहिए (आपके अन्य सुरक्षा उपायों के आधार पर)। यदि आप केवल प्रासंगिक डेटा भेजते हैं, तो समस्या हल हो गई है।
  2. सभी सूचनाओं को भेजने के बजाय परिवर्तनों को भेजना कब महत्वपूर्ण है? आधुनिक मशीनों पर उपलब्ध बैंडविड्थ को ध्यान में रखते हुए, क्या हम सभी जानकारी भेजने के बजाय "डेल्टा" भेजने से कुछ हासिल करते हैं, यदि ऐसा है तो?
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.