最近接手新项目,要本地化上渠道,所以需要reskin图片资源。但发现图集文件是未曾接触过的文件,经一番网上搜寻,发现是libgdx打包的。
参考文章:
Unity批处理生成/拆分图集_TopGames的博客-优快云博客_unity图集拆分
libGDX游戏开发之文理打包(八)_凌康ACG的博客-优快云博客_libgdx开发的游戏
(软件:gdx-texture-packer-gui;
地址:https://github.com/crashinvaders/gdx-texture-packer-gui/;
不想编译的话,直接下载release最新版)
言归正传,首先用gdx-texture-packer-gui(我下载的版本是4.10.2,不同版本可能略有差异)
将原项目的图集用自带的切图功能(Texture unpacker),分割成小图,然后增删修改对应文件,再合成一张大图集
打开.atlas文件看看内容:
dialog.png
size: 1993, 664
format: RGBA8888
filter: Nearest, Nearest
repeat: none
1_star
rotate: false
xy: 1754, 99
size: 111, 103
orig: 111, 103
offset: 0, 0
index: -1
2_star
rotate: false
xy: 1658, 425
size: 112, 110
orig: 112, 110
offset: 0, 0
index: -1
从中可看出,1-5行是整图大概信息,而后面是散图的具体信息;
所以大概思路就是读取atlas文件内的数据,根据具体散图的数据修改已有整图的分割数据(TextureImporter,SpriteMetaData)
具体代码参考:Unity批处理生成/拆分图集_TopGames的博客-优快云博客_unity图集拆分
我这里做了部分修改:
同级目录下,放入同名atlas文件,选中图片,右键ModifyByAltas,即可完成切图信息修改。
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Text;
public class ModifyByAltas
{
/// <summary>
/// 子图 信息
/// </summary>
internal class GDXAtlasElement
{
internal string name;
internal Rect rect;
}
/// <summary>
/// 图集 信息
/// </summary>
internal class GDXAtlas
{
internal string name;
internal List<GDXAtlasElement> elements;
}
/// <summary>
/// 解析图集配置表工具类
/// </summary>
internal class GDXAtlasUtility
{
internal static bool ParseAtlas(string atlasTxtFile, out GDXAtlas atlas)
{
atlas = null;
if (!File.Exists(atlasTxtFile))
{
Debug.Log($"Doesnt exist atlasTxtFile:{atlasTxtFile}");
return false;
}
var lines = new List<string>();
foreach (var line in File.ReadLines(atlasTxtFile,Encoding.UTF8))
{
if (string.IsNullOrWhiteSpace(line))
{
continue;
}
lines.Add(line);
}
if (lines.Count <= 0)
{
Debug.Log($"lines.Count <= 0 lines:{lines.Count}");
return false;
}
string atlasName = lines[0].Trim();
List<GDXAtlasElement> elements = new List<GDXAtlasElement>();
for (int i = 5; i < lines.Count;)
{
if (i + 6 > lines.Count)
{
break;
}
string eName = lines[i];
Debug.Log($"eName:{eName} idx:{i}");
string[] eXyValues = lines[i + 2].Trim().Split(':')[1].Trim().Split(',');
string[] eSizeValues = lines[i + 3].Trim().Split(':')[1].Trim().Split(',');
GDXAtlasElement spElement = new GDXAtlasElement();
spElement.name = eName.Trim();
Vector2 position = new Vector2(float.Parse(eXyValues[0].Trim()), float.Parse(eXyValues[1].Trim()));
Vector2 size = new Vector2(float.Parse(eSizeValues[0].Trim()), float.Parse(eSizeValues[1].Trim()));
spElement.rect = new Rect(position, size);
elements.Add(spElement);
i += 7;
}
atlas = new GDXAtlas();
atlas.name = atlasName;
atlas.elements = elements;
return true;
}
}
[MenuItem("Assets/ModifyByAltas")]
static private void AutoSliceAtlas()
{
EditorUtility.DisplayCancelableProgressBar("Progress", "Slice Atlas...", 0);
string selectObjDir = GetDirPath(AssetDatabase.GetAssetPath(Selection.activeInstanceID));
string[] dirs = { selectObjDir };
var assetIds = AssetDatabase.FindAssets("t:Texture", dirs);
for (int i = 0; i < assetIds.Length; i++)
{
string spFileName = AssetDatabase.GUIDToAssetPath(assetIds[i]);
var spTex = AssetDatabase.LoadAssetAtPath<Texture>(spFileName);
string atlasTxtFile = string.Format("{0}{1}{2}", Application.dataPath.Substring(0, Application.dataPath.Length - 6), spFileName.Substring(0, spFileName.Length - 4),".atlas");
Debug.Log($"atlasTxtFile:{atlasTxtFile} spFileName:{spFileName}");
if (!GDXAtlasUtility.ParseAtlas(atlasTxtFile, out GDXAtlas gdxAtlas))
{
Debug.Log("false");
continue;
}
SpriteMetaData[] spSheet = new SpriteMetaData[gdxAtlas.elements.Count];
for (int elemIndex = 0; elemIndex < gdxAtlas.elements.Count; elemIndex++)
{
var eDt = gdxAtlas.elements[elemIndex];
var spDt = new SpriteMetaData();
var fixRect = eDt.rect;
//libgdx图集坐标系原点是左上角, 这里需要转换到Unity坐标系(左下角)
fixRect.y = spTex.height - fixRect.y - fixRect.size.y;
spDt.name = eDt.name;
spDt.rect = fixRect;
spDt.pivot = Vector2.one * 0.5f;
spDt.border = Vector4.zero;
spDt.alignment = (int)SpriteAlignment.Custom;
spSheet[elemIndex] = spDt;
}
var texImporter = AssetImporter.GetAtPath(spFileName) as TextureImporter;
texImporter.spritesheet = spSheet;
texImporter.spriteImportMode = SpriteImportMode.Multiple;
texImporter.isReadable = true;
Undo.RecordObject(texImporter, "Test Undo");
texImporter.SaveAndReimport();
EditorUtility.DisplayCancelableProgressBar("Progress", "Slice Atlas...", (i + 1) / (float)assetIds.Length);
}
EditorUtility.ClearProgressBar();
}
public static string GetDirPath(string tpath)
{
if (string.IsNullOrEmpty(tpath)) return "";
int tidx = tpath.LastIndexOf('/');
string path = tpath.Substring(0, tidx);
return path;
}
}