रीप्ले सिस्टम कैसे डिज़ाइन करें


75

तो मैं रीप्ले सिस्टम कैसे डिजाइन करूंगा?

आप इसे कुछ विशेष गेम जैसे कि Warcraft 3 या Starcraft से जान सकते हैं जहां आप पहले से खेले जाने के बाद गेम को फिर से देख सकते हैं।

आप अपेक्षाकृत छोटे रीप्ले फ़ाइल के साथ समाप्त होते हैं। तो मेरे सवाल हैं:

  • कैसे बचाएं डाटा? (कस्टम प्रारूप?) (छोटी फ़ाइल)
  • क्या बचाया जाएगा?
  • इसे सामान्य कैसे बनाया जाए ताकि इसका उपयोग अन्य खेलों में समयावधि रिकॉर्ड करने के लिए किया जा सके (और उदाहरण के लिए पूर्ण मिलान नहीं)?
  • इसे फॉरवर्ड करना और रिवाइंड करना संभव है (WC3 जहां तक ​​मुझे याद है वह रिवाइंड नहीं कर सकता)

3
यद्यपि नीचे दिए गए उत्तर बहुत अधिक मूल्यवान अंतर्दृष्टि प्रदान करते हैं, मैं सिर्फ आपके खेल / इंजन को अत्यधिक नियतात्मक ( en.wikipedia.org/wiki/Deterministic_al एल्गोरिदम ) होने के महत्व पर बल देना चाहता था , क्योंकि यह आपके लक्ष्य को प्राप्त करने के लिए आवश्यक है।
अरी पैट्रिक

2
यह भी ध्यान दें कि भौतिकी इंजन नियतात्मक नहीं हैं (हॉकोक यह दावा करता है ...) इसलिए केवल इनपुट और टाइमस्टैम्प को स्टोर करने का समाधान हर बार अलग-अलग परिणाम देगा यदि आपका गेम भौतिकी का उपयोग करता है।
समरसा

5
अधिकांश भौतिकी इंजन तब तक नियतात्मक होते हैं जब तक आप एक निश्चित टाइमस्टेप का उपयोग करते हैं, जो आपको वैसे भी करना चाहिए। मुझे बहुत आश्चर्य होगा अगर हॉक नहीं है। गैर-नियतत्ववाद कंप्यूटर पर आने के लिए काफी कठिन है ...

4
नियतात्मक का अर्थ है समान इनपुट = समान आउटपुट। यदि आपको एक प्लेटफ़ॉर्म पर फ़्लोट किया गया है और दूसरे (उदाहरण के लिए) पर डबल्स किया गया है, या अपने IEEE फ़्लोटिंग पॉइंट मानक कार्यान्वयन को निष्क्रिय कर दिया है, तो इसका मतलब है कि आप एक ही इनपुट के साथ नहीं चल रहे हैं, ऐसा नहीं है कि यह नियतात्मक नहीं है।

3
क्या यह मुझे है, या क्या यह सवाल हर दूसरे हफ्ते में मिलता है?
कम्युनिस्ट डक

जवाबों:


39

यह उत्कृष्ट लेख बहुत सारे मुद्दों को शामिल करता है: http://www.gamasutra.com/view/feature/2029/developing_your_own_replay_system.php

कुछ चीजें जो लेख का उल्लेख करता है और अच्छी तरह से करता है:

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

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

इस तरह की प्रणाली को पुन: प्रयोज्य बनाने के संदर्भ में, मैंने जेनेरिक इनपुट धाराओं के साथ रीप्ले सिस्टम सौदा किया है, लेकिन इन स्ट्रीमों को मार्शल कीबोर्ड / गेमपैड / माउस इनपुट के लिए गेम विशिष्ट तर्क देने की अनुमति देने के लिए हुक भी प्रदान करता है।

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


यदि RNG शामिल है, तो धाराओं में उक्त RNG के परिणामों को शामिल करें
शाफ़्ट फ्रीक

1
@ratchet फ्रीक: PRNG के नियतात्मक उपयोग के साथ आप चौकियों के दौरान केवल इसके बीज का भंडारण करके प्राप्त कर सकते हैं।
नॉनमरिक

22

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

चूंकि आपको रिवाइंडिंग के लिए वैसे भी चेकपॉइंटिंग की आवश्यकता होगी, इसलिए आप चीजों को जटिल करने से पहले इसे सरल तरीके से लागू करने का प्रयास कर सकते हैं।


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

@ समसौरा - यदि आप एक मानक संपीड़न पुस्तकालयों (जैसे gzip) का उपयोग करते हैं तो आपको वही (शायद बेहतर) संपीडन मिलेगा, जो मैन्युअल रूप से जाँचने जैसी चीज़ों की आवश्यकता के बिना हो सकता है कि राज्य बदल गया है या नहीं।
जस्टिन

2
@ क्रैगन: वास्तव में सच नहीं है। मानक संपीड़न पुस्तकालय निश्चित रूप से अच्छे हैं, लेकिन अक्सर डोमेन-विशिष्ट ज्ञान का लाभ नहीं ले पाएंगे। यदि आप उन्हें थोड़ा सा बाहर निकालने में मदद कर सकते हैं, तो उसी तरह के डेटा को आसन्न करके और ऐसे सामान को बाहर निकाल दें जो वास्तव में नहीं बदले, आप चीजों को काफी कम कर सकते हैं।
ज़ोरबहुत

1
@ZorbaTHut सिद्धांत रूप में हाँ, लेकिन व्यवहार में यह वास्तव में प्रयास के लायक है?
जस्टिन

4
क्या यह प्रयास के लायक है यह पूरी तरह से इस बात पर निर्भर करता है कि आपके पास कितना डेटा है। यदि आपको सैकड़ों या हजारों इकाइयों के साथ एक आरटीएस मिला है, तो यह संभवतः मायने रखता है। यदि आपको ब्रैड की तरह रिप्ले स्टोर करना है, तो यह शायद मायने रखता है।

21

आप अपने सिस्टम को देख सकते हैं जैसे कि यह राज्यों और कार्यों की एक श्रृंखला से बना है, जहां f[j]इनपुट के साथ एक फ़ंक्शन x[j]सिस्टम स्थिति s[j]को राज्य में बदलता है s[j+1], जैसे:

s[j+1] = f[j](s[j], x[j])

एक राज्य आपकी पूरी दुनिया की व्याख्या है। खिलाड़ी के स्थान, दुश्मन के स्थान, स्कोर, शेष बारूद, आदि। सब कुछ आप अपने खेल का एक फ्रेम आकर्षित करने की आवश्यकता है।

एक फ़ंक्शन कुछ भी है जो दुनिया को प्रभावित कर सकता है। एक फ्रेम परिवर्तन, एक कीपर, एक नेटवर्क पैकेट।

इनपुट वह डेटा है जिसे फ़ंक्शन लेता है। अंतिम फ़्रेम पास होने के बाद एक फ़्रेम परिवर्तन में समय की मात्रा लग सकती है, कुंजीपट में वास्तविक कुंजी दबाया जा सकता है, साथ ही शिफ्ट कुंजी दबाया गया था या नहीं।

इस स्पष्टीकरण के लिए, मैं निम्नलिखित धारणाएँ बनाऊंगा:

अनुमान 1:

गेम के दिए गए रन के लिए राज्यों की मात्रा कार्यों की मात्रा से बहुत बड़ी है। आपके पास संभवतः सैकड़ों हजारों राज्य हैं, लेकिन केवल कई दर्जन फ़ंक्शन (फ़्रेम परिवर्तन, कीबोर्ड, नेटवर्क पैकेट, आदि)। निस्संदेह, इनपुट की राशि राज्यों के ऋण की राशि के बराबर होनी चाहिए।

अनुमान 2:

किसी एक राज्य के भंडारण की स्थानिक लागत (मेमोरी, डिस्क) एक कार्य और उसके इनपुट को संग्रहित करने से बहुत अधिक होती है।

अनुमान 3:

किसी राज्य को प्रस्तुत करने की लौकिक लागत (समय) समान है, या किसी राज्य के कार्य की गणना करने की तुलना में अधिक समय के एक या दो आदेश।

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

विधि 1:

स्टोर s[0]...s[n]। यह बहुत सरल है, बहुत सीधा है। 2 धारणा के कारण, इस की स्थानिक लागत काफी अधिक है।

शतरंज के लिए, यह प्रत्येक चाल के लिए पूरे बोर्ड को खींचकर पूरा किया जाएगा।

विधि 2:

यदि आपको केवल फॉरवर्ड रिप्ले की आवश्यकता है, तो आप बस स्टोर कर सकते हैं s[0], और फिर स्टोर कर सकते हैं f[0]...f[n-1](याद रखें, यह केवल फ़ंक्शन की आईडी का नाम है) और x[0]...x[n-1](इन कार्यों में से प्रत्येक के लिए इनपुट क्या था)। फिर से खेलना करने के लिए, आप बस के साथ शुरू करते हैं s[0], और गणना करते हैं

s[1] = f[0](s[0], x[0])
s[2] = f[1](s[1], x[1])

और इसी तरह...

मैं यहां एक छोटा सा एनोटेशन करना चाहता हूं। कई अन्य टिप्पणीकारों ने कहा कि खेल "निर्धारक होना चाहिए"। जो कोई भी कहता है कि कंप्यूटर साइंस 101 को फिर से लेने की आवश्यकता है, क्योंकि जब तक आपका गेम क्वांटम कंप्यूटर पर चलने का नहीं है, सभी कंप्यूटर प्रोग्राम डीटेरिमिनिस्टिक हैं। यही कारण है कि कंप्यूटर इतना भयानक है।

हालाँकि, चूंकि आपका कार्यक्रम सबसे अधिक संभावना बाहरी कार्यक्रमों पर निर्भर करता है, पुस्तकालयों से लेकर सीपीयू के वास्तविक कार्यान्वयन तक, यह सुनिश्चित करना कि आपके कार्य प्लेटफ़ॉर्म के बीच समान व्यवहार करते हैं, काफी मुश्किल हो सकता है।

यदि आप छद्म यादृच्छिक संख्याओं का उपयोग करते हैं, तो आप या तो अपने इनपुट के हिस्से के रूप में उत्पन्न संख्याओं को स्टोर कर सकते हैं x, या अपने राज्य के हिस्से के रूप में प्रिंग फ़ंक्शन की स्थिति s, और फ़ंक्शन के भाग के रूप में इसके कार्यान्वयन को स्टोर कर सकते हैं f

शतरंज के लिए, यह प्रारंभिक बोर्ड (जो ज्ञात है) को आरेखित करके पूरा किया जाएगा और फिर प्रत्येक चाल का वर्णन करते हुए कहा जाएगा कि कौन सा टुकड़ा कहाँ गया। यह है कि वे वास्तव में इसे कैसे करते हैं, वैसे।

विधि 3:

अब, आप सबसे अधिक संभावना है कि आप फिर से खेलना चाहते हैं। यही है, s[n]एक मनमानी के लिए गणना करें n। विधि 2 का उपयोग करके, आपको गणना करने s[0]...s[n-1]से पहले गणना करने की आवश्यकता है s[n], जो कि धारणा 2 के अनुसार, काफी धीमी हो सकती है।

इसे लागू करने के लिए, विधि 3, विधियों 1 और 2 का एक सामान्यीकरण है: स्टोर f[0]...f[n-1]और x[0]...x[n-1]विधि 2 की तरह, लेकिन स्टोर भी s[j], सभी के j % Q == 0लिए एक निरंतर Q। आसान शब्दों में, इसका मतलब है कि आप प्रत्येक Qराज्य में से एक पर एक बुकमार्क स्टोर करते हैं। उदाहरण के लिए Q == 100, आप स्टोर करते हैंs[0], s[100], s[200]...

s[n]एक मनमानी के लिए गणना करने के लिए n, आप पहले संग्रहीत पहले लोड करते हैं s[floor(n/Q)], और फिर से सभी कार्यों की गणना floor(n/Q)करते हैं n। अधिक से अधिक, आप Qकार्यों की गणना करेंगे । छोटे मानों की Qगणना तेजी से Qहोती है, लेकिन अधिक स्थान की खपत करते हैं, जबकि कम स्थान की खपत के बड़े मूल्य , लेकिन गणना करने में अधिक समय लेते हैं।

पद्धति 3 के साथ Q==1विधि 1 के समान है, जबकि विधि 3 के साथ Q==infविधि 2 के समान है।

शतरंज के लिए, यह हर चाल को पूरा करने के साथ-साथ हर 10 बोर्ड (एक Q==10) में से एक होगा।

विधि 4:

आप पुनरावृत्ति रिवर्स करना चाहते हैं, तो आप विधि 3. का एक छोटा सा बदलाव मान लीजिए कर सकते हैं Q==100, और आप गणना करना चाहते हैं s[150]के माध्यम से s[90]रिवर्स में। अनमॉडिफाइड विधि 3 के साथ, आपको प्राप्त करने के लिए 50 गणना करने की आवश्यकता होगी s[150]और फिर प्राप्त करने के लिए 49 और अधिक गणना करनी होगी s[149]। लेकिन जब से आप पहले से ही s[149]प्राप्त करने के लिए गणना करते हैं s[150], s[100]...s[150]तब आप s[150]पहली बार गणना करने के साथ कैश बना सकते हैं और तब आप पहले s[149]से ही कैश में होते हैं जब आपको इसे प्रदर्शित करने की आवश्यकता होती है।

आप केवल कैश हर बार जब आप की गणना करनी है पुनर्जीवित करने के लिए की जरूरत है s[j], के लिए j==(k*Q)-1किसी भी के लिए k। इस बार, बढ़ने Qका परिणाम छोटे आकार (सिर्फ कैश के लिए) होगा, लेकिन अधिक समय (बस कैश को पुनः प्राप्त करने के लिए) होगा। Qयदि आप राज्यों और कार्यों की गणना करने के लिए आवश्यक आकारों और समय को जानते हैं, तो इसके लिए एक इष्टतम मूल्य की गणना की जा सकती है।

शतरंज के लिए, यह हर चाल को पूरा करने के साथ-साथ प्रत्येक 10 बोर्डों (के लिए Q==10) में एक पूरा किया जाएगा , लेकिन इसके अलावा, इसे कागज के एक अलग टुकड़े में ड्रा करना होगा, पिछले 10 बोर्ड जिनकी आपने गणना की है।

विधि 5:

यदि राज्य केवल बहुत अधिक स्थान का उपभोग करते हैं, या फ़ंक्शन बहुत अधिक समय लेते हैं, तो आप एक समाधान बना सकते हैं जो वास्तव में लागू होता है (नकली नहीं) उल्टा दोहराता है। ऐसा करने के लिए, आपको प्रत्येक फ़ंक्शन के लिए रिवर्स फ़ंक्शन बनाने होंगे। हालाँकि, इसके लिए आवश्यक है कि आपका प्रत्येक कार्य एक इंजेक्शन हो। यदि यह उल्लेखनीय है, तो f'फ़ंक्शन के व्युत्क्रम को निरूपित करने के लिए f, गणना s[j-1]करना उतना ही सरल है

s[j-1] = f'[j-1](s[j], x[j-1])

ध्यान दें कि यहां, फ़ंक्शन और इनपुट दोनों हैं j-1, नहीं j। यदि आप गणना कर रहे थे तो यह वही फ़ंक्शन और इनपुट होगा जिसका आपने उपयोग किया होगा

s[j] = f[j-1](s[j-1], x[j-1])

इन कार्यों का उलटा बनाना मुश्किल हिस्सा है। हालाँकि, आप आमतौर पर नहीं कर सकते हैं, क्योंकि कुछ राज्य डेटा आमतौर पर एक गेम में प्रत्येक फ़ंक्शन के बाद खो जाता है।

यह विधि, जैसा है, गणना को उल्टा कर सकता है s[j-1], लेकिन केवल अगर आपके पास है s[j]। इसका मतलब यह है कि आप केवल रिप्ले को पीछे की तरफ देख सकते हैं, उस बिंदु से शुरू कर सकते हैं जिस पर आपने पीछे की ओर फिर से खेलना का फैसला किया था। यदि आप एक मनमाना बिंदु से पीछे की ओर खेलना चाहते हैं, तो आपको इसे विधि 4 के साथ मिलाना होगा।

शतरंज के लिए, इसे लागू नहीं किया जा सकता है, क्योंकि किसी दिए गए बोर्ड और पिछले कदम के साथ, आप जान सकते हैं कि कौन सा टुकड़ा स्थानांतरित किया गया था, लेकिन जहां से इसे स्थानांतरित नहीं किया गया था।

विधि 6:

अंत में, यदि आप गारंटी नहीं दे सकते कि आपके सभी कार्य इंजेक्शन हैं, तो आप ऐसा करने के लिए एक छोटी सी चाल बना सकते हैं। प्रत्येक फ़ंक्शन को केवल एक नया, राज्य वापस करने के बजाय, आप इसे छोड़ दिए गए डेटा को भी वापस कर सकते हैं, जैसे:

s[j+1], r[j] = f[j](s[j], x[j])

कहाँ r[j]खारिज डाटा नहीं है। और फिर अपने उलटा कार्य करें ताकि वे डेटा को छोड़ दें, जैसे:

s[j] = f'[j](s[j+1], x[j], r[j])

के अलावा f[j]और x[j], आप भी स्टोर चाहिए r[j]प्रत्येक कार्य के लिए। एक बार फिर, यदि आप तलाश करना चाहते हैं, तो आपको बुकमार्क स्टोर करना होगा, जैसे कि विधि 4।

शतरंज के लिए, यह विधि 2 के समान ही होगा, लेकिन विधि 2 के विपरीत, जो केवल कहता है कि कौन सा टुकड़ा कहां जाता है, आपको यह भी संग्रहीत करने की आवश्यकता है कि प्रत्येक टुकड़ा कहां से आया है।

कार्यान्वयन:

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

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

मैंने इसे कुछ प्रमुख खेलों में लागू किया है, ज्यादातर हाल ही में डेटा के पुनरावृत्ति के लिए जब एक घटना (एफपीएस में एक टुकड़ा, या खेल के खेल में एक स्कोर) होती है।

मुझे आशा है कि यह स्पष्टीकरण बहुत उबाऊ नहीं था।

Some इसका मतलब यह नहीं है कि कुछ कार्यक्रम गैर-नियतात्मक हैं (जैसे एमएस विंडोज ^ ^)। अब गंभीरता से, यदि आप एक निर्धारक कंप्यूटर पर एक गैर-नियतात्मक कार्यक्रम बना सकते हैं, तो आप बहुत आश्वस्त हो सकते हैं कि आप एक साथ फील्ड्स मेडल, ट्यूरिंग पुरस्कार और शायद सभी के लिए ऑस्कर और ग्रैमी भी जीत लेंगे।


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

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

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

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

9

एक बात जो अन्य उत्तर अभी तक कवर नहीं की गई है, वे हैं तैरने का खतरा। आप फ़्लोट्स का उपयोग करके पूरी तरह से निर्धारक अनुप्रयोग नहीं बना सकते।

फ्लोट्स का उपयोग करना, आपके पास पूरी तरह से नियतात्मक प्रणाली हो सकती है, लेकिन केवल अगर:

  • बिल्कुल उसी बाइनरी का उपयोग करना
  • बिल्कुल उसी CPU का उपयोग करना

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

यह काफी स्पष्ट है कि यह AMD बनाम इंटेल बिट को कैसे प्रभावित करेगा - मान लीजिए कि एक 80 बिट फ्लोट और दूसरा 64 का उपयोग करता है, उदाहरण के लिए - लेकिन एक ही बाइनरी आवश्यकता क्यों है?

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

आप न्यूनतम संभव सटीकता का उपयोग करने के लिए _control87 () / _ controlfp () झंडे सेट करके इसकी मदद कर सकते हैं । हालाँकि, कुछ पुस्तकालय भी इसे छू सकते हैं (कम से कम कुछ संस्करण d3d ने किया)।


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

8

अपने यादृच्छिक संख्या जनरेटर की प्रारंभिक स्थिति को बचाएं। फिर सहेजें, टाइमस्टैम्प्ड, प्रत्येक इनपुट (माउस, कीबोर्ड, नेटवर्क, जो भी हो)। यदि आपके पास एक नेटवर्क गेम है, तो संभवतः आपके पास यह सब पहले से ही है।

RNGs को फिर से सेट करें और इनपुट वापस करें। बस।

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

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


4

मैं निर्धारक पुनरावृत्ति के खिलाफ मतदान करूंगा। यह एफएआर सरल है और एफएआर कम त्रुटि-प्रवण हर इकाई की स्थिति को हर 1 / Nth सेकंड में बचाने के लिए।

जो आप प्लेबैक पर दिखाना चाहते हैं, उसे बचाएं - यदि यह सिर्फ पोजीशन और हेडिंग है, तो ठीक है, यदि आप आंकड़े भी दिखाना चाहते हैं, तो भी, लेकिन सामान्य रूप से जितना संभव हो उतना कम बचाएं।

एन्कोडिंग को ट्वीक करें। सब कुछ के लिए जितना संभव हो उतना कम बिट्स का उपयोग करें। रीप्ले को तब तक परफेक्ट नहीं होना है जब तक यह काफी अच्छा दिखता है। यहां तक ​​कि अगर आप फ्लोट के लिए उपयोग करते हैं, कहते हैं, शीर्षक, आप इसे बाइट में सहेज सकते हैं और 256 संभावित मान (1.4। परिशुद्धता) प्राप्त कर सकते हैं। यह आपकी विशेष समस्या के लिए पर्याप्त या बहुत अधिक हो सकता है।

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

यदि आप आसान रिवाइंड करना चाहते हैं, तो प्रत्येक N फ्रेम में keyframes (पूरा डेटा, कोई डेल्टास) नहीं जोड़ें। इस तरह से आप डेल्टास और अन्य मूल्यों के लिए कम सटीकता के साथ दूर हो सकते हैं, यदि आप समय-समय पर "सही" मान पर रीसेट करते हैं, तो गोलाई त्रुटियां इतनी समस्याग्रस्त नहीं होंगी।

अंत में, पूरी बात gzip :)


1
हालांकि यह खेल के प्रकार पर थोड़ा निर्भर करता है।
जरी कोमप्पा

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

2

यह मुश्किल है। सबसे पहले और सबसे ज्यादा जरी कोमप्पा के उत्तर पढ़ें।

मेरे कंप्यूटर पर बनाया गया रीप्ले आपके कंप्यूटर पर काम नहीं कर सकता क्योंकि फ्लोट का परिणाम थोड़ा अलग होता है। यह एक बड़ा सौदा है।

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

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

मुख्य बिंदु हैं - यादृच्छिक संख्याओं से निपटना - इनपुट इनपुट (खिलाड़ी) और दूरस्थ खिलाड़ी (ओं) को कॉपी करना - फाइलों के इर्द-गिर्द कूदने के लिए डंपिंग स्थिति और ... - फ्लाइंग फ्लोट नॉट ब्रेक थिंग्स (हां, मुझे चिल्लाना पड़ा


2

मैं कुछ हद तक हैरान हूं कि किसी ने भी इस विकल्प का उल्लेख नहीं किया है, लेकिन यदि आपके गेम में मल्टीप्लेयर घटक है, तो आप इस सुविधा के लिए पहले से ही बहुत मेहनत कर सकते हैं। आखिरकार, मल्टीप्लेयर क्या है, लेकिन अपने कंप्यूटर पर अलग-अलग समय (थोड़ा) में किसी और की गतिविधियों को फिर से खेलने का प्रयास है?

यह आपको साइड इफेक्ट के रूप में एक छोटे फ़ाइल आकार का लाभ भी देता है, फिर से मान लें कि आप बैंडविड्थ के अनुकूल नेटवर्क कोड पर काम कर रहे हैं।

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

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


0

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

आपको सबसे अधिक खेल तर्क RNG और GUI, कण प्रभाव, ध्वनियों जैसी चीजों के लिए RNG की आवश्यकता है। एक बार जब आप यह कर लेते हैं, तो आपको खेल तर्क RNG की प्रारंभिक स्थिति को रिकॉर्ड करने की आवश्यकता होती है, फिर सभी खिलाड़ियों के गेम कमांड हर फ्रेम में होते हैं।

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

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

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

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

नोट: यदि आपके गेम में सामग्री की गतिशील स्ट्रीमिंग शामिल है या आपके पास कई थ्रेड्स या अलग-अलग कोर पर गेम-लॉजिक है ... शुभकामनाएँ।


0

रिकॉर्डिंग और रीवाइंडिंग दोनों को सक्षम करने के लिए, सभी घटनाओं (उपयोगकर्ता द्वारा उत्पन्न, टाइमर उत्पन्न, संचार उत्पन्न, ...) को रिकॉर्ड करें।

प्रत्येक ईवेंट के रिकॉर्ड समय के लिए, जो बदला गया था, पिछले मान, नए मान।

गणना मूल्यों को तब तक दर्ज करने की आवश्यकता नहीं है जब तक कि गणना यादृच्छिक न हो
(इन मामलों में आप या तो गणना किए गए मूल्यों को भी रिकॉर्ड कर सकते हैं, या प्रत्येक यादृच्छिक गणना के बाद बीज में रिकॉर्ड परिवर्तन कर सकते हैं)।

सहेजा गया डेटा परिवर्तनों की एक सूची है।
परिवर्तन विभिन्न स्वरूपों (बाइनरी, xml, ...) में बचाया जा सकता है।
परिवर्तन में इकाई आईडी, संपत्ति का नाम, पुराना मूल्य, नया मूल्य शामिल है।

सुनिश्चित करें कि आपका सिस्टम इन परिवर्तनों को वापस चला सकता है (वांछित इकाई तक पहुंच, नई राज्य के लिए वांछित संपत्ति को बदल दिया है या पुराने राज्य को पीछे)।

उदाहरण:

  • प्रारंभ से समय = t1, एंटिटी = खिलाड़ी 1, गुण = स्थिति, a से b में परिवर्तित
  • प्रारंभ से समय = t1, एंटिटी = सिस्टम, प्रॉपर्टी = गेम मोड, c से d में परिवर्तित
  • प्रारंभ से समय = टी 2, इकाई = खिलाड़ी 2, संपत्ति = राज्य, ई से एफ में बदल गया
  • तेजी से रिवाइंडिंग / फास्टफॉर्वर्डिंग या केवल कुछ निश्चित समय सीमाओं को रिकॉर्ड करने में सक्षम करने के लिए,
    कुंजी फ़्रेम आवश्यक हैं - यदि हर समय, हर समय रिकॉर्ड करना और फिर संपूर्ण गेम स्थिति को सहेजना।
    यदि केवल एक निश्चित समय सीमा की रिकॉर्डिंग की जाती है, तो शुरुआत में प्रारंभिक स्थिति को बचाएं।


    -1

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

    आप देखेंगे कि कोई भी पूर्ववत / फिर से लागू करने वाला व्यक्ति निर्धारक / गैर-नियतात्मक, फ्लोट चर या विशिष्ट सीपीयू के बारे में शिकायत नहीं करता है।


    पूर्ववत करें / पुन: करें उन अनुप्रयोगों में होता है जो स्वयं मूलभूत रूप से नियतात्मक, घटना-चालित और राज्य-प्रकाश होते हैं (उदाहरण के लिए शब्द प्रोसेसर दस्तावेज़ की स्थिति केवल पाठ और चयन है, न कि संपूर्ण लेआउट, जिसे पुन: प्रतिष्ठित किया जा सकता है)।

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