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 »

Tutorial: Using the XNA Content Pipeline for localization: Part 1, Strings

Posted by Roy Triesscheijn on 30th January 2010

Lately I’ve been working on making localization easier. I came up with an idea to use the content pipeline and content manager to automatically localize your strings*, textures and sounds (and any other content type you might want to localize).

In part one we create a new content pipeline extension and a new content manager without much effort. And we start to replace our strings with calls to our new content manager (that replaces the default one). In the end you will be able to load a string using Content.Load<String>(“Package.Key”); The correct string will be loaded from our xnb files (imported using a custom xml importer) in the language set in the content manager.

In part two we are going to extend this example to load other content as normal (Content.Load<Texture2D>(@”Folder\Name”);). And if you’ve created a localized version this will be loaded instead of the original file.

Part 1 is now up at SgtConker.Com: http://www.sgtconker.com/2010/01/article-using-xna-content-pipeline-extensions-for-localization/

Feel free to post any comments and questions either here or at sgtconker.com.

Part 2 will probably be up end of next week.

kick it on GameDevKicks.com

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

Simple On demand Content Loading in XNA

Posted by Roy Triesscheijn on 27th August 2008

Written in C# for XNA2.0 but might be useful with other versions.

Warning: I just found out that the XNA2.0 ContentManager automatically checks if content is already loaded or not. The on-demand content loading part of this short-tut is still usefull and there isn’t really overhead in finding content via this dictionary (you have to find it someway) so it is dynamic and very handy, but it doesn’t get an extra plus for making content only load once.

The problem.

There are a few problems with content in any game.

-Is it already loaded?
-Will I need it again? (solved by XNA’s own ContentManager)
-Where do I need to use it
-How am I going to get it to the object that needs it.

Loading content in game1.cs is easy. You just ask to contentmanager to load a certain file, for example:

Texture2D  texture = Content.Load<Texture2D>(@”Textures\Monkey”);

But what if you have several classes in a hierarchy, take for example a WorldHandler that handles multiple pawns. How do you get the ContentManager to the pawns when they want a texture for visual representation. Ofcourse the anwser to this question is relatively easy, create a static class that makes the ContentManager accesible to them. However if we are going to create a seperate class, why not let that class load the content for the pawns so that we don’t need any loading logic in there? That would be great, it would save us a whole lot of lines and if something about the content changes (for example what if we decide that we want a seperate folder for all the lolcats -textures we are going to put in our game?) we only have to change it in one place.

So that solves the later 2 problems. But if we have 50 warriors in our scene that all use the same texture, it would be a waste to load it 50 times, but how can we know if it was already loaded? The sollution I came up for is a dictionary like this:

private static Dictionary<string, Texture2D> textures = new Dictionary<string, Texture2D>();

We can make a dictionary for each content type (for my sprite game I’m using textures and effects atm).

Next we need a way of loading the content into the dictionaries in a smart way. We could load all the content at startup but that would be kind of wasteful, we can do 2 things.

-Load at start of a level
-Load on demand

Or a way in between. After some thinking at this moment I went with total on demand loading. Here is a code sample of how I did it.

public static Texture2D LoadTexture(string name)
{
    Texture2D texture;
    if (textures.TryGetValue(name, out texture) == true)
    {
        return texture;
    }
    else
    {
        texture = Content.Load<texture2D>(@"Textures\" + name);
        textures.Add(name, texture);
        return texture;
    }
}

If a texture’s tag wasn’t found in the dictionary yet it will be loaded and then returned to the calling object. If it was already loaded it will be returned directly.

Now we have a sollution for all 4 simple problems. We have a nice static class that will take care of all the content loading, that will never load a piece of content twice and that is accesible for all objects in your game. This class could be extended with a garbage collection mechanism if you have so much content that this is needed.

If you would like to load some content at the start of a level, dont worry you can always make a list of content that is needed (in this case a string[] with all the tags for the content) and run a foreach loop on the public static Texture2D LoadTexture(string name) method. You don’t even have to supply a Texture2D object already because just calling the method will get the content loaded.

I hope this article helps all of you around there with making your content management more efficient and easier to handle, but remember I’m still new to all this to and can I no way be compared to all the XNA devs and their techdemo’s so be sure to load arroound at creators.xna.com for some more info on content management. Goodluck and don’t hesitate to comment with links to your tutorials/games/content!

Tags: , , , , , , ,
Posted in XNA | 3 Comments »