रियल टाइम स्ट्रेटेजी गेम्स के लिए नेटवर्किंग


16

मैं एक कंप्यूटर विज्ञान पाठ्यक्रम के लिए एक वास्तविक समय रणनीति खेल विकसित कर रहा हूं। इसका एक कठिन पहलू क्लाइंट-सर्वर नेटवर्किंग और सिंक्रोनाइज़ेशन लगता है। मैंने इस विषय ( 1500 तीरंदाजों सहित ) पर पढ़ा है , लेकिन मैंने अन्य मॉडल (उदाहरण के लिए, लैन पर) के विपरीत क्लाइंट-सर्वर दृष्टिकोण लेने का फैसला किया है।

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

अभी मैं पतले क्लाइंट कर रहा हूं, जो सिर्फ सर्वर को पैकेट भेजते हैं और प्रतिक्रिया का इंतजार करते हैं। हालांकि, कई समस्याएं हैं।

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

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

TL; DR प्रति सेकंड 50 घटनाओं के साथ RTS बना रहा है, मैं ग्राहकों को कैसे सिंक्रनाइज़ करूं?


आप शायद ईव-ऑनलाइन करते हैं और सब कुछ ठीक से संसाधित करने की अनुमति देने के लिए "धीमा" समय लागू कर सकते हैं।
रयान एरब

3
यहाँ ग्रहों के एनीहिलेशन के क्लाइंट / सर्वर मॉडल का एक अनिवार्य लिंक दिया गया है: forrestthewoods.ghost.io/… यह लॉकस्टेप मॉडल का एक विकल्प है जो लगता है कि उनके लिए बहुत अच्छा काम कर रहा है।
DallonF

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

जवाबों:


12

वास्तविक समय में प्रति सेकंड 50 घटनाओं को सिंक्रनाइज़ करने का आपका लक्ष्य मुझे लगता है कि यह यथार्थवादी नहीं है। यही कारण है कि 1500 तीरंदाज लेख में लॉक-स्टेप दृष्टिकोण के बारे में बात की गई है, अच्छी तरह से, के बारे में बात की गई है!

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


6

खिलाड़ी द्वारा की जाने वाली प्रत्येक कार्रवाई नियतात्मक होती है, हालांकि, ऐसी घटनाएं होती हैं जो निर्धारित अंतराल पर होती हैं

मुझे लगता है कि आपकी समस्या है; आपके गेम में केवल एक टाइमलाइन (गेमप्ले प्रभावित करने वाली चीजों के लिए) होनी चाहिए। आप कहते हैं कि कुछ चीजें एक्स प्रति सेकंड की दर से बढ़ती हैं ; पता करें कि एक सेकंड में कितने गेम स्टेप्स हैं और इसे X प्रति Y गेम स्टेप्स की दर में परिवर्तित करें । फिर भले ही खेल धीमा हो जाए, लेकिन सब कुछ नियतात्मक होता है।

खेल को वास्तविक समय में स्वतंत्र रूप से चलाने के अन्य फायदे हैं:

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

आपने यह भी उल्लेख किया है कि जब आप> 50 ईवेंट हों, या सेकंड तक की देरी हो, तो आप समस्याओं में भाग लेंगे। यह 1500 तीरंदाजों में वर्णित परिदृश्य की तुलना में बहुत छोटा है , इसलिए देखें कि क्या आप अपने गेम को प्रोफाइल कर सकते हैं और पता लगा सकते हैं कि मंदी कहां है।


1
+1: फ़्रेम-आधारित सही विकल्प है, समय-आधारित नहीं। आप निश्चित रूप से प्रति सेकंड एन फ्रेम रखने का प्रयास कर सकते हैं। एक मामूली अड़चन फुल-ऑन डेसिंक से बेहतर है।
पैट्रिकबी

@PatrickB: मैं देखता हूं कि कई गेम एक "सिम्युलेटेड" समय का उपयोग करते हैं जो वीडियो फ्रेम से बंधा नहीं है । Warcraft की दुनिया केवल हर 100ms माने जैसी चीजों को अपडेट करती है, और बौना किले प्रति वीडियो फ्रेम में 10 टिक्स को डिफॉल्ट करता है।
मिंग डक

@Mooing Duck: मेरी टिप्पणी RTS के लिए विशिष्ट थी। ऐसी किसी चीज़ के लिए जहाँ छोटी-छोटी त्रुटियों को सहन किया जा सकता है और बाद में ठीक किया जा सकता है (जैसे MMORPGs, FPSs), तो निरंतर मूल्यों का उपयोग करना न केवल ठीक है, बल्कि महत्वपूर्ण भी है। हालाँकि, नियतात्मक सिमुलेशन जिन्हें कई मशीनों में समकालिक किया जाना है? फ्रेम करने के लिए छड़ी।
पैट्रिकबी

4

सबसे पहले, अनुसूचित घटनाओं के साथ समस्या को हल करने के लिए, जब वे होते हैं तो घटनाओं को प्रसारित नहीं करते हैं , लेकिन जब वे शुरू में निर्धारित होते हैं। यही है, बजाय एक "की टाइल ऊर्जा (बढ़ा देते भेजने की एक्स , वाई संदेश हर दूसरे)", बस "कह टाइल की ऊर्जा बढ़ाने के (एक संदेश भेजने के लिए एक्स , वाई ) एक बार प्रति सेकंड जब तक यह भरा हुआ है, या जब तक बाधित "। प्रत्येक क्लाइंट स्थानीय रूप से अपडेट शेड्यूल करने के लिए जिम्मेदार है।

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

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


दूसरा, ग्राहकों को सिंक्रनाइज़ रखने के लिए, सुनिश्चित करें कि आपका खेल नियतात्मक है। अन्य उत्तरों ने पहले ही इसके लिए अच्छी सलाह दे दी है, लेकिन मुझे क्या करना है इसका एक संक्षिप्त सारांश शामिल करें:

  • अपने खेल को आंतरिक रूप से मोड़-आधारित बनाएं, प्रत्येक मोड़ या "टिक" लेने के साथ, कहें, 1/50 सेकंड। (वास्तव में, आप शायद 1/10 सेकंड के साथ या उससे अधिक समय के लिए दूर हो सकते हैं।) किसी भी खिलाड़ी को एक ही मोड़ के दौरान होने वाली क्रियाओं को एक साथ इलाज किया जाना चाहिए। सभी संदेश, कम से कम सर्वर से क्लाइंट तक, टर्न नंबर के साथ टैग किए जाने चाहिए, ताकि प्रत्येक क्लाइंट को पता चले कि प्रत्येक ईवेंट किस मोड़ पर होता है।

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

  • एक ही मोड़ पर होने वाली "एक साथ" घटनाओं के लिए एक सुसंगत आदेश को परिभाषित करें, ताकि प्रत्येक ग्राहक उसी क्रम में उन्हें निष्पादित करें। यह आदेश कुछ भी हो सकता है, जब तक यह निर्धारक और सभी ग्राहकों (और सर्वर) के लिए समान है।

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

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


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

1
@MingDuck: अच्छी बात है। वास्तव में, मैं सुझाव दूंगा कि वर्तमान RNG बीज को हर मोड़ पर प्रसारित किया जाए, ताकि RNG वंशानुक्रम का तुरंत पता चल सके। इसके अलावा, यदि आपके यूआई कोड को किसी भी यादृच्छिकता की आवश्यकता है, तो इसे उसी RNG उदाहरण से खींचें जैसा कि गेम लॉजिक के लिए उपयोग किया जाता है।
इल्मरी करोनन

3

आपको अपने खेल तर्क को वास्तविक समय से पूरी तरह से स्वतंत्र बनाना चाहिए और अनिवार्य रूप से इसे टर्न-आधारित बनाना चाहिए। इस तरह से आपको पता चल जाता है कि किस मोड़ पर "टाइल ऊर्जा परिवर्तन होता है"। आपके मामले में, प्रत्येक मोड़ एक सेकंड का सिर्फ 1/50 वां है।

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


1

सबसे पहले आपको यह समझना होगा कि PC फ्लोट / डबल गणित IS निर्धारक नहीं है, जब तक कि आप अपनी गणना के लिए IEEE-754 का सख्ती से उपयोग करने का उल्लेख नहीं करते (धीमा होगा)

फिर यह है कि मैं इसे कैसे लागू करूंगा: क्लाइंट सर्वर से जुड़ता है, और समय का पता लगाता है (पिंग लेटेंसी का ख्याल रखें!) (लंबे गेमप्ले के लिए यह टाइमस्टैम्प / टर्न रिसीव करने के लिए आवश्यक हो सकता है)

अब, जब भी कोई ग्राहक कोई कार्रवाई करता है, तो उसमें टाइमस्टैम्प / टर्न शामिल होता है, और खराब टाइमस्टैम्प / टर्न को अस्वीकार करने के लिए सर्वर पर निर्भर होता है। फिर सर्वर ग्राहकों को कार्रवाई वापस भेज देता है, और हर बार एक मोड़ "बंद" होता है (उर्फ सर्वर मोड़ / टाइमस्टैम्प को इतना पुराना स्वीकार नहीं करेगा), सर्वर ग्राहकों को भेजते हैं और कार्रवाई को समाप्त करते हैं।

ग्राहकों के पास 2 "दुनिया" होगी: एक एंड-टर्न के साथ सिंक में है, दूसरे की गणना एंड-टर्न से शुरू होती है, कतार पर आने वाली कार्रवाई को जोड़ते हुए, जब तक कि वर्तमान क्लाइंट मोड़ / टाइमस्टैम्प नहीं हो जाता।

क्योंकि सर्वर थोड़ी पुरानी कार्रवाई को स्वीकार करेगा, ग्राहक सीधे कतार में अपनी कार्रवाई जोड़ सकता है, इसलिए नेटवर्क पर राउंड ट्रिप का समय कम से कम आपकी अपनी कार्रवाई के लिए छिपा होगा।

अंतिम बात यह है कि अधिक कार्रवाई को कतार में रखें ताकि आप MTU पैकेट को भर सकें, जिससे कम प्रोटोकोल ओवरहेड हो सकता है; एक अच्छा विचार यह है कि सर्वर पर किया जाए, इसलिए हर एंड-टर्न इवेंट में कतार पर कार्रवाई होती है।

मैं एक वास्तविक समय की शूटिंग के खेल पर इस algoritm का उपयोग करता हूं, और ठीक काम करता है (एक ग्राहक के बिना अपनी कार्रवाई का पीछा करते हुए, लेकिन सर्वर पिंग कम 20 / 50ms के साथ), साथ ही हर एक्स एंड-टर्न सर्वर एक विशेष "सभी को भेजते हैं" क्लाइंट मैप "पैकेट, बहाव मूल्यों को सही करने के लिए।


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

पूर्णांक के साथ क्षैतिज टाइल करना मुश्किल है, जब तक कि यह एक ऑक्टोगोनल बोर्ड नहीं है। नियत बिंदु के लिए कोई hw त्वरण नहीं है, इसलिए यह फ्लोट की तुलना में धीमा हो सकता है Iee754
लेस्टो
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.