हाँ। इसमें 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 के टाइमर ने हकलाना रोकने के लिए पहले से ही सटीकता का बलिदान किया है, इसलिए यदि आपको सही समय की आवश्यकता है, तो आपको कुछ और करना चाहिए।)