Application.Run
Wezwanie napędza pompę wiadomość Windows, który jest ostatecznie jakie moce wszystkie zdarzenia można zahaczyć o Form
klasie (i innych). Aby utworzyć pętlę gry w tym ekosystemie, chcesz nasłuchiwać, kiedy pompa komunikatów aplikacji jest pusta, a gdy pozostaje pusta, wykonaj typowy „stan wejściowy procesu, zaktualizuj logikę gry, renderuj scenę” etapów prototypowej pętli gry .
W Application.Idle
Zdarzenie raz za każdym razem kolejka przesłanie aplikacji jest opróżniany, a aplikacja jest przejście do stanu czuwania. Możesz zaczepić zdarzenie w konstruktorze swojej głównej formy:
class MainForm : Form {
public MainForm () {
Application.Idle += HandleApplicationIdle;
}
void HandleApplicationIdle (object sender, EventArgs e) {
//TODO: Implement me.
}
}
Następnie musisz ustalić, czy aplikacja jest nadal bezczynna. Idle
Zdarzenie wyzwala tylko raz, gdy aplikacja staje się bezczynny. Nie zostanie ponownie zwolniony, dopóki wiadomość nie dostanie się do kolejki, a następnie kolejka opróżni się ponownie. Windows Forms nie ujawnia metody zapytania o stan kolejki komunikatów, ale można użyć usług wywoływania platformy do delegowania zapytania do natywnej funkcji Win32, która może odpowiedzieć na to pytanie . Deklaracja importowa PeekMessage
i jej typy pomocnicze wyglądają następująco:
[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
w zasadzie pozwala spojrzeć na następną wiadomość w kolejce; zwraca true, jeśli istnieje, false w przeciwnym razie. Na potrzeby tego problemu żaden z parametrów nie jest szczególnie istotny: liczy się tylko wartość zwracana. Pozwala to napisać funkcję, która informuje, czy aplikacja jest nadal bezczynna (to znaczy, że nadal nie ma żadnych komunikatów w kolejce):
bool IsApplicationIdle () {
NativeMessage result;
return PeekMessage(out result, IntPtr.Zero, (uint)0, (uint)0, (uint)0) == 0;
}
Teraz masz wszystko, czego potrzebujesz, aby napisać całą pętlę gry:
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);
}
Co więcej, to podejście pasuje jak najbliżej (przy minimalnym uzależnieniu od P / Invoke) do kanonicznej natywnej pętli gier Windows, która wygląda następująco:
while (!done) {
if (PeekMessage(&message, window, 0, 0, PM_REMOVE)){
TranslateMessage(&message);
DispatchMessage(&message);
}
else {
Update();
Render();
}
}