ฉันจะป้องกัน XNA ไม่ให้หยุดการอัปเดตชั่วคราวในขณะที่กำลังปรับขนาด / ย้ายหน้าต่างได้อย่างไร


15

XNA หยุดการโทรUpdateและDrawในขณะที่หน้าต่างเกมกำลังถูกปรับขนาดหรือย้าย

ทำไม? มีวิธีป้องกันพฤติกรรมนี้หรือไม่?

(มันทำให้รหัสเครือข่ายของฉันยกเลิกการซิงโครไนซ์เนื่องจากข้อความเครือข่ายไม่ถูกปั๊ม)

คำตอบ:


20

ใช่. มันเกี่ยวข้องกับการสับสนเล็กน้อยกับ internals ของ XNA ฉันได้สาธิตการแก้ไขในช่วงครึ่งหลังของวิดีโอนี้

พื้นหลัง:

เหตุผลนี้เกิดขึ้นเนื่องจาก XNA ระงับนาฬิกาเกมเมื่อมีการปรับขนาดGameต้นแบบForm(หรือย้ายเหตุการณ์เหมือนกัน) ในทางกลับกันนี่เป็นเพราะมันผลักดันเกมวนมาจากApplication.Idleมัน เมื่อมีการปรับขนาดหน้าต่าง Windows จะทำบางสิ่งที่วนลูปข้อความ 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 จำกัด ระยะเวลาในการสะสมให้เหลือน้อยกว่าระยะเวลาเล็กน้อย Windows จะหยุดข้อความเมื่อคุณคลิกที่แถบชื่อเรื่อง ดังนั้นหากผู้ใช้คลิกที่แถบชื่อเรื่องค้างไว้โดยไม่ย้ายคุณจะได้รับการอัปเดตและทำให้เฟรมไม่สั้นตามเวลาจริง

(แต่นี่ก็ยังดีพอสำหรับกรณีส่วนใหญ่ตัวจับเวลาของ XNA ได้เสียสละความถูกต้องเพื่อป้องกันการพูดติดอ่างดังนั้นถ้าคุณต้องการเวลาที่สมบูรณ์แบบคุณควรทำอย่างอื่น)


2
คำตอบที่ครอบคลุมดี
MichaelHouse

1
ทำไมคุณถึงถามคำถามแล้วตอบทันที?
Alastair ลง

4
คำถามที่เป็นธรรม มันได้รับการสนับสนุนอย่างชัดเจน UI คำถามยังมีช่อง "ตอบคำถามของคุณเอง" เดิมทีฉันจะตอบคำถามนี้แต่นั่นจะเป็นการแก้ไขคำตอบเก่า ๆ และโซลูชันของฉันไม่สามารถแก้ส่วนหนึ่งของคำถามนั้นได้ วิธีนี้ทำให้มองเห็นได้ดีขึ้น (เป็นสิ่งใหม่และเป็น XNA ใน GDSE แทนที่จะเป็น SO) และข้อมูลนี้เหมาะสำหรับที่นี่มากกว่าบล็อกของฉัน
แอนดรูรัสเซลล์
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.