本章我们将开始制作编辑器。首先在解决方案中添加一个新项目。不过,这次我们添加的是“Windows Forms”项目。这会自动创建使用Windows窗体时必要的引用。这些窗体几乎是所有的Windows应用程序的基础。他们拥有一些窗口大小,标题,工具栏按钮等属性,一个窗体是诸如按钮,滑动条,文本框等对象的容器,这些对象被称为“控件”,而自定义控件可以从“Control”继承。

现在创建了你的项目,进入解决方案资源管理器然后单击Form1,你应该会在设计窗口看到这个窗体,并可以编辑您的窗体和控件。窗体中的控件不会回应点击,输入等,但你可以通过点击并打开属性窗口建立自己的处理程序。控件能够提供的事件也可以通过点击属性列表上方工具栏中的“闪电”使用。要处理事件句柄,只需双击事件名称这样就会切换到代码窗口,在那你可以编写窗体或控件的代码,这一步也可以通过右击“查看代码”实现。而代码视图中也有“设计视图”选项。关于属性编辑器的一个重要的提示:在设计视图窗口中设置的属性和添加的控件会自动生成代码。如果您按一下解决方案资源管理器中该文件旁边的[ + ],你会发现实际上有两个文件:一个是你可以编辑的代码文件:“ClassName.cs”,另一个是自动生成的,该文件的名称是“ClassName.Designer.cs”。

看一下第一个文件,您会看到控件是一个“partial”类,这仅意味着这个类是分开放在多个文件中的。如果看一下自动生成的代码,你会看到大多数设置属性的和创建实例的工作是在“InitializeComponent()”方法中。看一下自己输入的代码,当你创建一个窗体时(在项目创建时发生)一部分代码已近自动生成了,默认Form1类的构造函数调用了InitializeComponent()方法。你通常应该让这个方法第一个被调用,否则窗体控件可能不会正确地被创建。您不应该改变自动生成的代码,它会在属性改变或添加控件时自动重建。如果你编辑这个文件往往会将设计窗体弄得一团糟,因为建立设计窗体依赖于这个文件。总之,只编辑你的自己编写的文件。

设计器的最后部分是工具箱。如果您看不到它,可从“查看”菜单打开。如果您打开这个窗口会看到一个Microsoft所提供的所有标准控件的列表。您可以通过右击并选择“选择项...”添加更多的控件,只要将其拖放到正在编辑的窗体或控件上,程序就会自动添加并生成代码。添加控件也可以在手写文件中进行,与我们以前在类中声明的方式相同。要改变名称只需改变“Name”属性。现在您应该知道如何使用设计器了。接下来的第一件事是让窗体变得大一些。移动光标到窗口的右下方并调整到一个合理的尺寸,至少1024×768。你可以在属性窗口看到现在的尺寸。通过更改“Text”属性将默认的“Form1”改成其他名称。这将是游戏编辑器的主窗口,我命名为“Innovation Engine Editor”。

现在,我们的窗体已经建立了,我们需要将XNA渲染到我们的窗体。首先添加以下引用(References>Add Reference):Microsoft.Xna.Framework, Microsoft.Xna.Framework.Game,并找到:InnovationEngine.dll。现在我们可以渲染控件了。但XNA仁慈地将这种做法变得相当困难(说笑而已),所以我们必须先做一些事情。

我们需要建立绘制XNA所需的图形设备及相关服务,因为这里我们不能使用Game类。幸运的是,在Xna Creators Club有一个示例展示了如何做。下面大部分代码将借鉴这个范例,但我们必须使我们的控件能够绘制表面,并处理GameTime和GraphicsDevice。下面是微软编写的文件,将其添加到项目中。

GraphicsDeviceControl&GraphicsDeviceService.rar

现在我们将创建一个新的控件:添加>“新建项目”>用户控件,命名为RenderControl。代码如下所示:

using System.Diagnostics;
using System.Windows.Forms;
using Innovation;
using Microsoft.Xna.Framework;

namespace InnovationEditor
{
public partial class RenderControl : GraphicsDeviceControl
{
// Simulate the GameTime ourselves because we don't have Game
GameTime gameTime;

// Elapsed and total time for the GameTime
Stopwatch elapsedTime = new Stopwatch();
Stopwatch totalTime = new Stopwatch();

// Timer to keep track of refreshes
Timer timer;

// Camera, Keyboard, and Mouse will be handled here. They are
// public and static so they can be used anywhere
public static FPSCamera Camera;
public static KeyboardDevice Keyboard;
public static MouseDevice Mouse;

public RenderControl()
{
// Run pre-generated control code
InitializeComponent();

// Start the timer keeping track of total elapsed time
totalTime.Start();

// Hook the mouse down event for the mousedevice, so the control
// will be selected when clicked on. This avoids problems with
// mouse and keyboard camera commands affecting other controls on
// the form
this.MouseDown += new MouseEventHandler(RenderControl_MouseDown);
}

// Select control on mouse down
void RenderControl_MouseDown(object sender, MouseEventArgs e)
{
this.Select();
}

// Initialize the Control
protected override void Initialize()
{
// Set up the engine
Engine.SetupEngine(this.GraphicsServices);

// Set up the frame update timer
timer = new Timer();

// Lock framerate to 40 so we can keep performance up
timer.Interval = (int)((1f / 40f) * 1000);

// Hook timer's tick so we can refresh the view on cue
timer.Tick += new System.EventHandler(timer_Tick);
timer.Start();

// Setup the camera and move it to a good position, and make it a service
Camera = new FPSCamera();
Camera.RotateTranslate(new Vector3(0, 0, 0), new Vector3(128, 15, 300));
Engine.Services.AddService(typeof(Camera), Camera);

// Setup the keyboard and mouse, and allow the cursor to move
Keyboard = new KeyboardDevice();
Mouse = new MouseDevice();
Mouse.ResetMouseAfterUpdate = false;
}

// Timer's tick causes the view to refresh
void timer_Tick(object sender, System.EventArgs e)
{
// Invalidate everything so the whole control refreshes
this.Invalidate();

// Force the view update
this.Update();
}

// Draw the scene
protected override void Draw()
{
// Update GameTime and update the engine
UpdateGameTime();
Engine.Update(gameTime);

// Update GameTime again and draw the scene
UpdateGameTime();
Engine.Draw(gameTime, ComponentType.All);
}

// Updates the GameTime object instead of relying on Game
void UpdateGameTime()
{
// Recreate the GameTime with the current values
gameTime = new GameTime(totalTime.Elapsed, totalTime.Elapsed,
elapsedTime.Elapsed, elapsedTime.Elapsed);

// Restart the elapsed timer that keeps track of time between frames
elapsedTime.Reset();
elapsedTime.Start();
}
}
}

现在,只需回到主窗体,并从工具箱中拖动一个新的RenderControl到panel中。如果看不到RenderControl控件,您可能需要再次编译该项目。现在运行项目,您会看到熟悉的XNA游戏初始状态,一个空白的蓝色屏幕。没有其他东西,但至少工作正常!

如果您想测试,可以在构造函数中创建一些组件,您可以看到游戏引擎像一个普通游戏一样正常运行。

====================================