Skip to content

Instantly share code, notes, and snippets.

@hwei
Created April 21, 2016 03:02
Show Gist options
  • Save hwei/2d950bbd49d4046e199c328f6d507044 to your computer and use it in GitHub Desktop.
Save hwei/2d950bbd49d4046e199c328f6d507044 to your computer and use it in GitHub Desktop.
Replace Unity sprite packing with custom atlas.
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.IO;
public class BakeSpriteAtlas : EditorWindow
{
const string TITLE = "Bake Sprite Atlas";
[MenuItem ("Window/" + TITLE)]
static void showWindow ()
{
EditorWindow.GetWindow<BakeSpriteAtlas> (TITLE);
}
string tagName;
Texture2D[] textureArray;
Vector4[] borderArray;
bool hasBorder;
Vector2 scrollPosition;
Texture2D replaceAtlas;
string replaceFolder = "GameContent";
void OnGUI ()
{
this.tagName = EditorGUILayout.TextField ("Tag", this.tagName);
if (GUILayout.Button ("1. Search"))
{
var guids = AssetDatabase.FindAssets ("t:sprite");
var assetPathSet = new HashSet<string> ();
var textureList = new List<Texture2D> ();
var borderList = new List<Vector4> ();
this.hasBorder = false;
for (int i = 0; i < guids.Length; ++i)
{
var path = AssetDatabase.GUIDToAssetPath (guids[i]);
if (assetPathSet.Contains (path))
continue;
var importer = AssetImporter.GetAtPath (path) as TextureImporter;
if (importer == null)
continue;
if (importer.spritePackingTag != this.tagName)
continue;
textureList.Add (AssetDatabase.LoadAssetAtPath<Texture2D> (path));
borderList.Add (importer.spriteBorder);
if (importer.spriteBorder != Vector4.zero)
this.hasBorder = true;
}
this.textureArray = textureList.ToArray ();
this.borderArray = borderList.ToArray ();
}
if (this.textureArray != null && this.borderArray != null)
{
this.scrollPosition = EditorGUILayout.BeginScrollView (this.scrollPosition);
var oriColor = GUI.color;
for (int i = 0; i < this.textureArray.Length; ++i)
{
GUI.color = this.borderArray[i] == Vector4.zero ? oriColor : Color.yellow;
EditorGUILayout.ObjectField (this.textureArray[i], typeof (Texture2D), false);
}
GUI.color = oriColor;
EditorGUILayout.EndScrollView ();
if (this.hasBorder)
EditorGUILayout.LabelField ("* Yellow means it has border settings");
}
if (GUILayout.Button ("2. Output (for custom texture packing)"))
{
this.output ();
}
this.replaceAtlas = (Texture2D) EditorGUILayout.ObjectField (
"Replace Atlas", this.replaceAtlas, typeof (Texture2D), false);
this.replaceFolder = EditorGUILayout.TextField ("Replace Folder", this.replaceFolder);
if (GUILayout.Button ("3. Replace (and delete all original texures)"))
{
this.replace ();
}
}
void output ()
{
var nameSet = new HashSet<string> ();
for (int i = 0; i < this.textureArray.Length; ++i)
{
var name = this.textureArray[i].name;
if (nameSet.Contains (name))
{
EditorUtility.DisplayDialog ("Output altas texture error", "Find duplicate name: " + name, "OK");
return;
}
nameSet.Add (name);
}
var dir = EditorUtility.SaveFolderPanel ("Select output directory", "", "");
for (int i = 0; i < this.textureArray.Length; ++i)
{
var texture = this.textureArray[i];
var srcPath = AssetDatabase.GetAssetPath (texture);
File.Copy (srcPath, Path.Combine (dir, Path.GetFileName (srcPath)), true);
}
}
void replace ()
{
var replaceAtlasPath = AssetDatabase.GetAssetPath (this.replaceAtlas);
{
var name2border = new Dictionary<string, Vector4> ();
for (int i = 0; i < this.textureArray.Length; ++i)
{
if (this.borderArray[i] != Vector4.zero)
{
name2border.Add (this.textureArray[i].name, this.borderArray[i]);
}
}
var replaceAtlasImporter = (TextureImporter) AssetImporter.GetAtPath (replaceAtlasPath);
var spritesheet = replaceAtlasImporter.spritesheet;
bool dirty = false;
for (int i = 0; i < spritesheet.Length; ++i)
{
Vector4 border;
if (name2border.TryGetValue (spritesheet[i].name, out border))
{
if (spritesheet[i].border != border)
{
spritesheet[i].border = border;
dirty = true;
}
}
}
if (dirty)
{
replaceAtlasImporter.spritesheet = spritesheet;
EditorUtility.SetDirty (replaceAtlasImporter);
replaceAtlasImporter.SaveAndReimport ();
Debug.Log ("Copy border settings automatically.");
}
}
var replaceAtlasGuid = AssetDatabase.AssetPathToGUID (replaceAtlasPath);
var replaceAtlasMetaLines = File.ReadAllLines (replaceAtlasPath + ".meta");
var name2fileID = new Dictionary<string, string> ();
{
int i = 0;
for (; i < replaceAtlasMetaLines.Length; ++i)
{
var l = replaceAtlasMetaLines[i];
if (l.EndsWith ("fileIDToRecycleName:"))
{
++i;
break;
}
}
int indent = 0;
string indentString;
{
var l = replaceAtlasMetaLines[i];
while (l[indent] == ' ')
++indent;
indentString = new string (' ', indent);
}
for (; i < replaceAtlasMetaLines.Length; ++i)
{
var l = replaceAtlasMetaLines[i];
if (!l.StartsWith (indentString))
break;
var colonPos = l.IndexOf (':');
var fileID = l.Substring (indent, colonPos - indent);
var name = l.Substring (colonPos + 2);
name2fileID.Add (name, fileID);
}
}
var replaceDict = new Dictionary <string, string> ();
for (int i = 0; i < this.textureArray.Length; ++i)
{
var fileID = name2fileID[this.textureArray[i].name];
var replaceTarget = string.Format ("fileID: {0}, guid: {1}", fileID, replaceAtlasGuid);
var path = AssetDatabase.GetAssetPath (this.textureArray[i]);
replaceDict.Add (AssetDatabase.AssetPathToGUID (path), replaceTarget);
}
var filePathList = new List<string> ();
getAllFilesMatchingPattern ("Assets/" + this.replaceFolder, filePathList, "*.unity");
getAllFilesMatchingPattern ("Assets/" + this.replaceFolder, filePathList, "*.prefab");
var logBuilder = new System.Text.StringBuilder ();
const string searchPattern = "fileID: 21300000, guid: ";
const int guidLenght = 32;
int replaceLength = searchPattern.Length + guidLenght;
foreach (var path in filePathList)
{
var text = File.ReadAllText (path);
System.Text.StringBuilder textBuilder = null;
for (int i = 0;; i += replaceLength)
{
i = text.IndexOf (searchPattern, i);
if (i == -1)
break;
var guid = text.Substring (i + searchPattern.Length, guidLenght);
string replaceTarget;
if (replaceDict.TryGetValue (guid, out replaceTarget))
{
if (textBuilder == null)
textBuilder = new System.Text.StringBuilder (text);
textBuilder.Remove (i, replaceLength);
textBuilder.Insert (i, replaceTarget);
}
}
if (textBuilder != null)
{
File.WriteAllText (path, textBuilder.ToString ());
logBuilder.AppendLine (path);
}
}
for (int i = 0; i < this.textureArray.Length; ++i)
{
AssetDatabase.DeleteAsset (AssetDatabase.GetAssetPath (this.textureArray[i]));
}
this.tagName = null;
this.textureArray = null;
this.borderArray = null;
this.replaceAtlas = null;
Debug.Log (logBuilder.ToString ());
}
static void getAllFilesMatchingPattern (string dir, List<string> filePathList, string searchPattern)
{
var files = Directory.GetFiles (dir, searchPattern);
filePathList.AddRange (files);
var dirs = Directory.GetDirectories (dir);
for (int i = 0; i < dirs.Length; ++i)
{
getAllFilesMatchingPattern (dirs[i], filePathList, searchPattern);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment