XNA berhenti memanggil Update
danDraw
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.)
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 Game
mendasarinya Form
diubah 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 Idle
dari menembak.
Jadi, jika Game
tidak 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 Suspend
danResume
metode untuk jam permainan.
Ini bersifat pribadi, jadi Anda harus menggunakan refleksi untuk melepaskan peristiwa (di mana this
a 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_Tick
pada 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, Update
masukkan 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.)