Der Application.Run
Aufruf steuert Ihre Windows-Nachrichtenpumpe. Dies ist letztendlich die Grundlage für alle Ereignisse, die Sie für die Form
Klasse (und für andere) festlegen können . Um eine Spieleschleife in diesem Ökosystem zu erstellen, möchten Sie abhören, wenn die Nachrichtenpumpe der Anwendung leer ist. Führen Sie die typischen Schritte der prototypischen Spieleschleife aus, während sie leer bleibt .
Das Application.Idle
Ereignis wird jedes Mal ausgelöst, wenn die Nachrichtenwarteschlange der Anwendung geleert wird und die Anwendung in einen Ruhezustand übergeht. Sie können das Ereignis in den Konstruktor Ihres Hauptformulars einbinden:
class MainForm : Form {
public MainForm () {
Application.Idle += HandleApplicationIdle;
}
void HandleApplicationIdle (object sender, EventArgs e) {
//TODO: Implement me.
}
}
Als Nächstes müssen Sie feststellen können, ob die Anwendung noch inaktiv ist. Das Idle
Ereignis löst nur einmal, wenn die Anwendung wird im Leerlauf. Es wird nicht erneut ausgelöst, bis eine Nachricht in der Warteschlange eingeht und die Warteschlange dann wieder geleert wird. Windows Forms stellt keine Methode zum Abfragen des Status der Nachrichtenwarteschlange zur Verfügung. Sie können jedoch Plattformaufrufdienste verwenden, um die Abfrage an eine systemeigene Win32-Funktion zu delegieren, die diese Frage beantworten kann . Die Einfuhranmeldung für PeekMessage
und ihre unterstützenden Typen sieht folgendermaßen aus:
[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
Im Prinzip können Sie die nächste Nachricht in der Warteschlange anzeigen. es gibt true zurück, falls vorhanden, andernfalls false. Für die Zwecke dieses Problems ist keiner der Parameter besonders relevant: Nur der Rückgabewert ist von Bedeutung. Auf diese Weise können Sie eine Funktion schreiben, die angibt, ob die Anwendung noch inaktiv ist (dh, die Warteschlange enthält noch keine Nachrichten):
bool IsApplicationIdle () {
NativeMessage result;
return PeekMessage(out result, IntPtr.Zero, (uint)0, (uint)0, (uint)0) == 0;
}
Jetzt hast du alles, was du brauchst, um deine komplette Spieleschleife zu schreiben:
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);
}
Darüber hinaus entspricht dieser Ansatz so genau wie möglich (mit minimaler Abhängigkeit von P / Invoke) der kanonischen systemeigenen Windows-Spieleschleife, die wie folgt aussieht:
while (!done) {
if (PeekMessage(&message, window, 0, 0, PM_REMOVE)){
TranslateMessage(&message);
DispatchMessage(&message);
}
else {
Update();
Render();
}
}
WM_TIMER
-basierte)Mit der Antwort von Josh einverstanden, möchte nur meine 5 Cent hinzufügen. Die WinForms-Standardnachrichtenschleife (Application.Run) könnte durch die folgende ersetzt werden (ohne p / invoke):
Auch wenn Sie Code in Message Pump einfügen möchten, verwenden Sie diesen Befehl:
quelle
Ich verstehe, dass dies ein alter Thread ist, möchte aber zwei Alternativen zu den oben vorgeschlagenen Techniken anbieten. Bevor ich darauf eingehe, hier einige Fallstricke mit den bisher gemachten Vorschlägen:
PeekMessage und die aufgerufenen Bibliotheksmethoden (SlimDX IsApplicationIdle) verursachen einen erheblichen Overhead.
Wenn Sie gepufferten RawInput verwenden möchten, müssen Sie die Nachrichtenpumpe mit PeekMessage in einem anderen Thread als dem UI-Thread abfragen, damit Sie sie nicht zweimal aufrufen müssen.
Application.DoEvents ist nicht dafür ausgelegt, in einer engen Schleife aufgerufen zu werden. GC-Probleme treten schnell auf.
Wenn Sie Application.Idle oder PeekMessage verwenden, wird Ihr Spiel oder Ihre Anwendung beim Verschieben oder Ändern der Fenstergröße ohne Codegerüche nicht ausgeführt, da Sie nur im Leerlauf arbeiten.
Um dies zu umgehen (mit Ausnahme von 2, wenn Sie die RawInput-Straße entlang fahren), haben Sie folgende Möglichkeiten:
Erstellen Sie einen Threading.Thread und führen Sie dort Ihre Spieleschleife aus.
Erstellen Sie eine Threading.Tasks.Task mit dem IsLongRunning-Flag und führen Sie sie dort aus. Microsoft empfiehlt, dass Aufgaben heutzutage anstelle von Threads verwendet werden, und es ist nicht schwer zu verstehen, warum.
Beide Techniken isolieren Ihre Grafik-API vom Benutzeroberflächenthread und der Nachrichtenpumpe, wie es der empfohlene Ansatz ist. Der Umgang mit der Zerstörung von Ressourcen / Zuständen und der Wiederherstellung während der Größenänderung von Fenstern wird ebenfalls vereinfacht und ist ästhetisch weitaus professioneller, wenn dies von außerhalb des UI-Threads erledigt wird.
quelle