La Application.Run
chiamata guida il tuo pump dei messaggi di Windows, che è in definitiva ciò che alimenta tutti gli eventi che puoi agganciareForm
classe (e altri). Per creare un loop di gioco in questo ecosistema, si desidera ascoltare quando il pump dei messaggi dell'applicazione è vuoto e, mentre rimane vuoto, eseguire i passaggi tipici "processo input input, aggiornamento logica di gioco, rendering della scena" del prototipo di loop di gioco .
L' Application.Idle
evento viene generato una volta ogni volta che la coda dei messaggi dell'applicazione viene svuotata e l'applicazione passa a uno stato inattivo. Puoi agganciare l'evento nel costruttore del tuo modulo principale:
class MainForm : Form {
public MainForm () {
Application.Idle += HandleApplicationIdle;
}
void HandleApplicationIdle (object sender, EventArgs e) {
//TODO: Implement me.
}
}
Successivamente, devi essere in grado di determinare se l'applicazione è ancora inattiva. L' Idle
evento viene generato solo una volta, quando l'applicazione diventa inattiva. Non viene licenziato di nuovo fino a quando un messaggio non entra nella coda e quindi la coda si svuota di nuovo. Windows Forms non espone un metodo per eseguire una query sullo stato della coda dei messaggi, ma è possibile utilizzare i servizi di chiamata della piattaforma per delegare la query a una funzione Win32 nativa in grado di rispondere a tale domanda . La dichiarazione di importazione per PeekMessage
e i suoi tipi di supporto è simile a:
[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
sostanzialmente ti permette di guardare il prossimo messaggio in coda; ritorna vero se ne esiste uno, falso altrimenti. Ai fini di questo problema, nessuno dei parametri è particolarmente rilevante: è solo il valore di ritorno che conta. Ciò ti consente di scrivere una funzione che ti dice se l'applicazione è ancora inattiva (ovvero, non ci sono ancora messaggi nella coda):
bool IsApplicationIdle () {
NativeMessage result;
return PeekMessage(out result, IntPtr.Zero, (uint)0, (uint)0, (uint)0) == 0;
}
Ora hai tutto il necessario per scrivere il tuo ciclo di gioco completo:
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);
}
Inoltre, questo approccio si avvicina il più vicino possibile (con la minima dipendenza da P / Invoke) al canone di gioco nativo canonico , che assomiglia a:
while (!done) {
if (PeekMessage(&message, window, 0, 0, PM_REMOVE)){
TranslateMessage(&message);
DispatchMessage(&message);
}
else {
Update();
Render();
}
}