Created
April 21, 2016 03:02
-
-
Save hwei/2d950bbd49d4046e199c328f6d507044 to your computer and use it in GitHub Desktop.
Replace Unity sprite packing with custom atlas.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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