A Application.Run
chamada aciona sua bomba de mensagens do Windows, que é o que alimenta todos os eventos que você pode conectar à Form
classe (e outros). Para criar um loop de jogo nesse ecossistema, você deseja escutar quando a bomba de mensagens do aplicativo estiver vazia e, enquanto estiver vazia, execute as etapas típicas do "estado de entrada do processo, atualize a lógica do jogo, renderize a cena" do loop de protótipo do jogo .
O Application.Idle
evento é acionado uma vez toda vez que a fila de mensagens do aplicativo é esvaziada e o aplicativo está em transição para um estado inativo. Você pode conectar o evento no construtor do seu formulário principal:
class MainForm : Form {
public MainForm () {
Application.Idle += HandleApplicationIdle;
}
void HandleApplicationIdle (object sender, EventArgs e) {
//TODO: Implement me.
}
}
Em seguida, você precisa determinar se o aplicativo ainda está ocioso. O Idle
evento é acionado apenas uma vez, quando o aplicativo fica inativo. Ele não é acionado novamente até que uma mensagem entre na fila e, em seguida, a fila esvazia novamente. O Windows Forms não expõe um método para consultar o estado da fila de mensagens, mas você pode usar os serviços de chamada de plataforma para delegar a consulta a uma função nativa do Win32 que possa responder a essa pergunta . A declaração de importação PeekMessage
e seus tipos de suporte são semelhantes 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
basicamente permite que você veja a próxima mensagem na fila; retorna true se houver, false caso contrário. Para os propósitos desse problema, nenhum dos parâmetros é particularmente relevante: é apenas o valor de retorno que importa. Isso permite que você escreva uma função que informa se o aplicativo ainda está ocioso (ou seja, ainda não há mensagens na fila):
bool IsApplicationIdle () {
NativeMessage result;
return PeekMessage(out result, IntPtr.Zero, (uint)0, (uint)0, (uint)0) == 0;
}
Agora você tem tudo o que precisa para escrever seu ciclo completo do jogo:
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);
}
Além disso, essa abordagem se aproxima o mais próximo possível (com dependência mínima de P / Invoke) do loop de jogo nativo canônico do Windows, que se parece com:
while (!done) {
if (PeekMessage(&message, window, 0, 0, PM_REMOVE)){
TranslateMessage(&message);
DispatchMessage(&message);
}
else {
Update();
Render();
}
}