1、 1 / 14 场景编辑器系列 1 将 XNA绘制到WinForm中 当游戏变得越来越复杂时,编写一个编辑器简化制作过程就变得越来越重要。比方说在我的引擎 StunEngine中,在场景中放置一个模型,需要使用一个 Vector3设置它的位置属性,但因为没有直观的参考,位置往往不尽如人意,这时就需要在源代码中重新调整这个位置,再次生成项目,看看是否合适,这个工作往往要进行多次。如果有一个场景编辑器,那么只需像 3DS MAX 一样,通过光标就可以对模型进行平移、选择、缩放等操作了 。 然后 可以 将场景中的信息保存为 xml文件,引擎只需调用 这个 xml文件就可以将编辑器中放置好的对象重现出
2、来。 编辑器界面参考 首先看一下大名鼎鼎的星际争霸 2的场景编辑的界面: 可见一个编辑器通常包含菜单栏,工具栏,左侧分栏中通常显示的是各个对象的列表和它们的属性,右侧分栏是编辑器主界面。但我认为因为大多数人是右撇子,所以应该将常用的功能放置在右侧而不是左侧,这一点上 Visual Studio的界面更符合用户使用习惯。 接下来是用 C+写成的 开源引擎 Delta3D所附的关卡编辑器 : 2 / 14 这个编辑器类似于 3DS Max 实现了前、顶、右和透视四个视口,这个效果应该可以实现,即将 XNA 图像绘制到四个控件中,每个控件相机的朝向不同,我没有尝试过。下面要提到的一个 WPF程序就实
3、现了类似效果。 然后是 用于 XNA开发的 商业引擎 Torque X: 右侧分栏 中包含场景浏览器、素材浏览器、对象属性三个面板。 最后是一个我认为做得最好的一个 XNA编辑器 Rough Edges Editor的 界面: 3 / 14 将 XNA嵌入 WinForm的方法 如果上网搜索,可以找到很多 XNA编辑器的实现,总结下来大致有两种方法: 第一种是 xna官网上的例子 WinForms Graphics Device示例 ,简单地说就是自己编写一个从 System.Windows.Forms.Control 继承的控件, 然后自己编写绘制代码,网上的 Jemgine就是这种思路 ,
4、不过目前源代码还想缺个文件,我无法调试成功,截图如下 。 此外还有生成树木的开源软件 XNA Procedural Ltrees也使用了 同样方法实现了 WinForm界面,截图如下: 但使用这种方法的缺点就是你无法再使用 Microsoft.Xna.Framework.Game 类,导致也无法使用 GameComponent等位于此命名空间下的类。 我曾经翻译过的 InnovationEngine引擎 就没有使用 Game类,而且它的 GameComponent组件也是自己编写的,因此它很容易使用第一种方法创建一个场景编辑器, 不过它的编辑器教程只 开了头就没了下文。 网上有 Northwi
5、nd Engine就是基于这个引擎制作的 ,不过它的编辑器并没有使用 WinForm窗体 。 但是我的引擎是从 Game类继承的,因此要使用第一种方法,可能就需要对引擎进行伤筋动骨地修改,所以只能退而求其次使用第二种方法。 第二种方法 的核心思路就 是将 XNA绘制的输出重新定向到一个 WinForm控件中,这种方法要简洁的多。但是因为使用了 Game类,所以 XNA会自动生成一个 Form,但是我 们并不想显示这个 Form,需要将它隐藏,因此这个实现方法肯定没有第一种方法效率来得高。 4 / 14 我以前也翻译过一篇 文章: XNA 和 WinForm,文中的实现原理就是第二种方法,但是我
6、觉得它比较难用,用我的话说:它使用的方法是以 Game类为核心,但我擅长的方法是以Form 类为核心,所以具体实现并没有参照上文。不过本网站上的 StunEngine 的 Sample05用的是这个方法,这是因为 Sample05比较简单,如果复杂如编辑器的话,代码会难以阅读。 具体实现 具体的实现过程参考了网上的这篇文章http:/ 下文简称 XNABeyond。 而且这篇文章不仅 介绍了如何将 XNA嵌入到 WinForm,而且还介绍了如何将 XNA嵌入到 WPF 中,应该说 WPF 是未来的潮流, 而且在 Net4.0 中还能实现漂亮的 Ribbon 界面, 所以如果时间允许, 以后我会
7、将场景编辑器移植到 WPF 中。这篇文章的第三个部分还通过Silverlight将 XNA嵌入到了网页中,但我没有调试成功,以后再研究吧。 同样在 codeproject 网 站 上 还 有 个 将 XNA 嵌入 WPF 的例子http:/ WPF的界面要比 WinForm漂亮,这个例子还实现了 类似于 3DS MAX的 分屏效果,如下图所示 。 好了,下面言归正传, 虽然原理参照了上述文章,但根据我的引擎做了一些修改。 首先创建一个 Windows Game 项目,我把它命名为 SceneEditor,然后在解决方案中添加引擎项目 StunEngine的引用,如下图所示。 接着在 Scene
8、Editor 项目中添加一个 Windows 窗体( IDE 会自动添加5 / 14 System.Windows.Forms的引用),我命名为 MainForm,然后设计以下界面: 其中包括顶部的菜单栏,工具栏,底部的状态栏,中间是一个 splitContainer 控件,左侧添加一个 Panel控件, XNA的输出图像就是绘制在这个控件上的,右侧再添加一个水平拆分的 splitContainer 控件,此控件上方添加一个 TreeView 控件,主要用于显示场景中所有对象的信息,下方是一个 propertyGrid控件,用于显示被选择的对象的属性。 然后添加一个类,我命名为 ScenEdi
9、tor,这个类就是游戏的主类,它从 引擎类 StunEngine继承,下面是关键代码, 注意我的实现原理与 XNABeyond 相同,但做了一些修改,使用起来更灵活。 1引擎类需要对 WinForm窗体(本例中是 MainForm)和绘制输出其上的 Control的引用(本例中是左侧的 panel1),为了使用灵活,需在 引擎项目 StunEngine中实现 ,所以首先在 StunEngine 项目 中添加一个接口,表示引擎必须实现 WinForm 和 Control,我命名为IWinForm,代码如下 : namespace StunEngine / / 通过 StunEngine引擎绘制的
10、 Windows Form接口 / public interface IWinForm / / 获取绘制 XNA图像的 winform控件,通常是一个 panel / Control winControl get; / 6 / 14 / 获取 Windows Form对象 / Form Form get; 2在 引擎类 StunEngine中,需要实现对 WinForm和 Control的引用;添加 WinForm和Control的几个事件处理程序;隐藏 XNA的游戏窗口;将 XNA的图像输出重新定向到 Control上,这需要修改引擎类的构造函数,为了代码清晰,我使用了部分类 partial
11、 class。 需要再 添加一个类,我命名为 StunEngineWinForm.cs,此类中的构造函数以 IWinForm 为参数,包含用于编辑器的 其他 代码 。现在 StunEngine.cs 中的不带参数的构造函数创建用于 XNA 游戏,StunEngineWinForm.cs中以 IWinForm接口为参数的构造函数用于编辑器 : namespace StunEngine /* * 这个部分类只包含用于编辑器的支持 Windows.Forms的代码 */ / / 引擎主类 / public partial class StunXnaGE : Game #if !XBOX / / 用于
12、绘制 XNA图像的 Windows.Forms.Control控件,通 常是一个 panel控件 / protected Control winControl; / / 用于绘制的 Windows.Forms.Form窗体 / protected Form winForm; / / XNA框架的游戏窗口 / private Form gameWindowForm; #endif 7 / 14 #if !XBOX /= / / 使用 IWinForm接口为参数创建一个新引擎对象。 / 不要创建这个类的示例,而是应该从这个类继承。 / StunXnaGE派生自 Microsoft.Xna.Fram
13、ework.Game,使用方法与 Game 对象相同。 / IWinForm接口使用 GraphicsDevice.Present(handle)将绘制重定向到 windows窗体的某个控件。 / / IWinForm接口,实现了对 winForm和 winControl的引用public StunXnaGE(IWinForm form) : this() if (form != null) this.winForm = form.Form; this.winControl = form.winControl; / 添加一些处理事件 this.winForm.FormClosing += ne
14、w FormClosingEventHandler(winForm_FormClosing); this.winControl.Resize += new EventHandler(winControl_Resize); / / 如果使用 Winform窗体,则将显示重新定向到一个窗体控件中 / / protected override void Draw(GameTime gameTime) base.Draw(gameTime); / 绘制光标 spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, S
15、aveStateMode.SaveState); if (scm.isShowMouseCursor) spriteBatch.Draw(cursor, new Rectangle(Input.MousePos.X, Input.MousePos.Y, 32, 32), Color.White); 8 / 14 spriteBatch.End(); if (winControl != null) GraphicsDevice.Present(winControl.Handle); / / 处理主窗体的 Closing事件 / void winForm_FormClosing(object se
16、nder, FormClosingEventArgs e) / 如果主窗体关闭则退出程序 this.Exit(); / / 根据 WinForm绘制控件的大小重新设置图形设备的大小 。 / / / void winControl_Resize(object sender, EventArgs e) Control c = (Control)sender; if (c != null) Graphics.PreferredBackBufferHeight = c.ClientSize.Height 0 ? c.ClientSize.Height : 2; Graphics.PreferredBa
17、ckBufferWidth = c.ClientSize.Width 0 ? c.ClientSize.Width : 2; Graphics.ApplyChanges(); / / 在 Windows.Forms环境中不需要 XNA游戏窗口,所以将它隐藏 / / / private void gameWindowForm_Shown(object sender, EventArgs e) this.gameWindowForm.Hide(); winControl_Resize(winControl, null); 9 / 14 #endif 还需要在引擎类的 Initialize方法中添加
18、以下代码( 注意 Initialize方法仍位于 StunEngine.cs文件中而不是在 StunEngineWinForm.cs文件中): #if !XBOX /- / 用于 Windows.Forms 支持的代码,添加 gameWindowForm 调用 Shown 方法时的处理事件 /- if (winForm != null) gameWindowForm = System.Windows.Forms.Form)System.Windows.Forms.Form.FromHandle(this.Window.Handle); gameWindowForm.Shown += new E
19、ventHandler(gameWindowForm_Shown); /- / 用于 Windows.Forms支持的代码:显示主窗体 /- if (winForm != null) winForm.Show(); #endif 3 在引擎类中做好了准备工作之后,就可以回到 SceneEditor项目了。由于在引擎中我们并不知道 SceneEditor 项目中的引擎类究竟叫什么名 称,而在编辑器项目中需要有对这个引擎类的引用,所以还需要创建一个接口实现这个类的引用,我命名为 IEditorHost,代码如下: namespace SceneEditor public interface IEd
20、itorHost : StunEngine.IWinForm / / 指向编辑器主类的引用 / SceneEditor Editor set; 这个接口非常简单,它派生自引擎的 IwinForm接口,在它的基础上添加了对 SceneEditor类的引用,因为此时我已经知道我的编辑器的引擎类叫做 SceneEditor。 4然后编写 SceneEditor类的代码: namespace SceneEditor 10 / 14 / / Game对象 / public class SceneEditor : StunXnaGE #region 构造函数和私用成员 / 编辑器使用的 Scene类 pu
21、blic Scene viewScene; / / 以一个 IEditorHost接口为参数的构造函数。 / / public SceneEditor(IEditorHost winForm) : base(winForm) winForm.Editor = this; #endregion #region 重写的方法 / / 初始化 / protected override void Initialize() base.Initialize(); / 将 fps设置为 30 帧每秒,在 WinForm中设置更高的 fps没什么意义 TargetElapsedTime = new TimeSpan(0, 0, 0, 0, 33); / / 设置状态栏的文字 / / protected override void Draw(GameTime gameTime) base.Draw(gameTime); StatusStrip sst = (MainForm)winForm).statusStrip1; sst.Items0.Text = “相机位置: “ + viewScene.Camera.Pose.Position.ToString();
Copyright © 2018-2021 Wenke99.com All rights reserved
工信部备案号:浙ICP备20026746号-2
公安局备案号:浙公网安备33038302330469号
本站为C2C交文档易平台,即用户上传的文档直接卖给下载用户,本站只是网络服务中间平台,所有原创文档下载所得归上传人所有,若您发现上传作品侵犯了您的权利,请立刻联系网站客服并提供证据,平台将在3个工作日内予以改正。