WinForms和XNA

XNA框架默认情况下不能用于WindowsForms 。这是因为负责创建窗口并初始化设备的graphicsDeviceManager不允许我们指定要绘制的surface的句柄。我们可以选择自己初始化device,但这样就无法利用graphicsDeviceManager为我们提供的各种功能。在 http://creators.xna.com上你可以找到两个例子,讲解了如何使用WindowsForms和XNA。第一个是WindowsForms Series 1:Graphics Device,它解释了如何自己初始化device以及如何用它来画一个三角形:

XNA_WinForm1

这个例子的问题是,你没有ContentManager,所以你不能使用的contentloaders加载材质,模型,声音等,你必须自己创建三角形。

另一个例子:WinForms Serie 2:Content Loading,能动态加载有纹理的FBX文件:

XNA_WinForm2

如果看看源代码,我们会发现它比上一个例子大得多。这是因为要创建一些类来使用ContentBuilder。要做到这一点,必须从项目中获得所有的内容文件,并使用一个加载器编译这些文件,然后生成xnb文件。

我个人认为这些都不是最佳的解决方案。在第一个例子中你不能充分利用XNA框架。第二个例子的代码中使用ContentBuilder有点混乱。经过研究,我想到了一个解决方案可以工作正常。其主要特点是实现简单而且你也可以使用默认的ContentManager。

我使它看起来像一个小编辑器,有一个菜单栏,一个属性栏在左边,一个panel在右边。看起来如图所示下:

Editor1

接着将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(graphics_PreparingDeviceSettings);

现在,添加方法改变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();

}

Editor2

这样就大功告成了!现在,我们就可以使用XNA和ContentManager创建与WindowsForms的混合应用程序了。

其实要实现上述效果,还有一个办法就是自己实现用户界面,用XNA把菜单、按钮等控件自己画出来,速度一定比Winform快,简单研究了一下,用起来总是没有winform这么顺手,有兴趣的话可以试着做一下。http://www.gameprojects.com/project/?id=e68f3464c4上有源代码下载,叫做xWinForms,现已更新到1.3.1版本。

xWinForms


发布时间:2008/12/23 上午7:27:23  阅读次数:9159

2006 - 2024,推荐分辨率 1024*768 以上,推荐浏览器 Chrome、Edge 等现代浏览器,截止 2021 年 12 月 5 日的访问次数:1872 万 9823 站长邮箱

沪 ICP 备 18037240 号-1

沪公网安备 31011002002865 号