In this chapter, we are going to get started on our editor. To get started, add a new project to your solution. However, this time we will make it a “Windows Forms” project. This will create a project with the necessary references to use windows forms in your project. These forms are the basis of (almost) all windows applications. They have a number of properties regarding size, title, toolbar buttons, etc. A form is basically a container for objects like buttons, sliders, textboxes, etc. These objects are called “controls”, and custom controls can be created by inheriting from “Control”.

Now that your project is created, go into the solution explorer and click Form1. You should see your form appear in the design window. This window is used for editing your forms and controls. Controls in the forms here won’t respond to clicks, input, etc, but they do give you access to all their properties by clicking on them and opening the Properties window. Events provided by the control are also available here by clicking the lighting bolt icon in the toolbar at the top of the property list. To add a handler to an event, just double click in the box by the events name and it will take you to the code window. This window is what you are used to, where you can edit the code of your Form or Control class. This is also available by right clicking and clicking on “View Code”. This works the other way around too, “View Design” is available in the code window. An important note about the property editor: the properties you set and controls you add to the form in the design window are all set via code that is automatically generated. If you click the [+] next to the form’s file in the solution explorer, you will see that there are actually two files here: one is the code file you should edit: “ClassName.cs”, the other is the automatically generated one. This file has the modified name “ClassName.Designer.cs”.

If you look at the first file, you will see that the Control is actually a “partial” class, which only means that its definition is split across multiple files. If you analyze the generated code, you will see that most of the work setting properties and creating instances of objects is done in the “InitializeComponent()” method. You can see that in the manually written file, part of which was written for you when you created the form (which happened when the project was created), the default constructor of the Form1 class calls this method. You should generally leave this as the first call, or your form’s controls will not be ready and set up properly. You should not change the code in the generated code file, as it will be rebuilt every time a property is changed or a control is added. You could even mess up the design window by editing this file, because it relies on it to setup the design window. In short, do all your editing in the manually written file.

The final part of the designer is the Toolbox. If you can’t see it, open it from the “View” menu. If you open this window you will see a list of all the standard controls provided by Microsoft. You can add more item to this box by right clicking and choosing “Choose Items…”. To add one of the items, just drag it onto the form or control you are editing and the code generator will take care of everything else. The controls added to the control can be accessed from the manual file just like it was declared in the class like we are used to. To change the name you access it from, change its “Name” property. You should now know most of what you need to know to use the designer. The first thing we are going to do is make our form a little bigger. Move the cursor over the bottom right of the window and resize it to a reasonable size, probably at least 1024×768-ish. You can see the current size in the Properties window. Change the “Text” property to something other than Form1. This will be the main form for the editor, so I just named mine “Innovation Engine Editor”.

Now that our form is set up, we need to get XNA rendering into our window. First add the following references (References>Add Reference): .Net: Microsoft.Xna.Framework, Microsoft.Xna.Framework.Game, Browse: InnovationEngine.dll. Now we can get to the render control. XNA has kindly made this rather difficult for us ( :) just kidding) so we have a few things to do.

We need to set up the graphics device and related services XNA needs to draw, because we can’t use the Game class here. Luckily, there is a sample on the Creators Club Website that demonstrates this. Most of the code will be borrowed from this sample, but we will need to make our control that will be the render surface, and take care of things like GameTime and the GraphicsDevice. So, here are the files Microsoft has written, add them to your project.

GraphicsDeviceControl.cs
GraphicsDeviceService.cs

Now we will create a new control: Add>New Item>User Control, and call it something like RenderControl. The code is below:

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();
        }
    }
}

Now, just go back to the main form, and drag a new RenderControl from the toolbox into our panel with the black border. If you can’t see RenderControl in the toolbox, you may need to build the project again. If you run the project now, you will see the familiar starting state of an XNA Game, the blank CornflowerBlue screen. It doesn’t look like much, but at least it’s working!

If you want to test this out, create some components in the constructor, and you should see that the engine operates the same way as it does in a normal game.

« »