मैं XNA को अपडेट को रोकने से कैसे रोकूं जबकि विंडो का आकार बदला जा रहा है?


15

XNA कॉल करना बंद कर देता है Updateऔर Drawजबकि गेम विंडो को आकार दिया या स्थानांतरित किया जा रहा है।

क्यों? क्या इस व्यवहार को रोकने का कोई तरीका है?

(यह मेरे नेटवर्क कोड को डिसिन्क्रिनाइज़ करने का कारण बनता है, क्योंकि नेटवर्क संदेशों को पंप नहीं किया जा रहा है।)

जवाबों:


20

हाँ। इसमें XNA के आंतरिक लोगों के साथ खिलवाड़ की एक छोटी मात्रा शामिल है। मुझे इस वीडियो के दूसरे भाग में एक फ़िक्स का प्रदर्शन मिला है ।

पृष्ठभूमि:

ऐसा होने का कारण यह है क्योंकि XNA अपनी खेल घड़ी को निलंबित कर देता है जब Gameअंतर्निहित अंतर्निहित Formहै (या स्थानांतरित किया गया है, तो घटनाएं समान हैं)। यह, बदले में, क्योंकि यह अपने गेम लूप को चला रहा है Application.Idle। जब एक विंडो को आकार दिया जाता है, तो विंडोज कुछ पागल win32 संदेश लूप सामान करता है Idleजो फायरिंग से बचाता है।

इसलिए, यदि Gameइसकी घड़ी को निलंबित नहीं किया गया है , तो आकार बदलने के बाद इसमें बहुत अधिक समय जमा होगा कि इसे एक ही फट में अपडेट करना होगा - स्पष्ट रूप से वांछनीय नहीं।

इसे कैसे जोड़ेंगे:

XNA में घटनाओं की एक लंबी श्रृंखला है (यदि आप उपयोग करते हैं Game, तो यह कस्टम विंडो पर लागू नहीं होता है) जो मूल रूप से अंतर्निहित Form, खेल की घड़ी के लिए Suspendऔर अंतर्निहित Resumeतरीकों के आकार को जोड़ता है ।

ये निजी हैं, इसलिए आपको घटनाओं को अनहुक करने के लिए प्रतिबिंब का उपयोग करने की आवश्यकता होगी (जहां thisएक है Game):

this.host.Suspend = null;
this.host.Resume = null;

यहाँ आवश्यक भस्म है:

object host = typeof(Game).GetField("host", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(this);
host.GetType().BaseType.GetField("Suspend", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(host, null);
host.GetType().BaseType.GetField("Resume", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(host, null);

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

तब आपको अपना स्वयं का टिक स्रोत प्रदान करना होगा, जब XNA टिक नहीं कर रहा हो Application.Idle। मैंने बस एक प्रयोग किया System.Windows.Forms.Timer:

timer = new Timer();
timer.Interval = (int)(this.TargetElapsedTime.TotalMilliseconds);
timer.Tick += new EventHandler(timer_Tick);
timer.Start();

अब, timer_Tickअंततः कॉल करेगा Game.Tick। अब जब घड़ी निलंबित नहीं हुई है, तो हम XNA के अपने टाइमिंग कोड का उपयोग करने के लिए स्वतंत्र हैं। आसान! बिलकुल नहीं: हम केवल यह चाहते हैं कि जब XNA का बिल्ट-इन टिक नहीं होता है तब हम अपना मैनुअल टिक करें। यह करने के लिए कुछ सरल कोड है:

bool manualTick;
int manualTickCount = 0;
void timer_Tick(object sender, EventArgs e)
{
    if(manualTickCount > 2)
    {
        manualTick = true;
        this.Tick();
        manualTick = false;
    }

    manualTickCount++;
}

और फिर, Updateइस कोड को काउंटर को रीसेट करने के लिए डाल दें यदि XNA सामान्य रूप से टिक कर रहा है।

if(!manualTick)
    manualTickCount = 0;

ध्यान दें कि यह टिकिंग XNA की तुलना में काफी कम सटीक है। हालांकि, यह वास्तविक समय के साथ अधिक-से-कम पंक्तिबद्ध रहना चाहिए।


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

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

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


2
अच्छा व्यापक जवाब।
MichaelHouse

1
आपने वास्तव में प्रश्न क्यों पूछा, तो तुरंत इसका उत्तर दें?
एलेस्टेयर पिट्स

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