L’ Application.Run
appel entraîne votre pompe de messages Windows, qui alimente en définitive tous les événements que vous pouvez raccrocher auForm
classe (et à d'autres). Pour créer une boucle de jeu dans cet écosystème, vous souhaitez écouter lorsque la pompe à messages de l'application est vide et pendant qu'il reste vide, effectuez les étapes typiques "État d'entrée du processus, mise à jour de la logique de jeu, rendu de la scène" de la boucle de jeu prototypique. .
L' Application.Idle
événement est déclenché une fois à chaque fois que la file de messages de l'application est vidée et que l'application passe à un état inactif. Vous pouvez accrocher l'événement au constructeur de votre formulaire principal:
class MainForm : Form {
public MainForm () {
Application.Idle += HandleApplicationIdle;
}
void HandleApplicationIdle (object sender, EventArgs e) {
//TODO: Implement me.
}
}
Ensuite, vous devez être capable de déterminer si l'application est toujours inactive. L' Idle
événement ne se déclenche qu'une seule fois, lorsque l'application devient inactive. Il ne se déclenche plus jusqu'à ce qu'un message arrive dans la file d'attente, puis la file se vide à nouveau. Windows Forms n'expose pas de méthode pour interroger l'état de la file d'attente de messages, mais vous pouvez utiliser les services d'appel de plate-forme pour déléguer la requête à une fonction Win32 native qui peut répondre à cette question . La déclaration d'importation de PeekMessage
et ses types de support se présentent comme suit:
[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
vous permet essentiellement de regarder le message suivant dans la file d'attente; il retourne vrai s'il en existe un, faux sinon. Pour les besoins de ce problème, aucun paramètre n'est particulièrement pertinent: seule la valeur de retour compte. Cela vous permet d'écrire une fonction qui vous indique si l'application est toujours inactive (c'est-à-dire qu'il n'y a toujours aucun message dans la file d'attente):
bool IsApplicationIdle () {
NativeMessage result;
return PeekMessage(out result, IntPtr.Zero, (uint)0, (uint)0, (uint)0) == 0;
}
Vous avez maintenant tout ce dont vous avez besoin pour écrire votre boucle de jeu complète:
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);
}
De plus, cette approche correspond le plus possible (avec une dépendance minimale à P / Invoke) à la boucle de jeu canonique native de Windows, qui ressemble à ceci :
while (!done) {
if (PeekMessage(&message, window, 0, 0, PM_REMOVE)){
TranslateMessage(&message);
DispatchMessage(&message);
}
else {
Update();
Render();
}
}
WM_TIMER
ceux basés sur)D'accord avec la réponse de Josh, je veux juste ajouter mes 5 centimes. La boucle de message par défaut de WinForms (Application.Run) peut être remplacée par la suivante (sans p / invoke):
Aussi, si vous souhaitez injecter du code dans Message Pump, utilisez ceci:
la source
Je comprends qu’il s’agit d’un vieux fil conducteur, mais j’aimerais proposer deux alternatives aux techniques suggérées ci-dessus. Avant d’y revenir, voici quelques-uns des pièges avec les propositions faites jusqu’à présent:
PeekMessage entraîne une surcharge considérable, tout comme les méthodes de bibliothèque qui l'appellent (SlimDX IsApplicationIdle).
Si vous souhaitez utiliser RawInput en mémoire tampon, vous devez interroger la pompe de messages avec PeekMessage sur un autre thread autre que le thread d'interface utilisateur. Vous ne souhaitez donc pas l'appeler deux fois.
Application.DoEvents n'est pas conçu pour être appelé dans une boucle serrée, des problèmes de GC vont rapidement apparaître.
Lorsque vous utilisez Application.Idle ou PeekMessage, étant donné que vous travaillez uniquement lorsque vous êtes inactif, votre jeu ou votre application ne s'exécutera pas lors du déplacement ou du redimensionnement de votre fenêtre, sans odeur de code.
Pour contourner ces problèmes (sauf 2 si vous suivez la route RawInput), vous pouvez soit:
Créez un Threading.Thread et lancez votre boucle de jeu là-bas.
Créez une tâche Threading.Tasks.Task avec l'indicateur IsLongRunning et exécutez-la ici. Microsoft recommande que les tâches soient utilisées à la place de Threads ces temps-ci et il n’est pas difficile de comprendre pourquoi.
Ces deux techniques isolent votre API graphique du thread d'interface utilisateur et de la pompe de message, comme le recommande l'approche. Le traitement de la destruction des ressources / de l’état et des activités récréatives lors du redimensionnement de la fenêtre est également simplifié et est beaucoup plus professionnel (c’est-à-dire qu’il est très professionnel de le faire (en exerçant la prudence nécessaire pour éviter les blocages avec la pompe de message) depuis l’extérieur du fil de l’interface utilisateur.
la source