Этот 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, которая может ответить на этот вопрос . Декларация import для 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
в основном позволяет просматривать следующее сообщение в очереди; возвращает true, если он существует, в противном случае - false. Для целей этой проблемы ни один из параметров не имеет особого значения: имеет значение только возвращаемое значение. Это позволяет вам написать функцию, которая сообщает вам, если приложение все еще находится в режиме ожидания (то есть, в очереди еще нет сообщений):
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();
}
}