该Application.Run
调用将驱动Windows消息泵,最终将驱动您可以挂接到Form
类(和其他事件)上的所有事件。要在此生态系统中创建游戏循环,您需要侦听应用程序的消息泵何时为空,而当消息泵为空时,执行原型游戏循环的典型“处理输入状态,更新游戏逻辑,渲染场景”步骤。 。
Application.Idle
每次清空应用程序的消息队列并且应用程序转换为空闲状态时,都会触发该事件。您可以将事件挂接到主窗体的构造函数中:
class MainForm : Form {
public MainForm () {
Application.Idle += HandleApplicationIdle;
}
void HandleApplicationIdle (object sender, EventArgs e) {
//TODO: Implement me.
}
}
接下来,您需要确定应用程序是否仍处于空闲状态。Idle
当应用程序变得空闲时,该事件仅触发一次。直到有消息进入队列然后再次清空该队列时,它才会再次被触发。Windows Forms没有公开查询消息队列状态的方法,但是您可以使用平台调用服务将查询委托给可以回答该问题的本机Win32函数。的导入声明PeekMessage
及其支持类型如下:
[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
基本上可以让您查看队列中的下一条消息;如果存在则返回true,否则返回false。对于此问题,没有一个参数特别重要:只是返回值很重要。这使您可以编写一个函数来告诉您应用程序是否仍处于空闲状态(即,队列中仍然没有消息):
bool IsApplicationIdle () {
NativeMessage result;
return PeekMessage(out result, IntPtr.Zero, (uint)0, (uint)0, (uint)0) == 0;
}
现在,您拥有编写完整的游戏循环所需的一切:
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);
}
此外,此方法与规范的本机 Windows游戏循环尽可能匹配(尽可能减少对P / Invoke的依赖),如下所示:
while (!done) {
if (PeekMessage(&message, window, 0, 0, PM_REMOVE)){
TranslateMessage(&message);
DispatchMessage(&message);
}
else {
Update();
Render();
}
}