The Application.Run
panggilan drive pompa pesan Windows Anda, yang pada akhirnya kekuatan apa semua peristiwa Anda dapat menghubungkan pada Form
kelas (dan lain-lain). Untuk membuat loop game di ekosistem ini, Anda ingin mendengarkan ketika pompa pesan aplikasi kosong, dan sementara itu tetap kosong, lakukan langkah-langkah "proses input negara, perbarui logika game, render adegan" membuat langkah-langkah loop game prototypical .
The Application.Idle
peristiwa kebakaran sekali setiap kali antrian pesan aplikasi dikosongkan dan aplikasi transisi ke keadaan idle. Anda dapat mengaitkan acara di konstruktor formulir utama Anda:
class MainForm : Form {
public MainForm () {
Application.Idle += HandleApplicationIdle;
}
void HandleApplicationIdle (object sender, EventArgs e) {
//TODO: Implement me.
}
}
Selanjutnya, Anda perlu untuk dapat menentukan apakah aplikasi tersebut masih menganggur. The Idle
event hanya kebakaran sekali, saat aplikasi menjadi siaga. Itu tidak dipecat lagi sampai pesan masuk ke antrian dan kemudian antrian kosong lagi. Formulir Windows tidak memaparkan metode untuk menanyakan keadaan antrian pesan, tetapi Anda dapat menggunakan layanan permintaan platform untuk mendelegasikan kueri ke fungsi Win32 asli yang dapat menjawab pertanyaan itu . Deklarasi impor untuk PeekMessage
dan jenis pendukungnya seperti:
[StructLayout(LayoutKind.Sequential)]
public struct NativeMessage
{
public IntPtr Handle;
public uint Message;
public IntPtr WParameter;
public IntPtr LParameter;
public uint Time;
public Point Location;
}
[DllImport("user32.dll")]
public static extern int PeekMessage(out NativeMessage message, IntPtr window, uint filterMin, uint filterMax, uint remove);
PeekMessage
pada dasarnya memungkinkan Anda untuk melihat pesan berikutnya dalam antrian; itu mengembalikan true jika ada, false sebaliknya. Untuk keperluan masalah ini, tidak ada parameter yang relevan: hanya nilai pengembalian yang penting. Ini memungkinkan Anda untuk menulis fungsi yang memberi tahu Anda jika aplikasi tersebut masih menganggur (artinya, masih belum ada pesan dalam antrian):
bool IsApplicationIdle () {
NativeMessage result;
return PeekMessage(out result, IntPtr.Zero, (uint)0, (uint)0, (uint)0) == 0;
}
Sekarang Anda memiliki semua yang Anda butuhkan untuk menulis lingkaran permainan lengkap Anda:
class MainForm : Form {
public MainForm () {
Application.Idle += HandleApplicationIdle;
}
void HandleApplicationIdle (object sender, EventArgs e) {
while(IsApplicationIdle()) {
Update();
Render();
}
}
void Update () {
// ...
}
void Render () {
// ...
}
[StructLayout(LayoutKind.Sequential)]
public struct NativeMessage
{
public IntPtr Handle;
public uint Message;
public IntPtr WParameter;
public IntPtr LParameter;
public uint Time;
public Point Location;
}
[DllImport("user32.dll")]
public static extern int PeekMessage(out NativeMessage message, IntPtr window, uint filterMin, uint filterMax, uint remove);
}
Lebih lanjut, pendekatan ini cocok sedekat mungkin (dengan ketergantungan minimal pada P / Invoke) ke loop game Windows asli kanonik , yang terlihat seperti:
while (!done) {
if (PeekMessage(&message, window, 0, 0, PM_REMOVE)){
TranslateMessage(&message);
DispatchMessage(&message);
}
else {
Update();
Render();
}
}
WM_TIMER
berbasis)Setuju dengan jawaban dari Josh, hanya ingin menambahkan 5 sen saya. WinForms default message loop (Application.Run) dapat diganti dengan yang berikut (tanpa p / meminta):
Juga jika Anda ingin menyuntikkan beberapa kode ke pompa pesan, maka gunakan ini:
sumber
Saya mengerti bahwa ini adalah utas lama, tetapi saya ingin memberikan dua alternatif untuk teknik yang disarankan di atas. Sebelum saya membahasnya, berikut adalah beberapa jebakan dengan proposal yang dibuat sejauh ini:
PeekMessage membawa overhead yang cukup besar, seperti halnya metode perpustakaan yang menyebutnya (SlimDX IsApplicationIdle).
Jika Anda ingin menggunakan RawInput yang disangga, Anda harus melakukan polling pompa pesan dengan PeekMessage di utas lain selain utas UI, jadi Anda tidak ingin menyebutnya dua kali.
Application.DoEvents tidak dirancang untuk dipanggil dalam loop ketat, masalah GC akan segera muncul.
Saat menggunakan Application.Idle atau PeekMessage, karena Anda hanya melakukan pekerjaan saat Idle, game atau aplikasi Anda tidak akan berjalan ketika memindahkan atau mengubah ukuran jendela Anda, tanpa bau kode.
Untuk mengatasinya (kecuali 2 jika Anda menggunakan RawInput), Anda dapat:
Buat Threading. Baca dan jalankan loop game Anda di sana.
Buat Threading.Tasks.Task dengan bendera IsLongRunning, dan jalankan di sana. Microsoft merekomendasikan bahwa Tugas digunakan sebagai pengganti Threads hari ini dan tidak sulit untuk melihat mengapa.
Kedua teknik ini mengisolasi API grafik Anda dari utas UI dan pompa pesan, seperti pendekatan yang disarankan. Penanganan kerusakan sumber daya / negara dan rekreasi selama pengubahan ukuran Window juga disederhanakan dan secara estetika jauh lebih profesional ketika dilakukan dari selesai (exericising karena hati-hati untuk menghindari kebuntuan dengan pompa pesan) dari luar utas UI.
sumber