Bagaimana cara mencegah XNA menjeda Pembaruan saat jendela diubah ukurannya / dipindahkan?

15

XNA berhenti memanggil UpdatedanDraw saat jendela permainan diubah ukurannya atau dipindahkan.

Mengapa? Apakah ada cara untuk mencegah perilaku ini?

(Ini menyebabkan kode jaringan saya tidak sinkron, karena pesan jaringan tidak dipompa.)

Andrew Russell
sumber

Jawaban:

20

Iya. Ini melibatkan sejumlah kecil kekacauan dengan internal XNA. Saya mendapat demonstrasi perbaikan di paruh kedua video ini .

Latar Belakang:

Alasan ini terjadi adalah karena XNA menangguhkan jam permainannya ketika Gamemendasarinya Formdiubah ukurannya (atau dipindahkan, acara-acaranya sama). Ini, pada gilirannya, adalah karena ia mendorong putaran permainannya Application.Idle. Ketika jendela diubah ukurannya, Windows melakukan beberapa hal loop pesan win32 gila yang mencegah Idledari menembak.

Jadi, jika Gametidak menangguhkan jamnya, setelah mengubah ukuran, itu akan mengakumulasi banyak waktu yang kemudian harus diperbarui dalam satu ledakan - jelas tidak diinginkan.

Bagaimana memperbaikinya:

Ada rantai panjang peristiwa di XNA (jika Anda menggunakan Game, ini tidak berlaku untuk jendela kustom) yang pada dasarnya menghubungkan peristiwa ukuran peristiwa yang mendasarinya Form, ke SuspenddanResume metode untuk jam permainan.

Ini bersifat pribadi, jadi Anda harus menggunakan refleksi untuk melepaskan peristiwa (di mana thisa Game):

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

Inilah mantera yang diperlukan:

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);

(Anda dapat memutus rantai di tempat lain, Anda dapat menggunakan ILSpy untuk mengetahuinya. Tetapi tidak ada lagi selain ukuran jendela yang menangguhkan pengatur waktu - sehingga acara ini sebagus apapun.)

Maka Anda perlu menyediakan sumber centang sendiri, untuk saat XNA tidak berdetak Application.Idle. Saya hanya menggunakan System.Windows.Forms.Timer:

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

Sekarang, timer_Tickpada akhirnya akan menelepon Game.Tick. Sekarang jam tidak ditangguhkan, kami bebas untuk terus menggunakan kode waktu XNA sendiri. Mudah! Tidak cukup: kami hanya ingin kutu manual kami terjadi ketika kutu bawaan XNA tidak terjadi. Berikut ini beberapa kode sederhana untuk melakukannya:

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

    manualTickCount++;
}

Dan kemudian, Updatemasukkan kode ini untuk mengatur ulang penghitung jika XNA berdetak secara normal.

if(!manualTick)
    manualTickCount = 0;

Perhatikan bahwa detakan ini sangat kurang akurat dengan XNA. Namun itu harus tetap lebih atau kurang berbaris dengan waktu nyata.


Masih ada satu kekurangan kecil dalam kode ini. Win32, memberkati wajah jeleknya yang bodoh, pada dasarnya menghentikan semua pesan selama beberapa ratus milidetik ketika Anda mengklik judul-judul jendela, sebelum mouse benar-benar bergerak (lihat tautan yang sama lagi ). Ini mencegah kitaTimer (yang berbasis pesan) dari berdetik.

Karena kita sekarang tidak menangguhkan jam permainan XNA, ketika penghitung waktu kita akhirnya mulai berdetak lagi, Anda akan mendapatkan ledakan pembaruan. Dan, sayangnya, XNA membatasi jumlah waktu terakumulasi ke jumlah yang sedikit lebih rendah dari lamanya waktu Windows menghentikan pesan ketika Anda mengklik bilah judul. Jadi, jika seorang pengguna mengklik dan menahan bilah judul Anda, tanpa memindahkannya, Anda akan mendapatkan ledakan pembaruan dan jatuh beberapa frame secara real time.

(Tapi ini seharusnya masih cukup baik untuk sebagian besar kasus. Timer XNA sudah mengorbankan akurasi untuk mencegah kegagapan, jadi jika Anda membutuhkan pengaturan waktu yang sempurna , Anda harus melakukan hal lain.)

Andrew Russell
sumber
2
Jawaban komprehensif yang bagus.
MichaelHouse
1
Mengapa tepatnya Anda mengajukan pertanyaan, lalu langsung menjawabnya?
Alastair Pitts
4
Pertanyaan yang adil. Secara eksplisit didorong . UI pertanyaan bahkan memiliki kotak "Jawab pertanyaan Anda sendiri". Awalnya saya akan membuatnya menjadi jawaban untuk pertanyaan ini , tetapi itu hanya akan menjadi edit dari jawaban lama, dan solusi saya tidak menyelesaikan sebagian dari pertanyaan itu. Dengan cara ini mendapat visibilitas yang lebih baik (menjadi baru dan menjadi XNA di GDSE daripada SO). Dan info ini lebih cocok untuk di sini, daripada blog saya.
Andrew Russell