XNA Game Engine Tutorial Series #6.5 – Engine Update
Note: This section is not necessary if you started with tutorial #1 after 12/10/2008
In this mini-tutorial, we will be making some minor updates to the engine. First, we will be changing the utility classes to static classes. Remove the instances of MathUtil and GraphicsUtil from the Engine class. Then, add the modifier ’static’ before the class definition and methods for the two classes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public static class GraphicsUtil { // Creates a RenderTarget2D with the specified parameters public static RenderTarget2D CreateRenderTarget() { return CreateRenderTarget(Engine.GraphicsDevice.Viewport.Width, Engine.GraphicsDevice.Viewport.Height); } // Creates a RenderTarget2D with the specified parameters public static RenderTarget2D CreateRenderTarget(int Width, int Height) { return CreateRenderTarget(Width, Height, Engine.GraphicsDevice.DisplayMode.Format); } etc, etc... |
Second, the Rectangle property in I2DComponent should be a Rectangle.
1 2 3 4 | public interface I2DComponent { Rectangle Rectangle { get; set; } } |
Now we will add a few more methods to the MathUtil class:
1 2 3 4 5 6 7 8 9 10 11 12 | // Converts a rotation vector into a rotation matrix public static Matrix Vector3ToMatrix(Vector3 Rotation) { return Matrix.CreateFromYawPitchRoll(Rotation.Y, Rotation.X, Rotation.Z); } // Converts a rotation matrix into a rotation vector public static Vector3 MatrixToVector3(Matrix Rotation) { Quaternion q = Quaternion.CreateFromRotationMatrix(Rotation); return new Vector3(q.X, q.Y, q.Z); } |
Now change these methods in the MathUtil to use matrices for rotation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | // Creates a world matrix public static Matrix CreateWorldMatrix(Vector3 Translation) { return CreateWorldMatrix(Translation, Matrix.Identity); } // Creates a world matrix public static Matrix CreateWorldMatrix(Vector3 Translation, Matrix Rotation) { return CreateWorldMatrix(Translation, Rotation, Vector3.One); } // Creates a world matrix public static Matrix CreateWorldMatrix(Vector3 Translation, Matrix Rotation, Vector3 Scale) { return Matrix.CreateScale(Scale) * Rotation * Matrix.CreateTranslation(Translation); } |
In the Camera class:
1 2 3 4 5 | Projection = Engine.Math.CreateProjectionMatrix(); becomes Projection = MathUtil.CreateProjectionMatrix(); |
Now change the following in the Actor class:
1 2 3 4 5 | Matrix world = Engine.Math.CreateWorldMatrix(Position, Rotation, Scale); becomes Matrix world = MathUtil.CreateWorldMatrix(Position, Rotation, Scale); |
1 2 3 4 5 | Rotation = Vector3.Zero; becomes EulerRotation = Vector3.Zero; |
Third, representing rotations as a Vector3 is a bad way to do it, for several reasons. Change I3DComponent to look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | // Represents a 3D object. These objects will be drawn before // 2D objects, and will have modifiers automatically provided // in the editor. public interface I3DComponent { // Position in the Cartesian system (X, Y, Z) Vector3 Position { get; set; } // Rotation represented as a Vector3. This shouldn't // be used for calculations, it is left in so that // the rotation can be more easily modified by hand Vector3 EulerRotation { get; set; } // Rotation as a Matrix. This will give much smoother // and cleaner calculations that a Vector3 Matrix Rotation { get; set; } // Scale for each axis (X, Y, Z) Vector3 Scale { get; set; } // BoundingBox to use for picking and pre-collision BoundingBox BoundingBox { get; } } |
Now we need to fix this in a few other classes. Change the following in the Actor class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | // I3DComponent values public virtual Vector3 Position { get; set; } public Vector3 EulerRotation { get { return MathUtil.MatrixToVector3(Rotation); } set { Rotation = MathUtil.Vector3ToMatrix(value); } } public virtual Matrix Rotation { get; set; } public virtual Vector3 Scale { get; set; } public virtual BoundingBox BoundingBox { get { return new BoundingBox( Position - (Scale / 2), Position + (Scale / 2) ); } } |
Now change the following in PhysicsActor:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | // Override the rotation of the base class to that // of the physics object public override Matrix Rotation { get { if (PhysicsObject != null) return PhysicsObject.Rotation; else return Matrix.Identity; } set { if (PhysicsObject != null) PhysicsObject.Rotation = value; } } |
Change the Camera class to look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | using Microsoft.Xna.Framework; using System; using System.Runtime.Serialization; namespace Innovation { // Basic Camera class public class Camera : Component, I3DComponent { // Internal values Vector3 position = Vector3.Zero; Matrix rotationMatrix = Matrix.Identity; Vector3 target = new Vector3(0, 0, -1); Vector3 up = Vector3.Up; Matrix view; Matrix projection; // The point the camera is looking at public virtual Vector3 Target { get { return target; } set { target = value; } } // The View and Projection matrices commonly used for rendering public virtual Matrix View { get { return view; } set { view = value; } } public virtual Matrix Projection { get { return projection; } set { projection = value; } } public virtual Vector3 Up { get { return up; } set { up = value; } } // Public I3DComponent values public virtual Vector3 Position { get { return position; } set { position = value; } } public virtual Vector3 Scale { get { return Vector3.One; } set { } } public Vector3 EulerRotation { get { return MathUtil.MatrixToVector3(Rotation); } set { Rotation = MathUtil.Vector3ToMatrix(value); } } // The rotation matrix used by the camera and the current up vector public virtual Matrix Rotation { get { return rotationMatrix; } set { rotationMatrix = value; } } public virtual BoundingBox BoundingBox { get { return new BoundingBox(Position - Vector3.One, Position + Vector3.One); } } // Constructors public Camera(GameScreen Parent) : base(Parent) { } public Camera() : base() { } // Update the camera public override void Update() { // Calculate the direction from the position to the target, and normalize Vector3 newForward = Target - Position; newForward.Normalize(); // Set the rotation matrix's forward to this vector Matrix rotationMatrixCopy = this.Rotation; rotationMatrixCopy.Forward = newForward; // Save a copy of "Up" (0, 1, 0) Vector3 referenceVector = Vector3.Up; // On the slim chance that the camera is pointed perfectly parallel with // the Y Axis, we cannot use cross product with a parallel axis, so we // change the reference vector to the forward axis (Z). if (rotationMatrixCopy.Forward.Y == referenceVector.Y || rotationMatrixCopy.Forward.Y == -referenceVector.Y) referenceVector = Vector3.Backward; // Calculate the other parts of the rotation matrix rotationMatrixCopy.Right = Vector3.Cross(this.Rotation.Forward, referenceVector); rotationMatrixCopy.Up = Vector3.Cross(this.Rotation.Right, this.Rotation.Forward); this.Rotation = rotationMatrixCopy; // Use the rotation matrix to find the new up Up = Rotation.Up; // Recalculate View and Projection using the new Position, Target, and Up View = Matrix.CreateLookAt(Position, Target, Up); Projection = MathUtil.CreateProjectionMatrix(); } } } |
Change the following in the PhysicsObject class:
1 2 3 4 5 6 | // The PhysicsObject's rotation as a Matrix public Matrix Rotation { get { return Body.Orientation; } set { Body.MoveTo(Body.Position, value); } } |
1 2 3 4 5 6 | // The PhysicsObject's rotation as a Euler Vector public Vector3 EulerRotation { get { return MathUtil.MatrixToVector3(Rotation); } set { Rotation = MathUtil.Vector3ToMatrix(value); } } |
Remove the Orientation property from PhysicsObject. Now go through the various physics object, and change any references to Rotation to EulerRotation, and any references to Orientation to Rotation.

Tags:
Sean
7 Responses to “XNA Game Engine Tutorial Series #6.5 – Engine Update”
December 8, 2008 at 11:05 pm
I’m guessing posting tut 7 was an accident? tut 7 showed in my rss then was gone on your site but now you posted this its back? oh well all the updates are great I’m learning a lot from this series and thanks for all of it.
December 8, 2008 at 11:47 pm
I posted it forgetting that there were a lot of updates to make, so I took it down, wrote this post, then reposted 7 so that it would work after people made the updates. I’m also in the middle of updating the older posts so new people wouldn’t have to do these ‘update’ posts.
December 9, 2008 at 11:56 pm
Fantastic tutorials Sean, great to see someone who comments their code seems to be a bit of a lost art. Keep up the great work, very much enjoying reading your stuff, caused me to change the way I have been structuring parts of my own engine.
December 10, 2008 at 3:22 am
Also just a quick typo above the projection that needs to be changed to use the MathUtil class is over in Camera not Actor.
December 11, 2008 at 4:25 pm
Errm, I think in the I2DComponent part, you need to change it to “The Rectangle property needs to be a Rectangle” instead of “needs to be a Vector2″.
Great tuts, btw!
December 11, 2008 at 4:51 pm
[...] XNA Game Engine Tutorial Series #6.5 – Engine Update [...]
December 11, 2008 at 4:57 pm
[...] XNA Game Engine Tutorial Series #6.5 – Engine Update [...]