WinForms Graphics Device示例
本示例展示了如何使用XNA框架的GraphicsDevice在WinForms程序中显示3D图形。
原文地址:http://creators.xna.com/en-US/sample/winforms_series1。
示例概览
XNA框架的Game类提供了一个快速轻便的方式创建一个游戏,它自动为游戏创建一个窗口,初始化图形硬件,并提供可以重写的Update和Draw方法。。但有时Game的行为还不够灵活。你可能还想对窗口的创建有更多的控制,或许你还想编写一个关卡编辑器并在3D绘制表面旁放置Windows用户界面控件。
幸运的是,XNA框架在设计时就想到了会遇到这种情况,框架实际上是由两个独立的部分组成的:Microsoft.Xna.Framework提供诸如数学、图形、输入和音频类等的核心功能,而Microsoft.Xna.Framework.Game提供诸如Game类等可选的高层次代码。如果你想使用其他方式创建你的游戏,你可以用自己的代码替换来自于Microsoft.Xna.Framework.Game的功能。
这个示例实现了一个GraphicsDeviceControl类,这个类从System.Windows.Forms.Control继承,可以让一个WinForms控件使用XNA Framework GraphicsDevice绘制自己。这个示例展示了如何让多个控件共享一个GraphicsDevice,如何处理图形设备的大小改变和丢失,如何实现IgraphicsDeviceService接口用来通过ContentManager加载数据。
要在你的代码中重用这些功能,你可以复制GraphicsDeviceControl.cs,GraphicsDeviceService.cs和ServiceContainer.cs源文件,让你的自定义控件类从GraphicsDeviceControl继承,并重写Initialize和Draw方法。
注意这个示例只适用于Windows,在Xbox 360上无法使用WinForms。
最小Shader配置
Vertex Shader Model 1.1
Pixel Shader Model 1.1
工作原理
这个示例使用标准的"Windows Application"项目模板,而不是使用XNA框架提供的"Windows Game"模板。为了使用XNA Framework的功能,你必须手动将Microsoft.Xna.Framework添加到项目的引用中去。
这个示例提供两个自定义控件:SpriteFontControl和SpinningTriangleControl。这两个控件都是从GraphicsDeviceControl继承的,通过使用XNA框架绘制图像。
管理GraphicsDevice
在同一时间内可以使用许多个GraphicsDeviceControl对象。从效率角度考虑,你只想创建一个底层的GraphicsDevice对象,这个对象通过GraphicsDeviceService类进行管理,这个类创建并拥有GraphicsDevice。一个引用计数变量(A reference counting system)保存有多少个GraphicsDeviceControl对象使用了共享的GraphicsDeviceService的数据。在AddRef方法中中,GraphicsDeviceService检查是否是需要图形设备的第一个控件。如果是,它就创建一个单例对象。如果不是,就只是重用已经存在的对象。在Release方法中,它检查是否是使用图形设备的最后一个控件,如果是,则释放共享的GraphicsDevice。
如果一个以上的控件共享一个图形设备时会出现一个问题:你应该对这个设备设置多大的后备缓冲?这些控件的大小通常是不一样的,如果每次在绘制另一个大小不同的控件时都要重置图形设备以改变后备缓冲的大小,这样做显然效率不高。解决方法是将后备缓冲的大小设置为最大的一个控件的大小,当绘制较小的控件时只使用这个后备缓冲的一部分。这个操作是由GraphicsDeviceControl.BeginDraw方法处理的,这个方法将视口设置为只绘制后备缓冲的左上部分(对应当前控件的大小),而通过GraphicsDeviceControl.EndDraw方法, 使用重载的GraphicsDevice.Present让你可以指定将后备缓冲的哪一部分区域复制到屏幕上。EndDraw还负责向Present传递正确的窗口句柄,这样它才能知道绘制的是哪个控件。
图形设备并不能保证总是可用的。当你锁定桌面,或某些其他程序切换到全屏3D模式时,设备会变成桌面。另外,当某些其他程序切换到全屏3D模式时,设备会在一段时间内无法访问。当发生以上这些情况时,你必须重置图形设备才能继续使用它。这个操作通常是由XNA框架的Game类完成的,但是因为现在你没有使用Game类,所以必须自己进行处理。这是由GraphicsDeviceControl.HandleDeviceReset方法进行处理的,这个方法被BeginDraw调用。它检查了当前设备的状态,如果设备丢失,它就返回一个错误信息,阻止你使用设备进行绘制。如果设备已经丢失并需要重置,或者后备缓冲太小,这个方法会重新设置设备,保证它是正确的,并且后备缓冲有合适的大小。
设计器支持
WinForms控件不仅用在程序的运行中,如果你将MainForm.cs文件加载到设计器中,你可以看到它包含的两个控件的预览,这些预览是通过设计器查找并创建一个自定义控件对象实现的。在大多数情况中,这个方法是有效的,但是在设计器中运行一个包含动画的3D图形控件太耗费资源了!
为了优雅地处理设计器显示,GraphicsDeviceControl类从OnCreateControl 方法中检查它的DesignMode属性。如果是运行在设计器中,它会略过初始化图形设备的操作,这样就不会调用Initialize或Draw方法。作为替代,它会使用一个简单得多的PaintUsingSystemDrawing方法显示一个控件的占位符,这样你就可以在设计器中看到它了。
加载内容(Content)
为了加载诸如模型,纹理或SpriteFont数据之类的内容,你需要两件事情。
首先,你必须创建内容,这可以通过在解决方案中添加一个XNA Framework content 项目做到。没有直接的方法可以创建一个content项目,你可以创建一个临时的"Windows Game"项目,然后将.contentproj文件复制到你的WinForms程序目录,然后将它添加到解决方案中。.contentproj扩展名无法被Add Existing Project对话框识别,所以你必须输入完整的项目文件名称(包括扩展名)。然后你就可以将内容文件(本例中使用的是Arial.spritefont)添加到这个内容项目中去了。要确保内容文件在运行程序中总是被生成,你需要右击主项目,选择项目依赖项(Project Dependencies),然后勾选content项目。要确保内容文件的输出目录放置在正确的目录中,你需要打开content项目的属性,将Build / Output路径属性设置为正确的位置(Debug和Release项目配置都要设置)。
第二,你必须创建一个ContentManager加载内容文件。ContentManager需要访问自定义的GraphicsDevice加载图形数据,你必须将两者通过某种方式联系起来。 GraphicsDeviceService类实现了IGraphicsDeviceService接口,当ContentManager需要图形设备时会使用这个接口。要暴露这个接口,GraphicsDeviceControl提供了一个Services属性,并在它的OnCreateControl方法中注册了IGraphicsDeviceService。有了这个通道你就可以将自定义Services传递到ContentManager构造函数中去了,具体实现可参见SpriteFontControl.Initialize方法。
本示例中使用的方法需要事先知道所有的内容文件,所以这些文件应该作为项目的一部分进行生成。想要动态地生成和加载内容,可参见WinForms Content Loading示例。
动画
WinForms程序通常是没有动画的,它们只是放置在某处,不做任何事情,直到用户的操作(按下键盘或鼠标事件)通知一个事件,这时,它们就会进行动作,处理事件,重画屏幕,然后又返回初态。
游戏的工作过程并不相同,如果你使用XNA Framework Game类, 即使用户没有输入,它也会频繁地调用Update和Draw方法。
当你替换掉Game类时,你必须判断是使用WinForms风格的基于事件的更新,还是使用游戏风格的连续动画。本示例对这两种方法都进行了展示。SpriteFontControl类没有动画:它只是使用Draw方法实现一些文本,只有当窗口改变大小或另一些窗口覆盖其上时才需要重绘,这个方式与普通的WinForms控件是相同的。而SpinningTriangleControl使用的游戏风格的动画,它是通过在Initialize方法中添加一行代码实现的。
// Hook the idle event to constantly redraw our animation. Application.Idle += delegate { Invalidate(); };
通过以上代码,只要处理完其他事件,WinForms就会重绘这个控件。在Draw方法中,你使用一个Stopwatch知道自上一次绘制以来经过的时间,然后这个时间值用来控制三角形的旋转速度。
文件下载(已下载 2080 次)发布时间:2010/8/28 上午9:51:00 阅读次数:9384