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的一些命令行参数。
下面我们写代码来用上这些命令行参数。写代码之前先看看我们要打成图集的小图们:
好的,上代码:
#if UNITY_EDITOR
using UnityEngine;
using System.IO;
using UnityEditor;
using System.Text;
using System.Diagnostics;
public class CommandBuild : Editor
{
[MenuItem("Tools/SpritesPacker/CommandBuild")]
public static void BuildTexturePacker()
{
//选择并设置TP命令行的参数和参数值
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}" ;
string inputPath = string.Format ("{0}/Images", Application.dataPath);//小图目录
string outputPath = string.Format ("{0}/TexturePacker", Application.dataPath);//用TP打包好的图集存放目录
string[] imagePath = Directory.GetDirectories (inputPath);
for (int i = 0; i < imagePath.Length; i++)
{
UnityEngine.Debug.Log (imagePath [i]);
StringBuilder sb = new StringBuilder("");
string[] fileName = Directory.GetFiles(imagePath[i]);
for (int j = 0; j < fileName.Length; j++)
{
string extenstion = Path.GetExtension(fileName[j]);
if (extenstion == ".png")
{
sb.Append(fileName[j]);
sb.Append(" ");
}
UnityEngine.Debug.Log("fileName [j]:" + fileName[j]);
}
string name = Path.GetFileName(imagePath [i]);
string outputName = string.Format ("{0}/TexturePacker/{1}/{2}", Application.dataPath,name,name);
string sheetName = string.Format("{0}/SheetsByTP/{1}", Application.dataPath, name);
//执行命令行
processCommand("D:\\Program Files (x86)\\CodeAndWeb\\TexturePacker\\bin\\TexturePacker.exe", string.Format(commandText, sheetName, sheetName, sb.ToString()));
}
AssetDatabase.Refresh();
}
private static void processCommand(string command, string argument)
{
ProcessStartInfo start = new ProcessStartInfo(command);
start.Arguments = argument;
start.CreateNoWindow = false;
start.ErrorDialog = true;
start.UseShellExecute = false;
if(start.UseShellExecute){
start.RedirectStandardOutput = false;
start.RedirectStandardError = false;
start.RedirectStandardInput = false;
} else{
start.RedirectStandardOutput = true;
start.RedirectStandardError = true;
start.RedirectStandardInput = true;
start.StandardOutputEncoding = System.Text.UTF8Encoding.UTF8;
start.StandardErrorEncoding = System.Text.UTF8Encoding.UTF8;
}
Process p = Process.Start(start);
if(!start.UseShellExecute)
{
UnityEngine.Debug.Log(p.StandardOutput.ReadToEnd());
UnityEngine.Debug.Log(p.StandardError.ReadToEnd());
}
p.WaitForExit();
p.Close();
}
}
#endif
运行 Tools/SpritesPacker/CommandBuild,并查看SheetsByTP目录下打包好的图集:
发现得到了两个文件,一个就是打包好的大图,及一个XML格式的配置文件:
但这样子的图集,unity并不能用,还需要做进一步的处理。这里先说下原理。在Unity里,一张图片的格式很多
不同格式有着不同的用途(UGUI用的是Sprite的格式),当我们往Unity里导入图片时,这张图片是什么格式由TextureImporter类决定,大家可以去看看这个类的API。然后我们再在Unity里随便找到一张图片的 .meta文件,用你喜欢的文本编辑器打开它:
fileFormatVersion: 2
guid: 542eed357a373ac4186621aa69a5ae78
timeCreated: 1456884951
licenseType: Pro
TextureImporter:
fileIDToRecycleName: {}
serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 1
linearTexture: 0
correctGamma: 0
fadeOut: 0
borderMipMap: 0
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: .25
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
generateCubemap: 0
cubemapConvolution: 0
cubemapConvolutionSteps: 8
cubemapConvolutionExponent: 1.5
seamlessCubemap: 0
textureFormat: -1
maxTextureSize: 2048
textureSettings:
filterMode: -1
aniso: -1
mipBias: -1
wrapMode: -1
nPOTScale: 1
lightmap: 0
rGBM: 0
compressionQuality: 50
allowsAlphaSplitting: 0
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: .5, y: .5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaIsTransparency: 0
textureType: -1
buildTargetSettings: []
spriteSheet:
sprites: []
spritePackingTag:
userData:
assetBundleName:
assetBundleVariant:
里边有个spriteSheet的项,在 TextureImporter的API里对应
发现是一个SpriteMetaData的数组。在看看SpriteMetaData有什么
我们只要把这些信息填好就行。上代码:
#if UNITY_EDITOR
using UnityEngine;
using System;
using System.IO;
using UnityEditor;
using System.Collections.Generic;
using System.Xml;
public class MySpritesPacker : Editor
{
[MenuItem("Tools/SpritesPacker/TexturePacker")]
public static void BuildTexturePacker()
{
string inputPath = string.Format("{0}/SheetsByTP/", Application.dataPath);
string[] imagePath = Directory.GetFiles(inputPath);
foreach (string path in imagePath)
{
if (Path.GetExtension(path) == ".png" || Path.GetExtension(path) == ".PNG")
{
string sheetPath = GetAssetPath(path);
Texture2D texture = AssetDatabase.LoadAssetAtPath(sheetPath);
Debug.Log(texture.name);
string rootPath = string.Format("{0}/TexturePacker/{1}", Application.dataPath,texture.name);
string pngPath = rootPath + "/" + texture.name + ".png";
TextureImporter asetImp = null;
Dictionary tIpterMap = new Dictionary();
if (Directory.Exists(rootPath))
{
if(File.Exists(pngPath))
{
Debug.Log("exite: " + pngPath);
asetImp = GetTextureIpter(pngPath);
SaveBoreder(tIpterMap, asetImp);
File.Delete(pngPath);
}
File.Copy(inputPath + texture.name + ".png", pngPath);
}
else
{
Directory.CreateDirectory(rootPath);
File.Copy(inputPath + texture.name + ".png", pngPath);
}
AssetDatabase.Refresh();
FileStream fs = new FileStream(inputPath + texture.name + ".xml", FileMode.Open);
StreamReader sr = new StreamReader(fs);
string jText = sr.ReadToEnd();
fs.Close();
sr.Close();
XmlDocument xml = new XmlDocument();
xml.LoadXml(jText);
XmlNodeList elemList = xml.GetElementsByTagName("SubTexture");
WriteMeta(elemList, texture.name, tIpterMap);
}
}
AssetDatabase.Refresh();
}
//如果这张图集已经拉好了9宫格,需要先保存起来
static void SaveBoreder(Dictionary tIpterMap,TextureImporter tIpter)
{
for(int i = 0,size = tIpter.spritesheet.Length; i < size; i++)
{
tIpterMap.Add(tIpter.spritesheet[i].name, tIpter.spritesheet[i].border);
}
}
static TextureImporter GetTextureIpter(Texture2D texture)
{
TextureImporter textureIpter = null;
string impPath = AssetDatabase.GetAssetPath(texture);
textureIpter = TextureImporter.GetAtPath(impPath) as TextureImporter;
return textureIpter;
}
static TextureImporter GetTextureIpter(string path)
{
TextureImporter textureIpter = null;
Texture2D textureOrg = AssetDatabase.LoadAssetAtPath(GetAssetPath(path));
string impPath = AssetDatabase.GetAssetPath(textureOrg);
textureIpter = TextureImporter.GetAtPath(impPath) as TextureImporter;
return textureIpter;
}
//写信息到SpritesSheet里
static void WriteMeta(XmlNodeList elemList, string sheetName,Dictionary borders)
{
string path = string.Format("Assets/TexturePacker/{0}/{1}.png", sheetName, sheetName);
Texture2D texture = AssetDatabase.LoadAssetAtPath (path);
string impPath = AssetDatabase.GetAssetPath(texture);
TextureImporter asetImp = TextureImporter.GetAtPath(impPath) as TextureImporter;
SpriteMetaData[] metaData = new SpriteMetaData[elemList.Count];
for (int i = 0, size = elemList.Count; i < size; i++)
{
XmlElement node = (XmlElement)elemList.Item(i);
Rect rect = new Rect();
rect.x = int.Parse(node.GetAttribute("x"));
rect.y = texture.height - int.Parse(node.GetAttribute("y")) - int.Parse(node.GetAttribute("height"));
rect.width = int.Parse(node.GetAttribute("width"));
rect.height = int.Parse(node.GetAttribute("height"));
metaData[i].rect = rect;
metaData[i].pivot = new Vector2(0.5f, 0.5f);
metaData[i].name = node.GetAttribute("name");
if (borders.ContainsKey(metaData[i].name))
{
metaData[i].border = borders[metaData[i].name];
}
}
asetImp.spritesheet = metaData;
asetImp.textureType = TextureImporterType.Sprite;
asetImp.spriteImportMode = SpriteImportMode.Multiple;
asetImp.mipmapEnabled = false;
asetImp.SaveAndReimport();
}
static string GetAssetPath(string path)
{
string[] seperator = { "Assets" };
string p = "Assets" + path.Split(seperator, StringSplitOptions.RemoveEmptyEntries)[1];
return p;
}
}
internal class TextureIpter
{
public string spriteName = "";
public Vector4 border = new Vector4();
public TextureIpter() { }
public TextureIpter(string spriteName, Vector4 border)
{
this.spriteName = spriteName;
this.border = border;
}
}
#endif
然后点击 Tools/SpritesPacker/TexturePacker,看看 TexturePacker文件夹,会发现
这样Unity就用了,为了验证能用,我们把原来的小图和在TP里打包的图集都删掉,然后创建一些Image,用上我们打包好的图集里的图片。
到此就成功地在Unity用上了用TP打包好的图集了。
图片用到的九宫格信息也在这里哦!
-----------------------------------------------------------修改相关bug-----------------------------------------------------
在上文代码中
using System.IO;
using UnityEditor;
using System.Collections.Generic;
using System.Xml;
public class MySpritesPacker : Editor
{
[MenuItem("Tools/SpritesPacker/TexturePacker")]
public static void BuildTexturePacker()
{
string inputPath = string.Format("{0}/SheetsByTP/", Application.dataPath);
string[] imagePath = Directory.GetFiles(inputPath);
foreach (string path in imagePath)
{
if (Path.GetExtension(path) == ".png" || Path.GetExtension(path) == ".PNG")
{
string sheetPath = GetAssetPath(path);
Texture2D texture = AssetDatabase.LoadAssetAtPath(sheetPath);
Debug.Log(texture.name);
string rootPath = string.Format("{0}/LuaFramework/Art/Atlas/{1}", Application.dataPath, texture.name);
string pngPath = rootPath + "/" + texture.name + ".png";
//TextureImporter asetImp = null;
Dictionary tIpterMap = new Dictionary();
if (Directory.Exists(rootPath))
{
if (File.Exists(pngPath))
{
Debug.Log("exite: " + pngPath);
//因为用TexturePacker打图集的时候软件并不会记录原图的九宫信息,所以保存的九宫信息都是默认的(0,0,0,0),此处注释掉,在生成图集meta文件的时候重新添加九宫信息
//asetImp = GetTextureIpter(pngPath);
//SaveBoreder(tIpterMap, asetImp);
File.Delete(pngPath);
}
File.Copy(inputPath + texture.name + ".png", pngPath);
}
else
{
Directory.CreateDirectory(rootPath);
File.Copy(inputPath + texture.name + ".png", pngPath);
}
上文中保存的是打出图集的九宫切图,原图各个小图的九宫切图信息丢失
故在生成图集的meta文件的时候去读取了原图的meta文件的九宫切图信息,添加到图集的meta文件中(图集的meta文件中有记录各个文件的信息的数组)
//写信息到SpritesSheet里
static void WriteMeta(XmlNodeList elemList, string sheetName, Dictionary borders)
{
string path = string.Format("Assets/LuaFramework/Art/Atlas/{0}/{1}.png", sheetName, sheetName);
Texture2D texture = AssetDatabase.LoadAssetAtPath(path);
string impPath = AssetDatabase.GetAssetPath(texture);
TextureImporter asetImp = TextureImporter.GetAtPath(impPath) as TextureImporter;
SpriteMetaData[] metaData = new SpriteMetaData[elemList.Count];
for (int i = 0, size = elemList.Count; i < size; i++)
{
XmlElement node = (XmlElement)elemList.Item(i);
Rect rect = new Rect();
rect.x = int.Parse(node.GetAttribute("x"));
rect.y = texture.height - int.Parse(node.GetAttribute("y")) - int.Parse(node.GetAttribute("height"));
rect.width = int.Parse(node.GetAttribute("width"));
rect.height = int.Parse(node.GetAttribute("height"));
metaData[i].rect = rect;
metaData[i].pivot = new Vector2(0.5f, 0.5f);
metaData[i].name = node.GetAttribute("name");
//读取源文件的meta文件,获取spriteBorder九宫信息,写进图集中
string _path = string.Format("{0}/LuaFramework/Art/{1}/{2}.png", Application.dataPath,sheetName, node.GetAttribute("name"));
Vector4 _border = GetTextureIpter(_path).spriteBorder;
//if (borders.ContainsKey(metaData[i].name))
//{
metaData[i].border = _border;
//}
}
asetImp.spritesheet = metaData;
asetImp.textureType = TextureImporterType.Sprite;
asetImp.spriteImportMode = SpriteImportMode.Multiple;
asetImp.mipmapEnabled = false;
asetImp.SaveAndReimport();
}
下图是原图的九宫切图信息
下图是图集meta文件中各个小图的信息