Unity中使用TexturePacker優化圖集

轉自:https://blog.csdn.net/Happy_zailing/article/details/87190150

TexturePacker是一款非常牛逼的打圖集軟件,是一款收費軟件。這是它的官網:https://www.codeandweb.com/texturepacker,大家可以下到最新版本。即便如此,網上還是有很多破解版的(雖然不是最新版的),但是已經夠用了。 
其實Unity本身也有圖集打包功能,但Unity並不想讓開發者知道圖集這個概念。開發的過程中,如果你不想知道圖集的存在,Unity完全可以幫你隱藏得很深,但其實它還在幫你打圖集。對於這種打圖集方式,我很不放心。我還是想像傳統那樣的自己打圖集,並且能看到打好的圖集在哪裏,長什麼樣,但又能被Unity所識別,即在Unity裏能用。那TP就可以派上用場了。這是我用的3.0.3版本的TP。大家可以到這裏下載 
:http://pan.baidu.com/s/1bgjlHs。關於TP的用法,本文並不做介紹。本文要做的就是利用TP的命令行來實現完全自動化的圖集打包。 
打開命令行工具並CD到TexturePacker的安裝目錄,輸入 TexturePacker –help,會出現該版本的TP的一些命令行參數。 

è¿éåå¾çæè¿°

下面我們寫代碼來用上這些命令行參數。寫代碼之前先看看我們要打成圖集的小圖們:

è¿éåå¾çæè¿°

好的,上代碼:


 
  1. #if UNITY_EDITOR

  2. using UnityEngine;

  3. using System.IO;

  4. using UnityEditor;

  5. using System.Text;

  6. using System.Diagnostics;

  7. public class CommandBuild : Editor

  8. {

  9.  
  10. [MenuItem("Tools/SpritesPacker/CommandBuild")]

  11. public static void BuildTexturePacker()

  12. {

  13. //選擇並設置TP命令行的參數和參數值

  14. string commandText = " --sheet {0}.png --data {1}.xml --format sparrow --trim-mode None --pack-mode Best --algorithm MaxRects --max-size 2048 --size-constraints POT --disable-rotation --scale 1 {2}" ;

  15. string inputPath = string.Format ("{0}/Images", Application.dataPath);//小圖目錄

  16. string outputPath = string.Format ("{0}/TexturePacker", Application.dataPath);//用TP打包好的圖集存放目錄

  17. string[] imagePath = Directory.GetDirectories (inputPath);

  18. for (int i = 0; i < imagePath.Length; i++)

  19. {

  20. UnityEngine.Debug.Log (imagePath [i]);

  21. StringBuilder sb = new StringBuilder("");

  22. string[] fileName = Directory.GetFiles(imagePath[i]);

  23. for (int j = 0; j < fileName.Length; j++)

  24. {

  25. string extenstion = Path.GetExtension(fileName[j]);

  26. if (extenstion == ".png")

  27. {

  28. sb.Append(fileName[j]);

  29. sb.Append(" ");

  30. }

  31. UnityEngine.Debug.Log("fileName [j]:" + fileName[j]);

  32. }

  33. string name = Path.GetFileName(imagePath [i]);

  34. string outputName = string.Format ("{0}/TexturePacker/{1}/{2}", Application.dataPath,name,name);

  35. string sheetName = string.Format("{0}/SheetsByTP/{1}", Application.dataPath, name);

  36. //執行命令行

  37. processCommand("D:\\Program Files (x86)\\CodeAndWeb\\TexturePacker\\bin\\TexturePacker.exe", string.Format(commandText, sheetName, sheetName, sb.ToString()));

  38. }

  39. AssetDatabase.Refresh();

  40. }

  41.  
  42. private static void processCommand(string command, string argument)

  43. {

  44. ProcessStartInfo start = new ProcessStartInfo(command);

  45. start.Arguments = argument;

  46. start.CreateNoWindow = false;

  47. start.ErrorDialog = true;

  48. start.UseShellExecute = false;

  49.  
  50. if(start.UseShellExecute){

  51. start.RedirectStandardOutput = false;

  52. start.RedirectStandardError = false;

  53. start.RedirectStandardInput = false;

  54. } else{

  55. start.RedirectStandardOutput = true;

  56. start.RedirectStandardError = true;

  57. start.RedirectStandardInput = true;

  58. start.StandardOutputEncoding = System.Text.UTF8Encoding.UTF8;

  59. start.StandardErrorEncoding = System.Text.UTF8Encoding.UTF8;

  60. }

  61.  
  62. Process p = Process.Start(start);

  63. if(!start.UseShellExecute)

  64. {

  65. UnityEngine.Debug.Log(p.StandardOutput.ReadToEnd());

  66. UnityEngine.Debug.Log(p.StandardError.ReadToEnd());

  67. }

  68.  
  69. p.WaitForExit();

  70. p.Close();

  71. }

  72.  
  73. }

  74. #endif


運行 Tools/SpritesPacker/CommandBuild,並查看SheetsByTP目錄下打包好的圖集:

è¿éåå¾çæè¿°

發現得到了兩個文件,一個就是打包好的大圖,及一個XML格式的配置文件:

è¿éåå¾çæè¿°

è¿éåå¾çæè¿°
但這樣子的圖集,unity並不能用,還需要做進一步的處理。這裏先說下原理。在Unity裏,一張圖片的格式很多

不同格式有着不同的用途(UGUI用的是Sprite的格式),當我們往Unity裏導入圖片時,這張圖片是什麼格式由TextureImporter類決定,大家可以去看看這個類的API。然後我們再在Unity裏隨便找到一張圖片的 .meta文件,用你喜歡的文本編輯器打開它:


 
  1. fileFormatVersion: 2

  2. guid: 542eed357a373ac4186621aa69a5ae78

  3. timeCreated: 1456884951

  4. licenseType: Pro

  5. TextureImporter:

  6. fileIDToRecycleName: {}

  7. serializedVersion: 2

  8. mipmaps:

  9. mipMapMode: 0

  10. enableMipMap: 1

  11. linearTexture: 0

  12. correctGamma: 0

  13. fadeOut: 0

  14. borderMipMap: 0

  15. mipMapFadeDistanceStart: 1

  16. mipMapFadeDistanceEnd: 3

  17. bumpmap:

  18. convertToNormalMap: 0

  19. externalNormalMap: 0

  20. heightScale: .25

  21. normalMapFilter: 0

  22. isReadable: 0

  23. grayScaleToAlpha: 0

  24. generateCubemap: 0

  25. cubemapConvolution: 0

  26. cubemapConvolutionSteps: 8

  27. cubemapConvolutionExponent: 1.5

  28. seamlessCubemap: 0

  29. textureFormat: -1

  30. maxTextureSize: 2048

  31. textureSettings:

  32. filterMode: -1

  33. aniso: -1

  34. mipBias: -1

  35. wrapMode: -1

  36. nPOTScale: 1

  37. lightmap: 0

  38. rGBM: 0

  39. compressionQuality: 50

  40. allowsAlphaSplitting: 0

  41. spriteMode: 0

  42. spriteExtrude: 1

  43. spriteMeshType: 1

  44. alignment: 0

  45. spritePivot: {x: .5, y: .5}

  46. spriteBorder: {x: 0, y: 0, z: 0, w: 0}

  47. spritePixelsToUnits: 100

  48. alphaIsTransparency: 0

  49. textureType: -1

  50. buildTargetSettings: []

  51. spriteSheet:

  52. sprites: []

  53. spritePackingTag:

  54. userData:

  55. assetBundleName:

  56. assetBundleVariant:


裏邊有個spriteSheet的項,在 TextureImporter的API裏對應 

è¿éåå¾çæè¿°
發現是一個SpriteMetaData的數組。在看看SpriteMetaData有什麼

è¿éåå¾çæè¿°

我們只要把這些信息填好就行。上代碼:


 
  1. #if UNITY_EDITOR

  2. using UnityEngine;

  3. using System;

  4. using System.IO;

  5. using UnityEditor;

  6. using System.Collections.Generic;

  7. using System.Xml;

  8. public class MySpritesPacker : Editor

  9. {

  10. [MenuItem("Tools/SpritesPacker/TexturePacker")]

  11. public static void BuildTexturePacker()

  12. {

  13. string inputPath = string.Format("{0}/SheetsByTP/", Application.dataPath);

  14. string[] imagePath = Directory.GetFiles(inputPath);

  15. foreach (string path in imagePath)

  16. {

  17. if (Path.GetExtension(path) == ".png" || Path.GetExtension(path) == ".PNG")

  18. {

  19. string sheetPath = GetAssetPath(path);

  20. Texture2D texture = AssetDatabase.LoadAssetAtPath<Texture2D>(sheetPath);

  21. Debug.Log(texture.name);

  22. string rootPath = string.Format("{0}/TexturePacker/{1}", Application.dataPath,texture.name);

  23. string pngPath = rootPath + "/" + texture.name + ".png";

  24. TextureImporter asetImp = null;

  25. Dictionary<string, Vector4> tIpterMap = new Dictionary<string,Vector4>();

  26. if (Directory.Exists(rootPath))

  27. {

  28. if(File.Exists(pngPath))

  29. {

  30. Debug.Log("exite: " + pngPath);

  31. asetImp = GetTextureIpter(pngPath);

  32. SaveBoreder(tIpterMap, asetImp);

  33. File.Delete(pngPath);

  34. }

  35. File.Copy(inputPath + texture.name + ".png", pngPath);

  36. }

  37. else

  38. {

  39. Directory.CreateDirectory(rootPath);

  40. File.Copy(inputPath + texture.name + ".png", pngPath);

  41. }

  42. AssetDatabase.Refresh();

  43. FileStream fs = new FileStream(inputPath + texture.name + ".xml", FileMode.Open);

  44. StreamReader sr = new StreamReader(fs);

  45. string jText = sr.ReadToEnd();

  46. fs.Close();

  47. sr.Close();

  48. XmlDocument xml = new XmlDocument();

  49. xml.LoadXml(jText);

  50. XmlNodeList elemList = xml.GetElementsByTagName("SubTexture");

  51. WriteMeta(elemList, texture.name, tIpterMap);

  52. }

  53. }

  54. AssetDatabase.Refresh();

  55. }

  56. //如果這張圖集已經拉好了9宮格,需要先保存起來

  57. static void SaveBoreder(Dictionary<string,Vector4> tIpterMap,TextureImporter tIpter)

  58. {

  59. for(int i = 0,size = tIpter.spritesheet.Length; i < size; i++)

  60. {

  61. tIpterMap.Add(tIpter.spritesheet[i].name, tIpter.spritesheet[i].border);

  62. }

  63. }

  64.  
  65. static TextureImporter GetTextureIpter(Texture2D texture)

  66. {

  67. TextureImporter textureIpter = null;

  68. string impPath = AssetDatabase.GetAssetPath(texture);

  69. textureIpter = TextureImporter.GetAtPath(impPath) as TextureImporter;

  70. return textureIpter;

  71. }

  72.  
  73. static TextureImporter GetTextureIpter(string path)

  74. {

  75. TextureImporter textureIpter = null;

  76. Texture2D textureOrg = AssetDatabase.LoadAssetAtPath<Texture2D>(GetAssetPath(path));

  77. string impPath = AssetDatabase.GetAssetPath(textureOrg);

  78. textureIpter = TextureImporter.GetAtPath(impPath) as TextureImporter;

  79. return textureIpter;

  80. }

  81. //寫信息到SpritesSheet裏

  82. static void WriteMeta(XmlNodeList elemList, string sheetName,Dictionary<string,Vector4> borders)

  83. {

  84. string path = string.Format("Assets/TexturePacker/{0}/{1}.png", sheetName, sheetName);

  85. Texture2D texture = AssetDatabase.LoadAssetAtPath <Texture2D>(path);

  86. string impPath = AssetDatabase.GetAssetPath(texture);

  87. TextureImporter asetImp = TextureImporter.GetAtPath(impPath) as TextureImporter;

  88. SpriteMetaData[] metaData = new SpriteMetaData[elemList.Count];

  89. for (int i = 0, size = elemList.Count; i < size; i++)

  90. {

  91. XmlElement node = (XmlElement)elemList.Item(i);

  92. Rect rect = new Rect();

  93. rect.x = int.Parse(node.GetAttribute("x"));

  94. rect.y = texture.height - int.Parse(node.GetAttribute("y")) - int.Parse(node.GetAttribute("height"));

  95. rect.width = int.Parse(node.GetAttribute("width"));

  96. rect.height = int.Parse(node.GetAttribute("height"));

  97. metaData[i].rect = rect;

  98. metaData[i].pivot = new Vector2(0.5f, 0.5f);

  99. metaData[i].name = node.GetAttribute("name");

  100. if (borders.ContainsKey(metaData[i].name))

  101. {

  102. metaData[i].border = borders[metaData[i].name];

  103. }

  104. }

  105. asetImp.spritesheet = metaData;

  106. asetImp.textureType = TextureImporterType.Sprite;

  107. asetImp.spriteImportMode = SpriteImportMode.Multiple;

  108. asetImp.mipmapEnabled = false;

  109. asetImp.SaveAndReimport();

  110. }

  111.  
  112. static string GetAssetPath(string path)

  113. {

  114. string[] seperator = { "Assets" };

  115. string p = "Assets" + path.Split(seperator, StringSplitOptions.RemoveEmptyEntries)[1];

  116. return p;

  117. }

  118.  
  119. }

  120.  
  121. internal class TextureIpter

  122. {

  123. public string spriteName = "";

  124. public Vector4 border = new Vector4();

  125. public TextureIpter() { }

  126. public TextureIpter(string spriteName, Vector4 border)

  127. {

  128. this.spriteName = spriteName;

  129. this.border = border;

  130. }

  131. }

  132. #endif


然後點擊 Tools/SpritesPacker/TexturePacker,看看 TexturePacker文件夾,會發現

è¿éåå¾çæè¿°

è¿éåå¾çæè¿°

這樣Unity就用了,爲了驗證能用,我們把原來的小圖和在TP裏打包的圖集都刪掉,然後創建一些Image,用上我們打包好的圖集裏的圖片。 

è¿éåå¾çæè¿°

è¿éåå¾çæè¿°
到此就成功地在Unity用上了用TP打包好的圖集了。 
圖片用到的九宮格信息也在這裏哦!

-----------------------------------------------------------修改相關bug-----------------------------------------------------

在上文代碼中


 
  1. using System.IO;

  2. using UnityEditor;

  3. using System.Collections.Generic;

  4. using System.Xml;

  5. public class MySpritesPacker : Editor

  6. {

  7. [MenuItem("Tools/SpritesPacker/TexturePacker")]

  8. public static void BuildTexturePacker()

  9. {

  10. string inputPath = string.Format("{0}/SheetsByTP/", Application.dataPath);

  11. string[] imagePath = Directory.GetFiles(inputPath);

  12. foreach (string path in imagePath)

  13. {

  14. if (Path.GetExtension(path) == ".png" || Path.GetExtension(path) == ".PNG")

  15. {

  16. string sheetPath = GetAssetPath(path);

  17. Texture2D texture = AssetDatabase.LoadAssetAtPath<Texture2D>(sheetPath);

  18. Debug.Log(texture.name);

  19. string rootPath = string.Format("{0}/LuaFramework/Art/Atlas/{1}", Application.dataPath, texture.name);

  20. string pngPath = rootPath + "/" + texture.name + ".png";

  21. //TextureImporter asetImp = null;

  22. Dictionary<string, Vector4> tIpterMap = new Dictionary<string, Vector4>();

  23. if (Directory.Exists(rootPath))

  24. {

  25. if (File.Exists(pngPath))

  26. {

  27. Debug.Log("exite: " + pngPath);

  28. //因爲用TexturePacker打圖集的時候軟件並不會記錄原圖的九宮信息,所以保存的九宮信息都是默認的(0,0,0,0),此處註釋掉,在生成圖集meta文件的時候重新添加九宮信息

  29. //asetImp = GetTextureIpter(pngPath);

  30. //SaveBoreder(tIpterMap, asetImp);

  31. File.Delete(pngPath);

  32. }

  33. File.Copy(inputPath + texture.name + ".png", pngPath);

  34. }

  35. else

  36. {

  37. Directory.CreateDirectory(rootPath);

  38. File.Copy(inputPath + texture.name + ".png", pngPath);

  39. }

上文中保存的是打出圖集的九宮切圖,原圖各個小圖的九宮切圖信息丟失

故在生成圖集的meta文件的時候去讀取了原圖的meta文件的九宮切圖信息,添加到圖集的meta文件中(圖集的meta文件中有記錄各個文件的信息的數組)


 
  1. //寫信息到SpritesSheet裏

  2. static void WriteMeta(XmlNodeList elemList, string sheetName, Dictionary<string, Vector4> borders)

  3. {

  4. string path = string.Format("Assets/LuaFramework/Art/Atlas/{0}/{1}.png", sheetName, sheetName);

  5. Texture2D texture = AssetDatabase.LoadAssetAtPath<Texture2D>(path);

  6. string impPath = AssetDatabase.GetAssetPath(texture);

  7. TextureImporter asetImp = TextureImporter.GetAtPath(impPath) as TextureImporter;

  8. SpriteMetaData[] metaData = new SpriteMetaData[elemList.Count];

  9. for (int i = 0, size = elemList.Count; i < size; i++)

  10. {

  11. XmlElement node = (XmlElement)elemList.Item(i);

  12. Rect rect = new Rect();

  13. rect.x = int.Parse(node.GetAttribute("x"));

  14. rect.y = texture.height - int.Parse(node.GetAttribute("y")) - int.Parse(node.GetAttribute("height"));

  15. rect.width = int.Parse(node.GetAttribute("width"));

  16. rect.height = int.Parse(node.GetAttribute("height"));

  17. metaData[i].rect = rect;

  18. metaData[i].pivot = new Vector2(0.5f, 0.5f);

  19. metaData[i].name = node.GetAttribute("name");

  20. //讀取源文件的meta文件,獲取spriteBorder九宮信息,寫進圖集中

  21. string _path = string.Format("{0}/LuaFramework/Art/{1}/{2}.png", Application.dataPath,sheetName, node.GetAttribute("name"));

  22. Vector4 _border = GetTextureIpter(_path).spriteBorder;

  23. //if (borders.ContainsKey(metaData[i].name))

  24. //{

  25. metaData[i].border = _border;

  26. //}

  27.  
  28. }

  29. asetImp.spritesheet = metaData;

  30. asetImp.textureType = TextureImporterType.Sprite;

  31. asetImp.spriteImportMode = SpriteImportMode.Multiple;

  32. asetImp.mipmapEnabled = false;

  33. asetImp.SaveAndReimport();

  34. }

下圖是原圖的九宮切圖信息

下圖是圖集meta文件中各個小圖的信息

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章