WinForms和XNA
XNA框架默认情况下不能用于WindowsForms 。这是因为负责创建窗口并初始化设备的graphicsDeviceManager不允许我们指定要绘制的surface的句柄。我们可以选择自己初始化device,但这样就无法利用graphicsDeviceManager为我们提供的各种功能。在 http://creators.xna.com上你可以找到两个例子,讲解了如何使用WindowsForms和XNA。第一个是WindowsForms Series 1:Graphics Device,它解释了如何自己初始化device以及如何用它来画一个三角形:
这个例子的问题是,你没有ContentManager,所以你不能使用的contentloaders加载材质,模型,声音等,你必须自己创建三角形。
另一个例子:WinForms Serie 2:Content Loading,能动态加载有纹理的FBX文件:
如果看看源代码,我们会发现它比上一个例子大得多。这是因为要创建一些类来使用ContentBuilder。要做到这一点,必须从项目中获得所有的内容文件,并使用一个加载器编译这些文件,然后生成xnb文件。
我个人认为这些都不是最佳的解决方案。在第一个例子中你不能充分利用XNA框架。第二个例子的代码中使用ContentBuilder有点混乱。经过研究,我想到了一个解决方案可以工作正常。其主要特点是实现简单而且你也可以使用默认的ContentManager。
我使它看起来像一个小编辑器,有一个菜单栏,一个属性栏在左边,一个panel在右边。看起来如图所示下:
接着将XNA绘制在panel上。进入form窗体代码并添加以下代码:
public Control Panel {
get {
return splitContainer.Panel;
}
}
现在,我们只需要知道有一种方式能改变surface处理,而XNA正是绘制在这个surface上的:
GraphicsDeviceInformation.PresentationParameters.DeviceWindowHandle
当Graphics在为创建device准备参数时我们将这个属性包含在内。我们将对program.cs文件和game.cs文件添加一些代码:
static class Program
{
static Game1 game;
[STAThread]
static void Main(string[] args)
{
Main form = new Main();
form.Disposed += new EventHandler(form_Disposed);
using (game = new Game1(form))
{
form.Show();
form.TopMost = true;
game.Run();
}
}
static void form_Disposed(object sender, EventArgs e)
{
game.Exit();
}
}
如你所见,我们建立了一个Game1静态变量,并在Main方法中初始化form。因为我们将让Game通过Game.Run()方法处理程序线程,退出游戏时我们需要告知Game也退出,为此我们注册了一个Disposed事件。下一步,我们创建了一个Game1类的实例,它将form作为参数。我们调用form的Show方法,激活TopMost选择。最后,我们调用game.Run()。
你可能会问为什么我们激活了TopMost选项。原因是graphicsDeviceManager默认情况下将创建一个窗口,但这不是我们想要的哪一个,这样会导致有两个窗口在屏幕上。要隐藏这个窗口,我们要告知form覆盖其他窗口,因此,默认的窗口将隐藏在form后面。届时,我们将已经改变了XNA的处理,所以它会绘制在form的panel控件上。由于没有办法告诉graphicsDeviceManager不要创建默认窗口,我们将使它在获得焦点时不可见,使用Visible属性并将form的TopMost属性恢复到false。
如果你不这样做,你将看到XNA创建的窗口弹出并很快消失。这就是为什么我说这个解决方案并非十全十美,虽然这是唯一的缺点。
现在,我们在Game中编写代码。首先,我们需要与form同一类型的atribute并在构造函数中初始化:
Main form; public Game1(Main form)
{
this.form = form;
}
下一步,我们必须:
1.改变XNA将要绘制的句柄。我们在Game的构造函数中注册准备device的参数的事件:
graphics.PreparingDeviceSettings += new EventHandler
现在,添加方法改变device绘制的句柄:
void graphics_PreparingDeviceSettings(object sender, PreparingDeviceSettingsEventArgs e)
{
e.GraphicsDeviceInformation.PresentationParameters.DeviceWindowHandle = form.Panel.Handle;
}
2.当默认窗口获得焦点时,我们将使它不可见并改变form的TopMost属性。
我们在构造函数中写下代码获取XNA默认创建的窗口:
Form xnaWindow = (Form)Control.FromHandle((this.Window.Handle));
下一步,注册GotFocus方法。这种方法将使窗口不可见并改变TopMost属性:
xnaWindow.GotFocus += new EventHandler(xnaWindow_GotFocus);
void xnaWindow_GotFocus(object sender, EventArgs e)
{
((Form)sender).Visible = false;
form.TopMost = false;
}
3.为了保持比例还要在当panel的大小改变时调整Device和相机的aspectRatio。
我们将在构造函数中注册panel的Resize方法,以使我们能够改变设备属性和相机的aspectRatio:
form.Panel.Resize += new EventHandler(Panel_Resize);
void Panel_Resize(object sender, EventArgs e)
{
graphics.PreferredBackBufferWidth = form.Panel.Width;
graphics.PreferredBackBufferHeight = form.Panel.Height;
aspectRatio = (float)form.Panel.Width / form.Panel.Height;
graphics.ApplyChanges();
}
这样就大功告成了!现在,我们就可以使用XNA和ContentManager创建与WindowsForms的混合应用程序了。
其实要实现上述效果,还有一个办法就是自己实现用户界面,用XNA把菜单、按钮等控件自己画出来,速度一定比Winform快,简单研究了一下,用起来总是没有winform这么顺手,有兴趣的话可以试着做一下。http://www.gameprojects.com/project/?id=e68f3464c4上有源代码下载,叫做xWinForms,现已更新到1.3.1版本。
发布时间:2008/12/23 上午7:27:23 阅读次数:9671