XNA Creators Club Online
Page 1 of 1 (8 items)
Sort Posts: Previous Next

Content Pipeline Xml Reading

Last post 1/21/2008 1:19 PM by Shawn Hargreaves. 7 replies.
  • 1/9/2008 8:48 PM

    Content Pipeline Xml Reading

    Based on Nick's informative article over at Ziggy's I have started revamping my level loading system in my game.  I've run into a bit of a hassle though and I'm hoping there is a workaround.

    Here is a sampling of my Level class:

    public class Level
    {
    private string name;

    public string Name
    {
    get { return name; }
    set { name = value; }
    }

    private int width;
    public int Width
    {
    get { return width; }
    set { width = value; }
    }

    private int height;
    public int Height
    {
    get { return height; }
    set { height = value; }
    }

    private string decription;
    public string Description
    {
    get { return decription; }
    set { decription = value; }
    }


    private List<ILevelObstacle> obstacles = new List<ILevelObstacle>();
    public List<ILevelObstacle> Obstacles
    {
    get { return obstacles; }
    set { obstacles = value; }
    }

    // bunch of useful methods below here ...

    }

    The tricky part is the last property which needs populated.  I'm not sure how to write the LevelContentWriter and LevelContentReader (or even the XML for that matter) to get this data into the Level object.

    My current non-PipeLine XML look like this:

    <level>
      <Name>Test Level 04</Name>
      <Description>Wonderful info included here</Description>
      <Width>1500</Width>
      <Height>1500</Height>
      <obstacles>
        <obstacle typeId="Start" positionX="250" positionY="400" mass="-1" rotation="0" radius="60" requiredIndex="0"/>
        <obstacle typeId="Gate" positionX="1250" positionY="750" mass="-1" rotation="0" radius="60" requiredIndex="1"/>
        <obstacle typeId="Finish" positionX="250" positionY="1100" mass="-1" rotation="0" radius="60" requiredIndex="0"/>
        <obstacle typeId="Asteroid" positionX="900" positionY="750" mass="250" rotation="1" radius="180" requiredIndex="0"/>
      </obstacles>
    </level>

    My new PipeLine XML is like this so far...

    <?xml version="1.0" encoding="utf-8" ?>
    <XnaContent>
    <Asset Type="SpaceCourse.Level">
       <Name>Construction Level</Name>
       <Description>Level used to test stuff</Description>
       <Width>800</Width>
       <Height>800</Height>
    </Asset>
    </XnaContent>

    Is there a way to get the obstacles into the list using the List<ILevelObstacle>?


  • 1/9/2008 9:21 PM In reply to

    Re: Content Pipeline Xml Reading

    After some more reading in the XNA Game Studio 2.0 documentation I can see what I want to do can certainly be done. I still can't quite find an example of the code though.

    In the docs Programming Guide | Sprite Font XML Schema Reference shows how the XML for the SpriteFont includes <CharacterRegions> element which consists of 0 or more <CharacterRegion>.  This is exactly what I need.

    However, I cannot find an exmaple of how the Reader/Writer works in this context.  If anyone knows of a reference I'd greatly appreciate it.
  • 1/9/2008 11:09 PM In reply to

    Re: Content Pipeline Xml Reading

    Actually, it is quite simple. For example, see my class:
    public class Sprite
    {
    Vector2 myPosition;
    float myRotation;
    Vector2 myScale;

    string myTextureName;
    Texture2D myTexture;

    public Vector2 Position
    {
    get { return myPosition; }
    set { myPosition = value; }
    }
    public float Rotation
    {
    get { return myRotation; }
    set { myRotation = value; }
    }
    public Vector2 Scale
    {
    get { return myScale; }
    set { myScale = value; }
    }

    public string TextureName
    {
    get { return myTextureName; }
    set { myTextureName = value; }
    }
    [ContentSerializerIgnore]
    public Texture2D Texture
    {
    get { return myTexture; }
    }

    public void LoadContent(ContentManager content)
    {
    myTexture = content.Load<Texture2D>(myTextureName);
    }

    public void Draw(SpriteBatch batch)
    {
    batch.Draw(myTexture, myPosition, null, Color.White, myRotation, Vector2.Zero,
    myScale, SpriteEffects.None, 0f);
    }
    }

    public class SpriteContentReader : ContentTypeReader<Sprite>
    {
    protected override Sprite Read(ContentReader input,
    Sprite existingInstance)
    {
    Sprite sprite = new Sprite();

    sprite.Position = input.ReadVector2();
    sprite.Rotation = input.ReadSingle();
    sprite.Scale = input.ReadVector2();
    sprite.TextureName = input.ReadString();

    sprite.LoadContent(input.ContentManager);

    return sprite;
    }
    }

    Its Reader it's exactly this way:
    public class SpriteContentReader : ContentTypeReader<Sprite>
    {
    protected override Sprite Read(ContentReader input,
    Sprite existingInstance)
    {
    Sprite sprite = new Sprite();

    sprite.Position = input.ReadVector2();
    sprite.Rotation = input.ReadSingle();
    sprite.Scale = input.ReadVector2();
    sprite.TextureName = input.ReadString();

    sprite.LoadContent(input.ContentManager);

    return sprite;
    }
    }

    And the Writer (which goes in a Content Pipeline Expansion Library:
    [ContentTypeWriter]
    public class SpriteContentWriter : ContentTypeWriter<Sprite>
    {
    protected override void Write(ContentWriter output, Sprite value)
    {
    output.Write(value.Position);
    output.Write(value.Rotation);
    output.Write(value.Scale);
    output.Write(value.TextureName);
    }

    public override string GetRuntimeReader(TargetPlatform targetPlatform)
    {
    return typeof(SpriteContentReader).AssemblyQualifiedName;
    }
    }

    The XML code for this classes:
    <?xml version="1.0" encoding="utf-8" ?>
    <XnaContent>
      <Asset Type="SpriteEngine.Graphics.Sprite">
        <Position>100 100</Position>
        <Rotation>0</Rotation>
        <Scale>1 1</Scale>
        <TextureName>001-Fighter01</TextureName>
      </Asset>
    </XnaContent>

    You can put the read/write's fields in any order, you don't need to follow the XML file's order. But the reader's fields order MUST be te same as the writer's ones.
    Shine on You Crazy Diamond
  • 1/9/2008 11:24 PM In reply to

    Re: Content Pipeline Xml Reading

    I'm confused. This code is the same as in Nick's article and doesn't answer what I was asking.

    If the Sprite class also had a property like:

    private List<SomeUserType> myList = new List<SomeUserType>();
    public List<SomeUserType> MyList
    {
       get { return myList; }
    }


    Then we'd be heading in the right direction.
  • 1/9/2008 11:28 PM In reply to

    Re: Content Pipeline Xml Reading

    You can't serialize/deserialize from an interface as far as I know. Unfortunately because you will need to know all the fields and properties of the object at both serialization and deserialization, so I don't believe there is a way to do it. If it's something you don't need to serialize/deserialize that list, you can add that ignore attribute to it and it won't be required. If it is necessary, you might want to consider making it a list of a type instead of an interface.

    Edit: I see you are mainly wondering how to serialize a List. I do that towards the end of the article. You'll be able to see how the XML is set up for a list which should show you just how it's done.
  • 1/9/2008 11:55 PM In reply to

    Re: Content Pipeline Xml Reading

    Bah! I completely forgot about the interface in regards to serialization.  Unfortunately my entire system is very interface dependant... I wouldnt be able to do a lot of the things I'm doing without adding enormous amounts of code.

    I suppose if I was in the mindset of regular old Xml serialization than I would have known what to do.  I would have needed to make the object in the list also serializable as well (pretending for a moment it wasn't an interface).  Sometimes I get caught up the "game" stuff with the new terminology and not paying attention to general programming.

    Anyhow... looks like the content pipeline system isn't going to work for me after all, at least in this case.

    If I recall, the WCF had some interesting contributions to serializing data.  I may have to go digging through some projects and see if there isn't another system I can tinker with.
  • 1/19/2008 6:23 PM In reply to

    Re: Content Pipeline Xml Reading

    Nick, in your example, the Asset type is List.  How can you load a List that is in the Asset?  I get a ContentLoadException when I try something like this:

     

    List<MyType> objects = input.ReadObject<List<MyType>>();

    Can you do a ReadObject on a List?

    It seems to read the first object of MyType just fine, but then the exception is raised after it returns from reading that first MyType object.

  • 1/21/2008 1:19 PM In reply to

    Re: Content Pipeline Xml Reading

    Is your problem in the XML serialization, or in the ContentTypeWriter/Reader part? These are different things: the XML serialization is handled using reflection, while the ContentTypeWriter/Reader are only used with the binary .xnb file, and do not affect serializing the data to XML.

    With both the XML and binary .xnb serialization, I believe it should be possible to have a collection of interfaces, just the same as how you could have a collection of polymorphic objects. The trick is to realize that you aren't actually serializing the interface, but a concrete object which implements that interface. So (assuming your problem is with the .xnb side of things) you need to have a ContentTypeWriter for each concrete type that implements your interface.
    XNA Framework Developer - blog - homepage
Page 1 of 1 (8 items) Previous Next