Roy Triesscheijn’s Weblog

My programming world

XNA: Proxyclasses to ‘Serialize’ assets using the IntermediateSerializer

Posted by Roy Triesscheijn on 28th September 2010

As some of you’ve might seen, I’ve been breaking my head over the following problem for the past few days:

How do I serialize the class like the following without creating a special ‘intermediate’ struct for each and ever classes

public class MyData
    {
        [ContentSerializer(SharedResource = true)]
        public Texture2D myTexture;
        public int myInt;
        public string[] myLines;
        public MyData() { } // for deserialization
        public MyData(int myInt, Texture2DProxy myTexture, string[] myLines)
        {
            this.myInt = myInt;
            this.myTexture = myTexture;
            this.myLines = myLines;
        }
    }

Even though I’ve marked the Texture2D as a shared resource, I still can’t serialize it using the IntermediateSerializer because a Texture2D always has a reference to a GraphicsDevice object, and the IS can’t serialize that.

Fortunately some helpfull people in #XNA on Efnet (ecosky and flashed, that’s you guys!) told me about proxyclasses, first I was confused and thought they wanted me to write a proxy for every class in my project, but soon they helped me understand that I should write a proxy class for Texture2D. I just had to write a proxy class that can be serialized and that will store the path to texture, so that it can reload itself when deserialized.

I quickly came up with the following class, very simple but it will suffice for now:

public class Texture2DProxy
    {
        public Texture2DProxy(string path)
        {
            LoadTexture(path);
        }

        public Texture2DProxy() { }

        private void LoadTexture(string path)
        {
            ContentManager content = GameServices.GetService<ContentManager>(); //My own class, see my code snippet, you can also reference a static CM here.
            texture = content.Load<Texture2D>(path);
            this.path = path;
        }

        [ContentSerializerIgnore()] //make this is not serialized
        private Texture2D texture;

        [ContentSerializerIgnore()]
        public Texture2D Texture
        {
            get
            {
                if (texture == null)
                {
                    LoadTexture(path);
                }
                return texture;
            }
        }

        [ContentSerializer()] //make sure this is, even-though it's a private field.
        private string path;
    }

Now we just need to replace Texture2D with Texture2DProxy everywhere, and update some code. In the end we can now serialize and deserialize the MyData class like this:

public void DoSerialize(MyData myData)
        {
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;
            using (XmlWriter writer = XmlWriter.Create("example.xml", settings))
            {
                IntermediateSerializer.Serialize(writer, myData, null);
            }
        }

        public MyData DoLoad()
        {
            MyData tmp;
            XmlReaderSettings settings = new XmlReaderSettings();
            using(XmlReader reader = XmlReader.Create("example.xml", settings))
            {
                tmp = IntermediateSerializer.Deserialize<MyData>(reader, null);
            }

            return tmp;
        }

Btw, serializing like this will generate the following XML file:

<?xml version="1.0" encoding="utf-8"?>
<XnaContent>
  <Asset Type="XNASerialization.MyData">
    <myTexture>#Resource1</myTexture>
    <myInt>42</myInt>
    <myLines>
      <Item>Two households, both alike in dignity,</Item>
      <Item>In fair Verona, where we lay our scene,</Item>
      <Item>From ancient grudge break to new mutiny,</Item>
      <Item>Where civil blood makes civil hands unclean.</Item>
    </myLines>
  </Asset>
  <Resources>
    <Resource ID="#Resource1" Type="XNASerialization.Texture2DProxy">
      <path>latern</path>
    </Resource>
  </Resources>
</XnaContent>

With this problem out of the way, I can now finally start working on a way to save and restore levels in Hollandia. Oh btw, you can ofcourse do the same trick with SoundEffects and Models or anything else for that matter.

Tags: , , , , , ,
Posted in Blog, General Coding, General Gamedesign, Hollandia, Tips, XNA | 3 Comments »

Code snippet: XNA Simple ArcBallCamera (Updated)

Posted by Roy Triesscheijn on 21st February 2010

Update 23-2-2010: Today I’ve updated the source code of the arcball camera, I made some small changes and added two helper methods for moving the camera right and forward as seen from the camera’s perspective. I’ve also changed the Position prop to be read only and show the real position of the camera instead of the relative position. I’ve also added a zoom property which can now be used instead of setting position, making this a truer ArcBallCamera and fixing a few small glitches when having a lookAt not at Vector3.Zero. (The LookAt is now truly the center of attention, changing the lookAt will also change the position of the camera.) Enjoy!

Today I was struggling with all different sorts of ArcBall/Rotating/Quaternion cameras to create a camera that could simple rotate around an object. I found a sample on xnawiki, but that didn’t seem to work (I still dont know why). In the end I ended up creating my own class.

Note that you never need an Update method for a camera, with one or two booleans you can track if something changed and only then recalculate your matrices the next time they are accessed. This saves you a lot of cpu cycles, because instead of every frame (60x per second) you only recalculate these matrices when you move the camera or change another camera property. In a lot of games this will be a lot less then 60x per second.

Special thanks to Gorion for the winning tip, when I got a bit stuck (my rotation order was flawed), sony` for spotting that the Quaternion was unneeded, and X-Tatic for helping me with the MoveCameraRight method.

Anyway the code:

public class ArcBallCamera
    {

        public ArcBallCamera(float aspectRation, Vector3 lookAt)
            : this(aspectRation, MathHelper.PiOver4, lookAt, Vector3.Up, 0.1f, float.MaxValue) { }

        public ArcBallCamera(float aspectRatio, float fieldOfView, Vector3 lookAt, Vector3 up, float nearPlane, float farPlane)
        {
            this.aspectRatio = aspectRatio;
            this.fieldOfView = fieldOfView;
            this.lookAt = lookAt;
            this.nearPlane = nearPlane;
            this.farPlane = farPlane;
        }

        /// <summary>
        /// Recreates our view matrix, then signals that the view matrix
        /// is clean.
        /// </summary>
        private void ReCreateViewMatrix()
        {
            //Calculate the relative position of the camera
            position = Vector3.Transform(Vector3.Backward, Matrix.CreateFromYawPitchRoll(yaw, pitch, 0));
            //Convert the relative position to the absolute position
            position *= zoom;
            position += lookAt;

            //Calculate a new viewmatrix
            viewMatrix = Matrix.CreateLookAt(position, lookAt, Vector3.Up);
            viewMatrixDirty = false;
        }

        /// <summary>
        /// Recreates our projection matrix, then signals that the projection
        /// matrix is clean.
        /// </summary>
        private void ReCreateProjectionMatrix()
        {
            projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, AspectRatio, nearPlane , farPlane);
            projectionMatrixDirty = false;
        }

        #region HelperMethods

        /// <summary>
        /// Moves the camera and lookAt at to the right,
        /// as seen from the camera, while keeping the same height
        /// </summary>
        public void MoveCameraRight(float amount)
        {
            Vector3 right = Vector3.Normalize(LookAt - Position); //calculate forward
            right = Vector3.Cross(right, Vector3.Up); //calculate the real right
            right.Y = 0;
            right.Normalize();
            LookAt += right * amount;
        }

        /// <summary>
        /// Moves the camera and lookAt forward,
        /// as seen from the camera, while keeping the same height
        /// </summary>
        public void MoveCameraForward(float amount)
        {
            Vector3 forward = Vector3.Normalize(LookAt - Position);
            forward.Y = 0;
            forward.Normalize();
            LookAt += forward * amount;
        }

        #endregion

        #region FieldsAndProperties
        //We don't need an update method because the camera only needs updating
        //when we change one of it's parameters.
        //We keep track if one of our matrices is dirty
        //and reacalculate that matrix when it is accesed.
        private bool viewMatrixDirty = true;
        private bool projectionMatrixDirty = true;

        public float MinPitch = -MathHelper.PiOver2 + 0.3f;
        public float MaxPitch = MathHelper.PiOver2 - 0.3f;
        private float pitch;
        public float Pitch
        {
            get { return pitch; }
            set
            {
                viewMatrixDirty = true;
                pitch = MathHelper.Clamp(value, MinPitch, MaxPitch);
            }
        }

        private float yaw;
        public float Yaw
        {
            get { return yaw; }
            set
            {
                viewMatrixDirty = true;
                yaw = value;
            }
        }

        private float fieldOfView;
        public float FieldOfView
        {
            get { return fieldOfView; }
            set
            {
                projectionMatrixDirty = true;
                fieldOfView = value;
            }
        }

        private float aspectRatio;
        public float AspectRatio
        {
            get { return aspectRatio; }
            set
            {
                projectionMatrixDirty = true;
                aspectRatio = value;
            }
        }

        private float nearPlane;
        public float NearPlane
        {
            get { return nearPlane; }
            set
            {
                projectionMatrixDirty = true;
                nearPlane = value;
            }
        }

        private float farPlane;
        public float FarPlane
        {
            get { return farPlane; }
            set
            {
                projectionMatrixDirty = true;
                farPlane = value;
            }
        }

        public float MinZoom = 1;
        public float MaxZoom = float.MaxValue;
        private float zoom = 1;
        public float Zoom
        {
            get { return zoom; }
            set
            {
                viewMatrixDirty = true;
                zoom = MathHelper.Clamp(value, MinZoom, MaxZoom);
            }
        }

        private Vector3 position;
        public Vector3 Position
        {
            get
            {
                if (viewMatrixDirty)
                {
                    ReCreateViewMatrix();
                }
                return position;
            }
        }

        private Vector3 lookAt;
        public Vector3 LookAt
        {
            get { return lookAt; }
            set
            {
                viewMatrixDirty = true;
                lookAt = value;
            }
        }
        #endregion

        #region ICamera Members
        public Matrix ViewProjectionMatrix
        {
            get {return ViewMatrix * ProjectionMatrix; }
        }

        private Matrix viewMatrix;
        public Matrix ViewMatrix
        {
            get
            {
                if (viewMatrixDirty)
                {
                    ReCreateViewMatrix();
                }
                return viewMatrix;
            }
        }

        private Matrix projectionMatrix;
        public Matrix ProjectionMatrix
        {
            get
            {
                if (projectionMatrixDirty)
                {
                    ReCreateProjectionMatrix();
                }
                return projectionMatrix;
            }
        }
        #endregion
    }

Tags: , , , , , , , ,
Posted in Blog, General Gamedesign, XNA | 7 Comments »

Review: NDepend

Posted by Roy Triesscheijn on 17th August 2009

Just at the start of the summer vaction I got an e-mail from Patrick Smacchia, Lead Developer of NDepend he asked me if I was interested to write something about their wonderfull code analysis tool, as said called NDepend.

I warned Patrick that I’ve never used code analysis tools and that’s also the reason why this blogpost about NDepend is kind of brief, I can’t really compare it to anything else. However this is a good opertunity to check out where code analysis tools are at.

After receiving a product key I downloaded NDepend from their website which almost screams “MVP” and all the companies that use NDepend. (Altough I had never heard of it before Patrick e-mailed me). Big companies like Microsoft and NASA.  The website features quite a big documentation/tutorial section which are also accesible from within NDepend.

Reporting

After downloading and following the really easy video tutorials (I so love video tutorials) the power of NDepend becomes clear, it’s not just a simple tool that just shows a nice graph with dependencies. It’s much more, NDepend generates a very nice statistical analysis of your .Net code. When the statistic analysis is complete you can print a nice report and know all sorts of things about your code. Starting with a quick overview of your code.

Excerpt from my racing game:

Number of IL instructions: 1796
Number of lines of code: 294
Number of lines of comment: 152
Percentage comment: 34
Number of assemblies: 1
Number of classes: 6
Number of types: 8
Number of abstract classes: 0
Number of interfaces: 2
Number of value types: 0
Number of exception classes: 0
Number of attribute classes: 0
Number of delegate classes: 0
Number of enumerations classes: 0
Number of generic type definitions: 0
Number of generic method definitions: 0
Percentage of public types: 75%
Percentage of public methods: 85,15%
Percentage of classes with at least one public field: 12,5%

Number of IL instructions: 1796
Number of lines of code: 294
Number of lines of comment: 152
Percentage comment: 34
Number of assemblies: 1
Number of classes: 6
Number of types: 8
Number of abstract classes: 0
Number of interfaces: 2
Number of value types: 0
Number of exception classes: 0
Number of attribute classes: 0
Number of delegate classes: 0
Number of enumerations classes: 0
Number of generic type definitions: 0
Number of generic method definitions: 0
Percentage of public types: 75%
Percentage of public methods: 85,15%
Percentage of classes with at least one public field: 12,5%

As you can see allot of information, and this is just the top of the report.

Another very usefull metric in the report is the “Assemblies Abstractness vs. Instability” graph. Which will warn you if you are soldering everything togheter,  or a just creating useless interfaces instead of working code.  (Luckily my race game was pretty centered).

There are ofcourse allot more things, I could list them all here but it might be easier to just check this http://www.ndepend.com/Features.aspx#Metrics page.

Main screen

Let’s get to the main screen (sorry about the big image but there is just so much information there)
Main screenMain screen

The main screen visually lists all parts of your project, hovering over the ‘code’ blocks on the right gives you more information on the bottom left of the screen. You can change the way the code blocks on the right are ordered. Right now they are ordered by method with lines of codes, but you can select a variety of views. But you can also view them as types with #IL generated instructions or other things.

Code Query Language

NDepend also has a feature called ‘code query language’. This CQL allows you to select things that are of special interest to you, possibly all the methods that have more than 50 lines of code. You this in a simple SQL’esque language “SELECT METHODS WHERE NbLinesOfCode > 50″. There are also allot of predefined CQL statements that you can use ranging from method sizes to code quality and design.

Conclusion

As you can see NDepend has a lot to offer, and really I’ve just skimmed the surface here. I see a lot of potential for NDepend in large project groups especially for software engineers who need to worry about the code quality and robustness but who can’t check each and every line of code themselves. For small projects and ‘one man teams’ like me this tool is plainly overkill but fun to play with. Unfortunately I don’t have knowledge of other code analysis tools so I can’t really compare it to anything else. However NDepend feels solid and gives allot of usefull (documented) information.

Oh and did I mention it is a stand alone app aswell as an integration with Visual Studio? This pleases me very much as a VS Express user.

Special Thanks to Patrick Smacchia for providing a NDepend licence.

Tags: , , , ,
Posted in Blog, General Coding | 1 Comment »

Code snippets from Visual Studio to WordPress.com

Posted by Roy Triesscheijn on 30th September 2008

To keep everything nice and tidy I was hoping for some feature in WordPress to allow me to instert C# (or any style) code nicely. But wordpress wont even remember the indenting makeing everything gibberish unless I manually add spaces instead of tabs.

Many people use the <pre> tag but after some whining at the forums there is an actual function that does syntax highlighting and everything if you spell it exactly right.

Fill in your code between: {sourcecode language=’cshar’} … {/sourcecode} but replace the ‘{‘ and  ‘}’ by the standard brackets ‘[' and ']‘. This works really nicely like here below.

public partial class Form1 : Form
{
        public Form1()
        {
            InitializeComponent();
        }

        private void buttonGenerate_Click(object sender, EventArgs e)
        {
            string source = richTextSource.Text;
            textOutput.Text = source;
        }
}

The FAQ item can be found here
Special thanks to user ‘a sacred path’

Tags: , , ,
Posted in General Coding | 4 Comments »