proto_top_down/Assets/TextureAtlasSlicer/Editor/TextureAtlasSlicer.cs

281 lines
10 KiB
C#

using System;
using System.IO;
using System.Linq;
using System.Xml;
using UnityEditor;
using UnityEngine;
public class TextureAtlasSlicer : EditorWindow {
[MenuItem("CONTEXT/TextureImporter/Slice Sprite Using XML")]
public static void SliceUsingXML(MenuCommand command) {
var textureImporter = command.context as TextureImporter;
var window = CreateInstance<TextureAtlasSlicer>();
window.importer = textureImporter;
window.ShowUtility();
}
[MenuItem("Assets/Slice Sprite Using XML")]
public static void TextureAtlasSlicerWindow() {
var window = CreateInstance<TextureAtlasSlicer>();
window.Show();
}
[MenuItem("CONTEXT/TextureImporter/Slice Sprite Using XML", true)]
public static bool ValidateSliceUsingXML(MenuCommand command) {
var textureImporter = command.context as TextureImporter;
//valid only if the texture type is 'sprite' or 'advanced'.
return textureImporter && textureImporter.textureType == TextureImporterType.Sprite ||
textureImporter.textureType == TextureImporterType.Default;
}
public TextureImporter importer;
public TextureAtlasSlicer() {
titleContent = new GUIContent("XML Slicer");
}
[SerializeField]
private TextAsset xmlAsset;
public SpriteAlignment spriteAlignment = SpriteAlignment.Center;
public Vector2 customOffset = new Vector2(0.5f, 0.5f);
public void OnSelectionChange() {
UseSelectedTexture();
}
private Texture2D selectedTexture;
private void UseSelectedTexture() {
if (Selection.objects.Length > 1) {
selectedTexture = null;
} else {
selectedTexture = Selection.activeObject as Texture2D;
}
if (selectedTexture != null) {
var assetPath = AssetDatabase.GetAssetPath(selectedTexture);
importer = AssetImporter.GetAtPath(assetPath) as TextureImporter;
if (importer) {
var extension = Path.GetExtension(assetPath);
var pathWithoutExtension = assetPath.Remove(assetPath.Length - extension.Length, extension.Length);
var xmlPath = pathWithoutExtension + ".xml";
var textAsset = AssetDatabase.LoadAssetAtPath(xmlPath, typeof (TextAsset));
if (textAsset != null) {
xmlAsset = textAsset as TextAsset;
} else {
xmlAsset = null;
subTextures = null;
}
ParseXML();
} else {
xmlAsset = null;
subTextures = null;
}
} else {
importer = null;
xmlAsset = null;
subTextures = null;
}
Repaint();
}
private SubTexture[] subTextures;
private int wantedWidth, wantedHeight;
private void ParseXML() {
try {
var document = new XmlDocument();
document.LoadXml(xmlAsset.text);
var root = document.DocumentElement;
if (root == null || root.Name != "TextureAtlas") {
return;
}
subTextures = root.ChildNodes
.Cast<XmlNode>()
.Where(childNode => childNode.Name == "SubTexture")
.Select(childNode => new SubTexture {
width = Convert.ToInt32(childNode.Attributes["width"].Value),
height = Convert.ToInt32(childNode.Attributes["height"].Value),
x = Convert.ToInt32(childNode.Attributes["x"].Value),
y = Convert.ToInt32(childNode.Attributes["y"].Value),
name = childNode.Attributes["name"].Value
}).ToArray();
wantedWidth = 0;
wantedHeight = 0;
foreach (var subTexture in subTextures) {
var right = subTexture.x + subTexture.width;
var bottom = subTexture.y + subTexture.height;
wantedWidth = Mathf.Max(wantedWidth, right);
wantedHeight = Mathf.Max(wantedHeight, bottom);
}
} catch (Exception) {
subTextures = null;
}
}
public void OnEnable() {
UseSelectedTexture();
}
public void OnGUI() {
if (importer == null) {
EditorGUILayout.LabelField("Please select a texture to slice.");
return;
}
EditorGUI.BeginDisabledGroup(focusedWindow != this);
{
EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.ObjectField("Texture", Selection.activeObject, typeof (Texture), false);
EditorGUI.EndDisabledGroup();
if (importer.textureType != TextureImporterType.Sprite &&
importer.textureType != TextureImporterType.Default) {
EditorGUILayout.LabelField("The Texture Type needs to be Sprite or Advanced!");
}
EditorGUI.BeginDisabledGroup((importer.textureType != TextureImporterType.Sprite &&
importer.textureType != TextureImporterType.Default));
{
EditorGUI.BeginChangeCheck();
xmlAsset = EditorGUILayout.ObjectField("XML Source", xmlAsset, typeof (TextAsset), false) as TextAsset;
if (EditorGUI.EndChangeCheck()) {
ParseXML();
}
spriteAlignment = (SpriteAlignment) EditorGUILayout.EnumPopup("Pivot", spriteAlignment);
EditorGUI.BeginDisabledGroup(spriteAlignment != SpriteAlignment.Custom);
EditorGUILayout.Vector2Field("Custom Offset", customOffset);
EditorGUI.EndDisabledGroup();
var needsToResizeTexture = wantedWidth > selectedTexture.width || wantedHeight > selectedTexture.height;
if (xmlAsset != null && needsToResizeTexture) {
EditorGUILayout.LabelField(
string.Format("Texture size too small."
+ " It needs to be at least {0} by {1} pixels!",
wantedWidth,
wantedHeight));
EditorGUILayout.LabelField("Try changing the Max Size property in the importer.");
}
if (subTextures == null || subTextures.Length == 0) {
EditorGUILayout.LabelField("Could not find any SubTextures in XML.");
}
EditorGUI.BeginDisabledGroup(xmlAsset == null || needsToResizeTexture || subTextures == null ||
subTextures.Length == 0);
if (GUILayout.Button("Slice")) {
PerformSlice();
}
EditorGUI.EndDisabledGroup();
}
EditorGUI.EndDisabledGroup();
}
EditorGUI.EndDisabledGroup();
}
private struct SubTexture {
public int width;
public int height;
public int x;
public int y;
public string name;
}
private void PerformSlice() {
if (importer == null) {
return;
}
var textureHeight = selectedTexture.height;
var needsUpdate = false;
if (importer.spriteImportMode != SpriteImportMode.Multiple) {
needsUpdate = true;
importer.spriteImportMode = SpriteImportMode.Multiple;
}
var wantedSpriteSheet = (from subTexture in subTextures
let actualY = textureHeight - (subTexture.y + subTexture.height)
select new SpriteMetaData {
alignment = (int) spriteAlignment,
border = new Vector4(),
name = subTexture.name,
pivot = GetPivotValue(spriteAlignment, customOffset),
rect = new Rect(subTexture.x, actualY, subTexture.width, subTexture.height)
}).ToArray();
if (!needsUpdate && !importer.spritesheet.SequenceEqual(wantedSpriteSheet)) {
needsUpdate = true;
importer.spritesheet = wantedSpriteSheet;
}
if (needsUpdate) {
EditorUtility.SetDirty(importer);
try {
AssetDatabase.StartAssetEditing();
AssetDatabase.ImportAsset(importer.assetPath);
EditorUtility.DisplayDialog("Success!", "The sprite was sliced successfully.", "OK");
} catch (Exception exception) {
Debug.LogException(exception);
EditorUtility.DisplayDialog("Error", "There was an exception while trying to reimport the image." +
"\nPlease check the console log for details.", "OK");
} finally {
AssetDatabase.StopAssetEditing();
}
} else {
EditorUtility.DisplayDialog("Nope!", "The sprite is already sliced according to this XML file.", "OK");
}
}
//SpriteEditorUtility
public static Vector2 GetPivotValue(SpriteAlignment alignment, Vector2 customOffset) {
switch (alignment) {
case SpriteAlignment.Center:
return new Vector2(0.5f, 0.5f);
case SpriteAlignment.TopLeft:
return new Vector2(0.0f, 1f);
case SpriteAlignment.TopCenter:
return new Vector2(0.5f, 1f);
case SpriteAlignment.TopRight:
return new Vector2(1f, 1f);
case SpriteAlignment.LeftCenter:
return new Vector2(0.0f, 0.5f);
case SpriteAlignment.RightCenter:
return new Vector2(1f, 0.5f);
case SpriteAlignment.BottomLeft:
return new Vector2(0.0f, 0.0f);
case SpriteAlignment.BottomCenter:
return new Vector2(0.5f, 0.0f);
case SpriteAlignment.BottomRight:
return new Vector2(1f, 0.0f);
case SpriteAlignment.Custom:
return customOffset;
default:
return Vector2.zero;
}
}
}