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();
}
}
WM_TIMER
oparte na nich)Zgadzam się z odpowiedzią Josha, chcę tylko dodać moje 5 centów. Domyślna pętla komunikatów WinForm (Application.Run) może zostać zastąpiona następującą (bez p / invoke):
Również jeśli chcesz wstrzyknąć kod do pompy komunikatów, użyj tego:
źródło
Rozumiem, że to stary wątek, ale chciałbym przedstawić dwie alternatywy dla technik sugerowanych powyżej. Zanim przejdę do nich, oto niektóre z pułapek związanych z dotychczasowymi propozycjami:
PeekMessage niesie ze sobą znaczne koszty, podobnie jak metody biblioteczne, które go nazywają (SlimDX IsApplicationIdle).
Jeśli chcesz zastosować buforowane RawInput, musisz sondować pompę komunikatów za pomocą PeekMessage w innym wątku innym niż wątek interfejsu użytkownika, więc nie chcesz wywoływać tego dwukrotnie.
Application.DoEvents nie jest zaprojektowany do wywoływania w ciasnej pętli, problemy GC szybko się pojawią.
Podczas korzystania z Application.Idle lub PeekMessage, ponieważ wykonujesz pracę tylko w stanie Bezczynności, twoja gra lub aplikacja nie będzie działać podczas przenoszenia lub zmiany rozmiaru okna, bez zapachu kodu.
Aby obejść te problemy (z wyjątkiem 2, jeśli idziesz drogą RawInput), możesz:
Stwórz wątek. Wątek i uruchom tam swoją pętlę gry.
Utwórz Threading.Tasks.Task z flagą IsLongRunning i uruchom ją tam. Microsoft zaleca używanie Zadań zamiast Wątków i nietrudno zrozumieć, dlaczego.
Obie te techniki izolują interfejs graficzny API od wątku interfejsu użytkownika i pompy komunikatów, co jest zalecanym podejściem. Obsługa niszczenia zasobów / stanu i odtwarzania podczas zmiany rozmiaru okna jest również uproszczona i jest znacznie bardziej profesjonalna, gdy jest wykonywana (wykonując należytą ostrożność, aby uniknąć zakleszczeń przy pompie komunikatów) spoza wątku interfejsu użytkownika.
źródło