当前位置:网站首页>Unity drawing plug-in = = [support the update of the original atlas]
Unity drawing plug-in = = [support the update of the original atlas]
2022-07-07 15:53:00 【Le_ Sam】
Preface :
Project needs ,1. It needs to be provided to the art collection tool ,2. Atlas update function , So that the subgraphs in the new atlas do not have to be dragged and positioned again
Solutions :
1.unity Call external TexturePacker The command line tool executes the composite ,unity according to TP The generated data cuts out multiple pieces of the atlas sprite
2. When dragging and positioning subgraphs in the atlas , Corresponding GUID And FileID. Therefore, the new atlas updates the corresponding GUID And FileID that will do , however API No corresponding interface found , So start with the resource itself txt Mode on , Force the replacement of the corresponding ID
Reference documents :
https://www.xuanyusong.com/archives/4207
effect :
Source code :
The following three files , Placed in the project Assets\Editor that will do
using System;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using UnityEditor;
using System.Text;
using System.Diagnostics;
using System.Linq;
using System.Xml;
using System.Collections;
using LuaFramework;
public class TexturePackerBuild : Editor
{
private const string BuildDir = "Assets/TexturePacker/ Package the atlas ";
/// <summary>
/// TexturePacker.exe ###https://www.codeandweb.com/texturepacker/documentation/texture-settings
/// </summary>
private const string Paramet = " --sheet {0}.png --data {1}.xml --format sparrow --trim-mode CropKeepPos --pack-mode Best --algorithm MaxRects --max-size 4096 --size-constraints POT --disable-rotation {2}";
/// <summary>
/// The output directory
/// </summary>
private const string OutPutDirRoot = "Assets/TPSpriteBuild/";
private static string tpDir = "";
private static string argument = "";
[MenuItem(BuildDir, false)]
public static void BuildOneTP()
{
if (Directory.Exists(OutPutDirRoot) == false)
{
Directory.CreateDirectory(OutPutDirRoot);
}
var assetPaths = Selection.assetGUIDs.Select(AssetDatabase.GUIDToAssetPath).ToList();
List<string> filePathList = new List<string>();
foreach (var assetPath in assetPaths)
{
if (AssetDatabase.IsValidFolder(assetPath))
{
string[] filesPath = Directory.GetFiles(assetPath);
foreach (var filePath in filesPath)
{
if (filePathList.Contains(filePath) == false)
{
filePathList.Add(filePath);
}
else
{
UnityEngine.Debug.LogErrorFormat(" Same file detected {0}", assetPath);
}
}
}
else
{
filePathList.Add(assetPath);
}
}
Dictionary<string, FileData> fileDic = new Dictionary<string, FileData>();
foreach (var path in filePathList)
{
var dir = Path.GetFileName(Path.GetDirectoryName(path));
var fileName = Path.GetFileName(path);
UnityEngine.Debug.LogFormat("_TP dirName {0} _TP fileName {1} _TP fullPath {2}", dir, fileName, path);
if (fileDic.ContainsKey(dir) == false)
{
FileData fileData = new FileData();
fileData.filesList = new List<string>();
fileDic[dir] = fileData;
fileDic[dir].dirName = dir;
}
fileDic[dir].filesList.Add(path);
}
foreach (var data in fileDic)
{
TexturePackerBuild tpBuild = new TexturePackerBuild();
tpBuild.Build(data.Value);
}
}
public void Build(FileData data)
{
StringBuilder sb = new StringBuilder("");
GetImageName(data.filesList, ref sb);
string sheetName = OutPutDirRoot + data.dirName;
argument = string.Format(Paramet, sheetName, sheetName, sb.ToString());
tpDir = PlayerPrefs.GetString("_TPInstallDir", "");
RunExternalApp(tpDir, argument);
}
private StringBuilder GetImageName(List<string> fileName, ref StringBuilder sb)
{
foreach (var file in fileName)
{
string extenstion = Path.GetExtension(file);
if (extenstion == ".png")
{
sb.Append(file);
sb.Append(" ");
}
}
return sb;
}
private void RunExternalApp(string command, string argument)
{
UnityEngine.Debug.Log("TPSprite Recover");
ClearOtherFiles();
ProcessStartInfo start = new ProcessStartInfo(command);
start.Arguments = argument;
start.CreateNoWindow = false;
start.ErrorDialog = true;
start.UseShellExecute = false;
start.RedirectStandardOutput = true;
start.RedirectStandardError = true;
start.RedirectStandardInput = true;
start.StandardOutputEncoding = System.Text.UTF8Encoding.UTF8;
start.StandardErrorEncoding = System.Text.UTF8Encoding.UTF8;
try
{
Process p = Process.Start(start);
p.WaitForExit();
p.Close();
AssetDatabase.Refresh();
bool isNullFile; // Judge whether the file in the output directory is empty
string outPngPath;
BuildTexturePacker(out isNullFile,out outPngPath);
if (isNullFile == false)
{
tpDir = command;
PlayerPrefs.SetString("_TPInstallDir", command);
SelectAtlasUpdataWindow(outPngPath);
}
else
{
// The output file is empty , guess exe Incorrect use , Re import exe
OnError(" No pictures were detected in the output directory , Please re import ");
}
}
catch (Exception ex)
{
OnError(ex.Message);
}
}
/// <summary>
/// Clean up the original image under the output directory
/// </summary>
private void ClearOtherFiles()
{
string[] fileName = Directory.GetFiles(OutPutDirRoot);
if (fileName != null && fileName.Length > 0)
{
for (int i = 0; i < fileName.Length; i++)
{
string extenstion = Path.GetExtension(fileName[i]);
if (extenstion == ".png" || extenstion == ".xml")
{
File.Delete(fileName[i]);
}
}
}
}
/// <summary>
/// establish Texture
/// </summary>
/// <param name="isNull"></param>
/// <param name="outPngPath"></param>
public void BuildTexturePacker(out bool isNull, out string outPngPath)
{
isNull = true;
outPngPath = "";
string[] imagePath = Directory.GetFiles(OutPutDirRoot);
foreach (string path in imagePath)
{
if (Path.GetExtension(path) == ".png" || Path.GetExtension(path) == ".PNG")
{
Texture2D texture = AssetDatabase.LoadAssetAtPath<Texture2D>(path);
string rootPath = OutPutDirRoot + texture.name;
string pngPath = rootPath + "/" + texture.name + "_" + DateTime.Now.ToString("hh_mm_ss") + ".png";
outPngPath = pngPath;
isNull = false;
if (Directory.Exists(rootPath) == false)
{
Directory.CreateDirectory(rootPath);
}
File.Copy(OutPutDirRoot + texture.name + ".png", pngPath);
AssetDatabase.Refresh();
FileStream fs = new FileStream(OutPutDirRoot + texture.name + ".xml", FileMode.Open);
StreamReader sr = new StreamReader(fs);
string jText = sr.ReadToEnd();
fs.Close();
sr.Close();
WriteMeta(jText, pngPath);
}
}
ClearOtherFiles();
AssetDatabase.Refresh();
}
void SelectAtlasUpdataWindow(string pngPath)
{
TPSelectFileWindow win = new TPSelectFileWindow();
win.Popup(pngPath, UpdataAssetRes);
}
void UpdataAssetRes(string opPath, Texture pendingTex)
{
var srcObj = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(opPath);
TPFindReplaceRes tpFindRep = new TPFindReplaceRes();
tpFindRep.OnTPReplaceAssetData(pendingTex, srcObj);
}
// Write a message to SpritesSheet in
void WriteMeta(string jText, string path)
{
UnityEngine.Debug.Log("WriteMeta " + path);
XmlDocument xml = new XmlDocument();
xml.LoadXml(jText);
XmlNodeList elemList = xml.GetElementsByTagName("SubTexture");
Texture2D texture = AssetDatabase.LoadAssetAtPath<Texture2D>(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");
}
asetImp.spritesheet = metaData;
asetImp.textureType = TextureImporterType.Sprite;
asetImp.spriteImportMode = SpriteImportMode.Multiple;
asetImp.mipmapEnabled = false;
asetImp.SaveAndReimport();
}
private string OpenTPEXE()
{
string path = EditorUtility.OpenFilePanel(" Please locate TexturePacker.exe Program , For subsequent operations ", "", "exe");
return path;
}
private void OnError(string err)
{
UnityEngine.Debug.LogError("TPBuild Err:" + err);
EditorUtility.DisplayDialog(" Please reposition TexturePacker.exe Program !", "Err:" + err, " determine ");
var path = OpenTPEXE();
if (path.Length != 0)
{
RunExternalApp(path, argument);
}
}
public class FileData
{
public string dirName;
public List<string> filesList;
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
/// <summary>
/// Editor main interface
/// </summary>
public class TPSelectFileWindow : EditorWindow {
private Action<string, Texture> _callBack;
private string _opPath;
private Texture _selectTex;
public void Popup(string opPath, Action<string, Texture> cb)
{
_callBack = cb;
_opPath = opPath;
var isSeek = EditorUtility.DisplayDialog(" Waiting for execution ...", " Whether to update the original atlas , Currently, only a single atlas can be updated ", " determine ", " Cancel ");
if (isSeek == true)
{
TPSelectFileWindow window = EditorWindow.GetWindow(typeof(TPSelectFileWindow), true, " Waiting for execution ...") as TPSelectFileWindow;
window.minSize = new Vector2(450, 300);
window.Show();
}
else
{
if (_callBack != null)
{
_callBack.Invoke(_opPath, _selectTex);
}
_callBack = null;
}
}
private void OnGUI()
{
ShowEditorGUI();
}
private void ShowEditorGUI()
{
_selectTex = EditorGUILayout.ObjectField(" Add the original bound Texture", _selectTex, typeof(Texture), true) as Texture;
if (_selectTex != null && string.IsNullOrEmpty(_selectTex.name) == false)
{
EditorGUILayout.TextField(AssetDatabase.GetAssetOrScenePath(_selectTex));
}
if (GUILayout.Button(" Close the window and continue "))
{
// close window
this.Close();
}
}
private void OnDestroy()
{
if (_callBack != null)
{
_callBack.Invoke(_opPath, _selectTex);
}
_callBack = null;
_selectTex = null;
}
}
using UnityEngine;
using UnityEngine.UI;
using UnityEditor;
using System.Collections;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Generic;
public class TPFindReplaceRes {
Dictionary<string, AssetData> assetDic;
string srcGUID;
string tarGUID;
/// <summary>
/// to update Asset data
/// </summary>
/// <param name="srcObj"> Retrieve the located OBJ, It can be discarded after operation </param>
/// <param name="tarObj"> Wait for the updated data to be clean OBJ</param>
public void OnTPReplaceAssetData(Object srcObj, Object tarObj) {
AssetDatabase.Refresh();
EditorSettings.serializationMode = SerializationMode.ForceText;
string srcPath = AssetDatabase.GetAssetPath(srcObj);
string tarPath = AssetDatabase.GetAssetPath(tarObj);
if (string.IsNullOrEmpty(tarPath) || string.IsNullOrEmpty(srcPath))
{
Debug.Log("srcObj = null || tarObj = null");
return;
}
Debug.LogFormat("srcPath:{0} , tarPath:{1}", srcPath, tarPath);
srcGUID = AssetDatabase.AssetPathToGUID(srcPath);
tarGUID = AssetDatabase.AssetPathToGUID(tarPath);
OnCreateMetaData(srcPath, tarPath);
var withoutExtensions = new List<string>() { ".prefab", ".unity", ".mat", ".asset" };
string[] files = Directory.GetFiles(Application.dataPath, "*.*", SearchOption.AllDirectories).Where(s => withoutExtensions.Contains(Path.GetExtension(s).ToLower())).ToArray();
int startIndex = 0;
EditorApplication.update = delegate () {
string file = files[startIndex];
bool isCancel = EditorUtility.DisplayCancelableProgressBar(" Matching resources ", file, (float)startIndex / (float)files.Length);
if (Regex.IsMatch(File.ReadAllText(file), srcGUID))
{
OnUpdateData(file);
var obj = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(GetRelativeAssetsPath(file));
Debug.Log(" Replacement object " + file, obj);
}
startIndex++;
if (isCancel || startIndex >= files.Length)
{
EditorUtility.ClearProgressBar();
EditorApplication.update = null;
startIndex = 0;
Debug.Log(" End of data update ");
}
};
}
void OnCreateMetaData(string srcPath, string tarPath)
{
string srcMeta = AssetDatabase.GetTextMetaFilePathFromAssetPath(srcPath);
string tarMeta = AssetDatabase.GetTextMetaFilePathFromAssetPath(tarPath);
string[] srcFile = File.ReadAllLines(srcMeta);
string[] tarFile = File.ReadAllLines(tarMeta);
List<string> smList = new List<string>();
List<string> tempList = new List<string>();
TextureImporter srcImp = TextureImporter.GetAtPath(srcPath) as TextureImporter;
TextureImporter tarImp = TextureImporter.GetAtPath(tarPath) as TextureImporter;
foreach (var spMD in tarImp.spritesheet)
{
tempList.Add(spMD.name);
}
foreach (var spMD in srcImp.spritesheet)
{
if (tempList.Contains(spMD.name))
{
smList.Add(spMD.name);
//Debug.Log(" matching SpriteSheetMeta " + spMeta.name);
}
}
assetDic = new Dictionary<string, AssetData>();
foreach (var key in smList)
{
AssetData data = new AssetData();
data.name = key;
assetDic.Add(key, data);
foreach (var srcStr in srcFile)
{
if (srcStr.IndexOf(key) >= 0)
{
var str = srcStr.Trim();
var spMetaId = str.Substring(0, str.IndexOf(":"));
//Debug.LogFormat("src ==> {0} {1}", key, spMetaId);
if (string.IsNullOrEmpty(assetDic[key].src))
{
assetDic[key].src = spMetaId;
}
}
}
foreach (var tarStr in tarFile)
{
if (tarStr.IndexOf(key) >= 0)
{
var str = tarStr.Trim();
var spMetaId = str.Substring(0, str.IndexOf(":"));
//Debug.LogFormat("tar ==> {0} {1}", key, spMetaId);
if (string.IsNullOrEmpty(assetDic[key].tar))
{
assetDic[key].tar = spMetaId;
}
}
}
if (string.IsNullOrEmpty(assetDic[key].src) || string.IsNullOrEmpty(assetDic[key].tar))
{
assetDic.Remove(key);
}
}
//foreach (var meta in assetDic)
//{
// var data = meta.Value;
// Debug.LogFormat("meta ==> name:{0} src:{1} tar:{2}", data.name, data.src, data.tar);
//}
}
void OnUpdateData(string file)
{
var allInfo = File.ReadAllLines(file);
for (int i = 0; i < allInfo.Length; i++)
{
var str = allInfo[i];
if (str.IndexOf(srcGUID) >= 0)
{
if (str.IndexOf("guid") >= 0)
{
// Replace old GUID
str = str.Replace(srcGUID, tarGUID);
// Replace old fileID
if (str.IndexOf("fileID") >= 0)
{
foreach (var meta in assetDic)
{
if (str.IndexOf(meta.Value.src) >= 0)
{
str = str.Replace(meta.Value.src, meta.Value.tar);
}
}
}
}
allInfo[i] = str;
}
}
File.WriteAllLines(file, allInfo);
}
static string GetRelativeAssetsPath(string path) {
return "Assets" + Path.GetFullPath(path).Replace(Path.GetFullPath(Application.dataPath), "").Replace('\\', '/');
}
public class AssetData
{
public string name;
public string src;
public string tar;
}
}
边栏推荐
- Points for attention in porting gd32 F4 series programs to gd32 F3 series
- Tkinter after how to refresh data and cancel refreshing
- Cocos uses custom material to display problems
- 持续创作,还得靠它!
- Iterator and for of.. loop
- A JS script can be directly put into the browser to perform operations
- unnamed prototyped parameters not allowed when body is present
- [quick start of Digital IC Verification] 18. Basic grammar of SystemVerilog learning 5 (concurrent threads... Including practical exercises)
- LeetCode2_ Add two numbers
- How to understand that binary complement represents negative numbers
猜你喜欢
Write sequence frame animation with shader
Virtual memory, physical memory /ram what
UE4 exports the picture + text combination diagram through ucanvasrendertarget2d
webgl_ Enter the three-dimensional world (2)
Wireless sensor networks -- ZigBee and 6LoWPAN
[wechat applet] Chapter (5): basic API interface of wechat applet
Monthly observation of internet medical field in May 2022
Three. JS introductory learning notes 11:three JS group composite object
Cocos creator collision and collision callback do not take effect
[quick start of Digital IC Verification] 25. AHB sramc of SystemVerilog project practice (5) (AHB key review, key points refining)
随机推荐
【微信小程序】Chapter(5):微信小程序基础API接口
Clang compile link ffmpeg FAQ
webgl_ Enter the three-dimensional world (1)
OpenGL's distinction and understanding of VAO, VBO and EBO
XMIND frame drawing tool
Unity的三种单例模式(饿汉,懒汉,MonoBehaviour)
Using eating in cocos Creator
After UE4 is packaged, mesh has no material problem
[quick start of Digital IC Verification] 19. Basic grammar of SystemVerilog learning 6 (thread internal communication... Including practical exercises)
C4D learning notes 1- animation - animation key frames
./ Functions of configure, make and make install
Use moviepy Editor clips videos and intercepts video clips in batches
unnamed prototyped parameters not allowed when body is present
numpy--数据清洗
Monthly observation of internet medical field in May 2022
融云斩获 2022 中国信创数字化办公门户卓越产品奖!
Actually changed from 408 to self proposition! 211 North China Electric Power University (Beijing)
Do not use memset to clear floating-point numbers
有钱人买房就是不一样
When opening the system window under UE4 shipping, the problem of crash is attached with the plug-in download address