TextureAtlas from xml

Hi monos!

Im pretty new on monogame, c# and all this enviroment.
To start simple im trying to load a sprites sheet and add actions. From the content pipeline im only being able to add the png file but cant do it with the xml file (created with ShoeBox), it looks like this:

<TextureAtlas imagePath="sprite.png">
<SubTexture name="sprite_01.png" x="568" y="959" width="66" height="75"/>
<SubTexture name="sprite_02.png" x="636" y="981" width="66" height="76"/>
<SubTexture name="sprite_03.png" x="498" y="1416" width="68" height="78"/>
<SubTexture name="sprite_04.png" x="497" y="1264" width="69" height="79"/>
</TextureAtlas>

So I went doing it manually as follows:

protected override void LoadContent()
    {
        spriteBatch = new SpriteBatch(GraphicsDevice);
        var myTexture = Content.Load<Texture2D>("Sprites/sprite");
        var myAtlas = new TextureAtlas("myMap", myTexture );

        XmlDocument xdoc = new XmlDocument();
        xdoc.Load("Content/Sprites/myMap.xml");
        var texturesAtlas = xdoc.SelectNodes("TextureAtlas");
        
        foreach (XmlNode textureAtlas in texturesAtlas) {
            foreach (XmlNode frame in textureAtlas.SelectNodes("SubTexture")) {
                var name = frame.Attributes.GetNamedItem("name").Value;
                var width = Int32.Parse(frame.Attributes.GetNamedItem("width").Value);
                var height = Int32.Parse(frame.Attributes.GetNamedItem("height").Value);
                var x = Int32.Parse(frame.Attributes.GetNamedItem("x").Value);
                var y = Int32.Parse(frame.Attributes.GetNamedItem("y").Value);
                myAtlas.CreateRegion(name,x,y,width,height);
            }
        }

        var myAnimationFactory = new SpriteSheetAnimationFactory(myAtlas );
        myAnimationFactory.Add("idle", new SpriteSheetAnimationData(new[] { 0,1,2,1 }));
        myCharacter = new AnimatedSprite(myAnimationFactory);
        myCharacter.Position = new Vector2(350, 800);
        myCharacter.Play("idle").IsLooping = true;
    }

it works but im wondering if there is a better way to import the map, or to choose a xml file while creating the TextureAtlas with TextureAtlas.Create(“name”, texture, w, h);

Maybe doing a custom content importer? its seems complicated

Thanks

Actually, there is an easier way to import the whole TextureAtlas in one go. TextureAtlas has an constructor which takes as parameters Texture2D used as a base of atlas and Dictionary<string, Rectangle> which defines the regions in the atlas - that is, each image of the atlas itself.

That dictionary can be defined via XML itself and then imported with Content Pipeline, thus becoming ready to load with ContentManager.Load<T>() method. Unfortunately, ShoeBox by itself doesn’t create such XML out of the box, so you need to do some mundane work yourself.

Format of XML for atlas regions is:
<XnaContent>
<Asset Type="System.Collections.Generic.Dictionary[System.String, Microsoft.Xna.Framework.Rectangle]">
<Item><Key>RegionName</Key><Value>x y w h</Value></Item>
<!-- Add as many as needed -->
</Asset>
</XnaContent>

That way you can simply create new TextureAtlas by calling, for example,
var atlas = new TextureAtlas(contentManager.Load<Texture2D>(texturePath), contentManager.Load<Dictionary<string, Rectangle>>(regionsPath));

Also, for Monogame/XNA, instead of using ShoeBox I recommend using the following SpriteSheetPacker. Benefit is that it automatically forms you the XML supported by the TextureAtlas(Texture2D, Dictionary<string, Rectangle>) overload.

Thanks Aurioch!

It worked good with your recommendations.

Also tested changing the export command in ShoeBox to incorporate those lines.
Both ways give me the same results :smile:

Here is the ShoeBox config http://pastebin.com/MN6V9fYZ

I use https://www.codeandweb.com/texturepacker which is ok 'till now :wink: Supports MG, unity, cocos, etc.

Like with most things, it depends. The code you’ve written is fine. It works and does what you want.

However there are 2 reasons you might want to do it differently.

1. To support multiple platforms.

  `xdoc.Load("Content/Sprites/myMap.xml");`

Code like this typically won’t work on all platforms. If you want to make sure it’ll work on all platforms take a look into TitleContainer.OpenStream. It’s a bit more fiddly to setup but once you get the hang of it, there’s not much extra code involved.

The second reason you might run into problems on multiple platforms is the use of XmlDocument. This class comes from System.Xml. I could be wrong, but I don’t think that DLL is supported on all platforms.

2. To pre-process the content.

Aside from platform compatibility, the main reason you want to use the Content Pipeline is to do some pre-processing on the data. In this case there’s probably not much you can really do. The only real reason to do it is so that you’re not parsing XML directly in your game code.

I guess what I’m trying to say is, there might be a reason to change it in the future but until you’ve actually got a real reason, it’s probably not worth it.