Application.Run
Виклик жене своє повідомлення насоса Windows, який, в кінцевому рахунку , які сили всіх подій , які ви можете підключити на Form
класі (і інших). Щоб створити ігровий цикл у цій екосистемі, ви хочете слухати, коли насос повідомлення додатка порожній, і поки він залишається порожнім, виконайте типові "вхідні процеси процесу, оновіть логіку гри, візьміть сцену" кроки прототипічного циклу ігор .
У Application.Idle
подію спрацьовує один раз кожен раз , коли черга повідомлень додатка буде очищена , і додаток переходить в початковий стан. Ви можете підключити подію до конструктора основної форми:
class MainForm : Form {
public MainForm () {
Application.Idle += HandleApplicationIdle;
}
void HandleApplicationIdle (object sender, EventArgs e) {
//TODO: Implement me.
}
}
Далі потрібно мати змогу визначити, чи програма ще не працює. Idle
Подія спрацьовує тільки один раз, коли додаток стає простоює. Він знову не звільняється, поки повідомлення не потрапить у чергу, а потім черга знову не зникне. Windows Forms не розкриває спосіб запиту про стан черги повідомлень, але ви можете використовувати служби виклику платформи для делегування запиту в нативної функції Win32, яка може відповісти на це питання . Декларація про імпорт PeekMessage
та його допоміжні типи виглядає так:
[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
в основному дозволяє переглядати наступне повідомлення у черзі; вона повертає істину, якщо така існує, а інша помилкова. Для цілей цієї проблеми жоден з параметрів не є особливо актуальним: значення має лише повернене значення. Це дозволяє записати функцію, яка повідомляє, чи програма ще не працює (тобто досі в черзі немає повідомлень):
bool IsApplicationIdle () {
NativeMessage result;
return PeekMessage(out result, IntPtr.Zero, (uint)0, (uint)0, (uint)0) == 0;
}
Тепер у вас є все необхідне, щоб написати повний цикл гри:
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);
}
Крім того, цей підхід максимально відповідає (з мінімальною залежністю від P / Invoke) до канонічного нативного циклу ігор Windows, який виглядає так:
while (!done) {
if (PeekMessage(&message, window, 0, 0, PM_REMOVE)){
TranslateMessage(&message);
DispatchMessage(&message);
}
else {
Update();
Render();
}
}