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
01 02 03 04 05 06 07 08 09 10 11 12 13 14 | 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:
01 02 03 04 05 06 07 08 09 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 | 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:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 | 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:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 | <? 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.