Importing XML Spritesheets (Starling TextureAtlas) into Unity
2026 note
I’m in the process of migrating posts from my old blog into this new system.
Rather than rewriting everything, I’m keeping the original structure and tone where possible, and adding light context where it helps. This post originally spanned two entries written in 2014 and 2015. They’re presented together here, in chronological order, with minimal changes.
An update since the post below, a recent pull request has fixed it for later unity versions, so the code below is more for historical purposes.
2015 update: turning the script into a small tool
By 2015, I had cleaned this script up and published it as a small standalone project, along with a short update video.
The code was moved into a GitHub repository so it was easier to reuse and iterate on:
https://github.com/toxicFork/Unity3D-TextureAtlasSlicer
If you’re just here to grab something that works, this version is the one to start from.
Original post (2014)
Starling Framework spritesheets are described using TextureAtlas XML files.
I received an asset pack from Kenney that included a large number of spritesheets. The spritesheets were packed very tightly and unevenly, but conveniently shipped with matching XML files.
Since Unity was my main development environment, I wanted a simple way to import these spritesheets without manually defining dozens of rectangles.
So I wrote a small editor script to do exactly that.
Video walkthrough
The problem
Unity’s built-in slicing tools work well when:
sprites are evenly spaced
padding is consistent
or a clean grid can be inferred
These spritesheets did not meet any of those criteria.
Auto slicing fails.
Grid slicing fails.
Luckily, the XML already contains everything we need.
Example TextureAtlas XML
<TextureAtlas imagePath="sheet.png">
<SubTexture name="barHorizontal_blue_left.png" x="400" y="78" width="6" height="26"/>
<SubTexture name="barHorizontal_blue_mid.png" x="388" y="420" width="16" height="26"/>
<SubTexture name="squareWhite.png" x="384" y="476" width="19" height="26"/>
</TextureAtlas>Manually recreating these rectangles inside Unity would be painful, so instead we parse this XML and generate the sprite metadata automatically.
Implementation overview
The script:
Adds a context menu item to the
TextureImporterinspectorOpens a small configuration window
Reads the TextureAtlas XML
Converts Starling coordinates to Unity coordinates
Applies the generated slices
Reimports the texture
If you just want the script, feel free to scroll down.
Coordinate conversion
One important detail:
Starling XML uses a top-left origin
Unity textures use a bottom-left origin
So the Y coordinate must be flipped:
unityY = textureHeight - (xmlY + height)Editor script
Place the following script inside an Editor/ folder in your project.
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using UnityEditor;
using UnityEngine;
public class TextureAtlasSlicer : EditorWindow
{
private TextureImporter importer;
private TextAsset xmlAsset;
private SpriteAlignment alignment = SpriteAlignment.Center;
private Vector2 customPivot = new Vector2(0.5f, 0.5f);
[MenuItem("CONTEXT/TextureImporter/Slice using TextureAtlas XML", true)]
private static bool Validate(TextureImporter importer)
{
return importer != null && importer.textureType == TextureImporterType.Sprite;
}
[MenuItem("CONTEXT/TextureImporter/Slice using TextureAtlas XML")]
private static void Open(MenuCommand command)
{
var importer = command.context as TextureImporter;
if (importer == null) return;
var window = GetWindow<TextureAtlasSlicer>(true, "TextureAtlas Slicer", true);
window.importer = importer;
window.minSize = new Vector2(420, 170);
window.Show();
}
private void OnGUI()
{
xmlAsset = (TextAsset)EditorGUILayout.ObjectField("TextureAtlas XML", xmlAsset, typeof(TextAsset), false);
alignment = (SpriteAlignment)EditorGUILayout.EnumPopup("Pivot", alignment);
if (alignment == SpriteAlignment.Custom)
{
customPivot = EditorGUILayout.Vector2Field("Custom Pivot", customPivot);
}
GUILayout.FlexibleSpace();
if (xmlAsset != null && GUILayout.Button("Slice"))
{
Slice();
Close();
}
}
private void Slice()
{
var texturePath = importer.assetPath;
var texture = AssetDatabase.LoadAssetAtPath<Texture2D>(texturePath);
var doc = new XmlDocument();
doc.LoadXml(xmlAsset.text);
var nodes = doc.SelectNodes("TextureAtlas/SubTexture");
var metas = new List<SpriteMetaData>();
foreach (XmlNode node in nodes)
{
var name = node.Attributes["name"].Value;
var x = int.Parse(node.Attributes["x"].Value);
var y = int.Parse(node.Attributes["y"].Value);
var w = int.Parse(node.Attributes["width"].Value);
var h = int.Parse(node.Attributes["height"].Value);
var rect = new Rect(x, texture.height - (y + h), w, h);
metas.Add(new SpriteMetaData
{
name = Path.GetFileNameWithoutExtension(name),
rect = rect,
alignment = (int)alignment,
pivot = alignment == SpriteAlignment.Custom ? customPivot : Vector2.zero
});
}
importer.spriteImportMode = SpriteImportMode.Multiple;
importer.spritesheet = metas.ToArray();
AssetDatabase.ImportAsset(texturePath, ImportAssetOptions.ForceUpdate);
}
}That’s it. If you already have XML metadata for a spritesheet, this avoids a lot of manual slicing work.