638 lines
22 KiB
C#
638 lines
22 KiB
C#
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Text;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
|
|
/// <summary>
|
|
/// 纹理尺寸检查器
|
|
/// 扫描指定目录下的所有 Texture2D,检查宽/高是否都是 4 的倍数
|
|
/// (BC7 等块压缩格式要求宽高必须为 4 的倍数,否则 Unity 会回退到 RGBA32 等未压缩格式,包体与内存都会暴涨)。
|
|
/// 支持一键修复:在 PNG 的右侧/上方补齐像素凑满 4 的倍数(原图可选备份)。
|
|
/// 菜单:Tools → 纹理尺寸检查器 (BC7 4倍数)
|
|
/// </summary>
|
|
public class TextureDimensionChecker : EditorWindow
|
|
{
|
|
// ============================ 日志开关 ============================
|
|
// 启用: Player Settings → Scripting Define Symbols 添加 ENABLE_EDITOR_LOG
|
|
// 关闭: 删除该符号。未定义时所有 Log/LogError 调用被编译器自动移除,零开销
|
|
[System.Diagnostics.Conditional("ENABLE_EDITOR_LOG")]
|
|
private static void Log(string msg) { Debug.Log(msg); }
|
|
|
|
#region 内部类
|
|
|
|
private class TextureCheckInfo
|
|
{
|
|
public string path;
|
|
public int width;
|
|
public int height;
|
|
public bool isMultipleOf4;
|
|
public bool loadFailed;
|
|
public string fixState;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region 字段
|
|
|
|
private Vector2 scroll;
|
|
private string rootFolder = "Assets";
|
|
private bool onlyShowInvalid = true;
|
|
private bool backupBeforeFix = true;
|
|
private bool scanned;
|
|
|
|
private readonly List<TextureCheckInfo> results = new List<TextureCheckInfo>();
|
|
private int totalCount;
|
|
private int validCount;
|
|
private int invalidCount;
|
|
private int failedCount;
|
|
|
|
#endregion
|
|
|
|
#region 入口
|
|
|
|
[MenuItem("Tools/纹理尺寸检查器 (BC7 4倍数)")]
|
|
public static void Open()
|
|
{
|
|
TextureDimensionChecker window = GetWindow<TextureDimensionChecker>("纹理尺寸检查器");
|
|
window.minSize = new Vector2(760f, 360f);
|
|
window.Show();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GUI
|
|
|
|
private void OnGUI()
|
|
{
|
|
DrawToolbar();
|
|
DrawFixToolbar();
|
|
DrawSummary();
|
|
DrawResultList();
|
|
}
|
|
|
|
private void DrawToolbar()
|
|
{
|
|
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
|
|
|
|
GUILayout.Label("扫描目录:", EditorStyles.label, GUILayout.Width(65f));
|
|
rootFolder = EditorGUILayout.TextField(rootFolder, EditorStyles.toolbarTextField, GUILayout.Width(240f));
|
|
if (GUILayout.Button("选择", EditorStyles.toolbarButton, GUILayout.Width(45f)))
|
|
{
|
|
string abs = EditorUtility.OpenFolderPanel("选择要扫描的目录(必须在 Assets 内)", rootFolder, string.Empty);
|
|
if (!string.IsNullOrEmpty(abs))
|
|
{
|
|
string rel = ToAssetRelativePath(abs);
|
|
if (string.IsNullOrEmpty(rel))
|
|
{
|
|
EditorUtility.DisplayDialog("目录不合法", "请选择 Assets 目录或其子目录。", "确定");
|
|
}
|
|
else
|
|
{
|
|
rootFolder = rel;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (GUILayout.Button("开始扫描", EditorStyles.toolbarButton, GUILayout.Width(70f)))
|
|
{
|
|
Scan();
|
|
}
|
|
|
|
GUILayout.Space(10f);
|
|
onlyShowInvalid = GUILayout.Toggle(onlyShowInvalid, "仅显示不合规", EditorStyles.toolbarButton, GUILayout.Width(90f));
|
|
|
|
GUILayout.FlexibleSpace();
|
|
|
|
using (new EditorGUI.DisabledScope(invalidCount == 0))
|
|
{
|
|
if (GUILayout.Button("复制不合规路径", EditorStyles.toolbarButton, GUILayout.Width(100f)))
|
|
{
|
|
CopyInvalidPaths();
|
|
}
|
|
if (GUILayout.Button("导出CSV", EditorStyles.toolbarButton, GUILayout.Width(60f)))
|
|
{
|
|
ExportCsv();
|
|
}
|
|
}
|
|
|
|
EditorGUILayout.EndHorizontal();
|
|
}
|
|
|
|
private void DrawFixToolbar()
|
|
{
|
|
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
|
|
|
|
backupBeforeFix = GUILayout.Toggle(backupBeforeFix, "修复前备份原图", EditorStyles.toolbarButton, GUILayout.Width(110f));
|
|
|
|
using (new EditorGUI.DisabledScope(invalidCount == 0))
|
|
{
|
|
if (GUILayout.Button($"修复全部不合规 ({invalidCount})", EditorStyles.toolbarButton, GUILayout.Width(140f)))
|
|
{
|
|
FixAll();
|
|
}
|
|
}
|
|
|
|
GUILayout.Space(6f);
|
|
if (GUILayout.Button("打开备份目录", EditorStyles.toolbarButton, GUILayout.Width(90f)))
|
|
{
|
|
if (Directory.Exists(BackupRoot)) EditorUtility.RevealInFinder(BackupRoot);
|
|
else EditorUtility.DisplayDialog("备份目录不存在", $"还没有任何备份:\n{BackupRoot}", "确定");
|
|
}
|
|
|
|
GUILayout.FlexibleSpace();
|
|
GUILayout.Label("修复方式:PNG 右侧/上方补像素到 4 的倍数(最多各 3px),不改导入设置", EditorStyles.miniLabel);
|
|
|
|
EditorGUILayout.EndHorizontal();
|
|
}
|
|
|
|
private void DrawSummary()
|
|
{
|
|
if (!scanned)
|
|
{
|
|
EditorGUILayout.HelpBox(
|
|
"扫描指定目录下所有 Texture2D,检查宽/高是否均为 4 的倍数。\n" +
|
|
"BC7 等块压缩格式要求宽高为 4 的倍数,不满足时 Unity 会自动回退到未压缩格式,导致包体和内存增大。\n\n" +
|
|
"修复:在 PNG 的右侧/上方补齐像素(最多各 3px)凑满 4 的倍数,补边不改变 Sprite 图集的矩形坐标;\n" +
|
|
"平铺(Repeat)纹理补透明边会出现接缝,改为复制边缘像素;仅支持 PNG,其他格式需手动处理。",
|
|
MessageType.Info);
|
|
return;
|
|
}
|
|
|
|
EditorGUILayout.BeginHorizontal(EditorStyles.helpBox);
|
|
GUILayout.Label($"总计:{totalCount}", GUILayout.Width(110f));
|
|
GUILayout.Label($"合规:{validCount}", GUILayout.Width(110f));
|
|
GUILayout.Label($"不合规:{invalidCount}", GUILayout.Width(120f));
|
|
GUILayout.Label($"加载失败:{failedCount}", GUILayout.Width(120f));
|
|
EditorGUILayout.EndHorizontal();
|
|
}
|
|
|
|
private void DrawResultList()
|
|
{
|
|
if (!scanned) return;
|
|
if (results.Count == 0 || (onlyShowInvalid && invalidCount == 0 && failedCount == 0))
|
|
{
|
|
EditorGUILayout.HelpBox($"没有符合条件的纹理(共扫描 {totalCount} 张)。", MessageType.Info);
|
|
return;
|
|
}
|
|
|
|
// 表头
|
|
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
|
|
GUILayout.Label("状态", EditorStyles.label, GUILayout.Width(60f));
|
|
GUILayout.Label("尺寸", EditorStyles.label, GUILayout.Width(120f));
|
|
GUILayout.Label("路径", EditorStyles.label);
|
|
GUILayout.Label("修复", EditorStyles.label, GUILayout.Width(60f));
|
|
GUILayout.Label("操作", EditorStyles.label, GUILayout.Width(185f));
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
scroll = EditorGUILayout.BeginScrollView(scroll);
|
|
int rowIndex = 0;
|
|
for (int i = 0; i < results.Count; i++)
|
|
{
|
|
TextureCheckInfo info = results[i];
|
|
if (onlyShowInvalid && info.isMultipleOf4) continue;
|
|
|
|
int i2 = rowIndex++;
|
|
Rect rowRect = EditorGUILayout.BeginHorizontal();
|
|
|
|
// 斑马纹
|
|
if (Event.current.type == EventType.Repaint && i2 % 2 == 1)
|
|
{
|
|
EditorGUI.DrawRect(rowRect, new Color(0f, 0f, 0f, 0.06f));
|
|
}
|
|
|
|
Color oldColor = GUI.color;
|
|
if (info.loadFailed) GUI.color = new Color(0.7f, 0.7f, 0.7f);
|
|
else if (!info.isMultipleOf4) GUI.color = new Color(1f, 0.5f, 0.5f);
|
|
GUILayout.Label(info.loadFailed ? "加载失败" : (info.isMultipleOf4 ? "OK" : "不合规"), EditorStyles.label, GUILayout.Width(60f));
|
|
GUI.color = oldColor;
|
|
|
|
GUILayout.Label(info.loadFailed ? "-" : $"{info.width} x {info.height}", EditorStyles.label, GUILayout.Width(120f));
|
|
GUILayout.Label(info.path, EditorStyles.label);
|
|
|
|
GUILayout.FlexibleSpace();
|
|
|
|
GUILayout.Label(info.fixState ?? string.Empty, EditorStyles.miniLabel, GUILayout.Width(60f));
|
|
|
|
using (new EditorGUI.DisabledScope(info.loadFailed || info.isMultipleOf4))
|
|
{
|
|
if (GUILayout.Button("修复", EditorStyles.miniButton, GUILayout.Width(55f)))
|
|
{
|
|
FixOne(info);
|
|
}
|
|
}
|
|
if (GUILayout.Button("定位", EditorStyles.miniButton, GUILayout.Width(55f)))
|
|
{
|
|
Ping(info.path);
|
|
}
|
|
if (GUILayout.Button("选中", EditorStyles.miniButton, GUILayout.Width(55f)))
|
|
{
|
|
Select(info.path);
|
|
}
|
|
|
|
EditorGUILayout.EndHorizontal();
|
|
|
|
// 双击定位
|
|
if (rowRect.Contains(Event.current.mousePosition) && Event.current.type == EventType.MouseDown && Event.current.clickCount == 2)
|
|
{
|
|
Ping(info.path);
|
|
Event.current.Use();
|
|
}
|
|
}
|
|
EditorGUILayout.EndScrollView();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region 扫描
|
|
|
|
private void Scan()
|
|
{
|
|
results.Clear();
|
|
totalCount = validCount = invalidCount = failedCount = 0;
|
|
|
|
if (!AssetDatabase.IsValidFolder(rootFolder))
|
|
{
|
|
EditorUtility.DisplayDialog("目录不存在", $"目录不存在或不是 Assets 内的目录:\n{rootFolder}", "确定");
|
|
return;
|
|
}
|
|
|
|
string[] guids;
|
|
try
|
|
{
|
|
guids = AssetDatabase.FindAssets("t:Texture2D", new[] { rootFolder });
|
|
}
|
|
catch (System.Exception e)
|
|
{
|
|
Log($"FindAssets 失败: {e.Message}");
|
|
EditorUtility.DisplayDialog("扫描失败", e.Message, "确定");
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
for (int i = 0; i < guids.Length; i++)
|
|
{
|
|
if (EditorUtility.DisplayCancelableProgressBar("扫描纹理尺寸", $"{i + 1}/{guids.Length}", (float)i / guids.Length))
|
|
{
|
|
Log("扫描被用户取消");
|
|
break;
|
|
}
|
|
|
|
string path = AssetDatabase.GUIDToAssetPath(guids[i]);
|
|
if (string.IsNullOrEmpty(path)) continue;
|
|
|
|
totalCount++;
|
|
|
|
TextureCheckInfo info = new TextureCheckInfo { path = path };
|
|
|
|
Texture2D tex = AssetDatabase.LoadAssetAtPath<Texture2D>(path);
|
|
if (tex == null)
|
|
{
|
|
info.loadFailed = true;
|
|
failedCount++;
|
|
}
|
|
else
|
|
{
|
|
info.width = tex.width;
|
|
info.height = tex.height;
|
|
info.isMultipleOf4 = (tex.width % 4 == 0) && (tex.height % 4 == 0);
|
|
|
|
if (info.isMultipleOf4) validCount++;
|
|
else invalidCount++;
|
|
}
|
|
|
|
results.Add(info);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
EditorUtility.ClearProgressBar();
|
|
}
|
|
|
|
// 不合规 / 加载失败排前面,其余按路径排序
|
|
results.Sort((a, b) =>
|
|
{
|
|
int pa = a.loadFailed ? 0 : (a.isMultipleOf4 ? 2 : 1);
|
|
int pb = b.loadFailed ? 0 : (b.isMultipleOf4 ? 2 : 1);
|
|
if (pa != pb) return pa.CompareTo(pb);
|
|
return string.CompareOrdinal(a.path, b.path);
|
|
});
|
|
|
|
scanned = true;
|
|
Log($"扫描完成: 总计 {totalCount}, 合规 {validCount}, 不合规 {invalidCount}, 加载失败 {failedCount}");
|
|
|
|
Repaint();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region 修复
|
|
|
|
private void FixOne(TextureCheckInfo info)
|
|
{
|
|
string error;
|
|
if (!TryFix(info, out error))
|
|
{
|
|
info.fixState = "失败";
|
|
EditorUtility.DisplayDialog("修复失败", $"{info.path}\n\n原因:{error}", "确定");
|
|
Log($"修复失败 {info.path}: {error}");
|
|
Repaint();
|
|
}
|
|
}
|
|
|
|
private void FixAll()
|
|
{
|
|
List<TextureCheckInfo> targets = new List<TextureCheckInfo>();
|
|
for (int i = 0; i < results.Count; i++)
|
|
{
|
|
TextureCheckInfo info = results[i];
|
|
if (!info.loadFailed && !info.isMultipleOf4) targets.Add(info);
|
|
}
|
|
|
|
if (targets.Count == 0)
|
|
{
|
|
EditorUtility.DisplayDialog("无需修复", "当前没有不合规的纹理。", "确定");
|
|
return;
|
|
}
|
|
|
|
string msg = $"将修复 {targets.Count} 张不合规纹理:\n\n" +
|
|
"· 在 PNG 的右侧/上方补齐像素,最多各 3px,凑满 4 的倍数\n" +
|
|
"· 不改动任何导入设置(压缩格式、mipmap 等保持原样)\n" +
|
|
(backupBeforeFix ? $"· 原图备份到 {BackupRoot}\n" : "· 不备份原图(原图将被直接覆盖)\n") +
|
|
"· 仅支持 PNG,其他格式会跳过\n" +
|
|
"· 九宫格 / 自动切图的 Sprite 补边后建议抽查一下\n\n是否继续?";
|
|
|
|
if (!EditorUtility.DisplayDialog("修复确认", msg, "开始修复", "取消")) return;
|
|
|
|
int ok = 0;
|
|
int fail = 0;
|
|
try
|
|
{
|
|
for (int i = 0; i < targets.Count; i++)
|
|
{
|
|
TextureCheckInfo info = targets[i];
|
|
if (EditorUtility.DisplayCancelableProgressBar("修复纹理尺寸", $"{i + 1}/{targets.Count} {info.path}", (float)i / targets.Count))
|
|
{
|
|
Log("修复被用户取消");
|
|
break;
|
|
}
|
|
|
|
string error;
|
|
if (TryFix(info, out error)) ok++;
|
|
else
|
|
{
|
|
fail++;
|
|
info.fixState = "失败";
|
|
Log($"修复失败 {info.path}: {error}");
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
EditorUtility.ClearProgressBar();
|
|
AssetDatabase.Refresh();
|
|
}
|
|
|
|
EditorUtility.DisplayDialog("修复完成",
|
|
$"成功 {ok} 张,失败/跳过 {fail} 张。\n" +
|
|
(backupBeforeFix ? $"\n原图备份目录:\n{BackupRoot}" : string.Empty),
|
|
"确定");
|
|
|
|
Repaint();
|
|
}
|
|
|
|
/// <summary>把 PNG 补边到 4 的倍数;原图贴左下角,新增像素补在右侧/上方</summary>
|
|
private bool TryFix(TextureCheckInfo info, out string error)
|
|
{
|
|
error = null;
|
|
|
|
string ext = Path.GetExtension(info.path).ToLowerInvariant();
|
|
if (ext != ".png")
|
|
{
|
|
error = "仅支持自动修复 PNG";
|
|
return false;
|
|
}
|
|
|
|
string absPath = ToAbsolutePath(info.path);
|
|
if (!File.Exists(absPath))
|
|
{
|
|
error = "源文件不存在";
|
|
return false;
|
|
}
|
|
|
|
byte[] srcBytes;
|
|
try
|
|
{
|
|
srcBytes = File.ReadAllBytes(absPath);
|
|
}
|
|
catch (System.Exception e)
|
|
{
|
|
error = $"读取失败:{e.Message}";
|
|
return false;
|
|
}
|
|
|
|
Texture2D src = null;
|
|
Texture2D dst = null;
|
|
try
|
|
{
|
|
src = new Texture2D(2, 2, TextureFormat.RGBA32, false);
|
|
if (!src.LoadImage(srcBytes))
|
|
{
|
|
error = "PNG 解码失败";
|
|
return false;
|
|
}
|
|
|
|
int w = src.width;
|
|
int h = src.height;
|
|
int extraW = (4 - w % 4) % 4;
|
|
int extraH = (4 - h % 4) % 4;
|
|
if (extraW == 0 && extraH == 0)
|
|
{
|
|
error = "尺寸已是 4 的倍数";
|
|
return false;
|
|
}
|
|
|
|
TextureImporter importer = AssetImporter.GetAtPath(info.path) as TextureImporter;
|
|
// 平铺(Repeat)纹理补透明边会出现接缝,改为复制边缘像素
|
|
bool extendEdge = importer != null
|
|
&& importer.textureType != TextureImporterType.Sprite
|
|
&& importer.wrapMode == TextureWrapMode.Repeat;
|
|
|
|
int dstW = w + extraW;
|
|
int dstH = h + extraH;
|
|
|
|
Color32[] srcPixels = src.GetPixels32();
|
|
Color32[] dstPixels = new Color32[dstW * dstH];
|
|
if (extendEdge)
|
|
{
|
|
for (int y = 0; y < dstH; y++)
|
|
{
|
|
int srcRow = Mathf.Clamp(y, 0, h - 1) * w;
|
|
int dstRow = y * dstW;
|
|
for (int x = 0; x < dstW; x++)
|
|
{
|
|
dstPixels[dstRow + x] = srcPixels[srcRow + Mathf.Clamp(x, 0, w - 1)];
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int y = 0; y < h; y++)
|
|
{
|
|
System.Array.Copy(srcPixels, y * w, dstPixels, y * dstW, w);
|
|
}
|
|
}
|
|
|
|
dst = new Texture2D(dstW, dstH, TextureFormat.RGBA32, false);
|
|
dst.SetPixels32(dstPixels);
|
|
dst.Apply();
|
|
|
|
byte[] pngBytes = dst.EncodeToPNG();
|
|
if (pngBytes == null || pngBytes.Length == 0)
|
|
{
|
|
error = "PNG 编码失败";
|
|
return false;
|
|
}
|
|
|
|
if (backupBeforeFix)
|
|
{
|
|
try
|
|
{
|
|
BackupFile(info.path, srcBytes);
|
|
}
|
|
catch (System.Exception e)
|
|
{
|
|
error = $"备份失败(文件未修改):{e.Message}";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
File.WriteAllBytes(absPath, pngBytes);
|
|
}
|
|
catch (System.Exception e)
|
|
{
|
|
error = $"写入失败:{e.Message}";
|
|
return false;
|
|
}
|
|
|
|
AssetDatabase.ImportAsset(info.path, ImportAssetOptions.ForceUpdate);
|
|
|
|
Texture2D reloaded = AssetDatabase.LoadAssetAtPath<Texture2D>(info.path);
|
|
if (reloaded == null)
|
|
{
|
|
error = "重新导入后加载失败,请检查该资源";
|
|
return false;
|
|
}
|
|
|
|
info.width = reloaded.width;
|
|
info.height = reloaded.height;
|
|
info.isMultipleOf4 = (reloaded.width % 4 == 0) && (reloaded.height % 4 == 0);
|
|
if (!info.isMultipleOf4)
|
|
{
|
|
error = $"源图已补边为 {dstW}x{dstH},但导入尺寸为 {reloaded.width}x{reloaded.height}(可能被 Max Size 缩放),仍不合规";
|
|
return false;
|
|
}
|
|
|
|
info.fixState = "已修复";
|
|
if (invalidCount > 0) invalidCount--;
|
|
validCount++;
|
|
Log($"已修复 {info.path}: {w}x{h} -> {dstW}x{dstH}");
|
|
return true;
|
|
}
|
|
finally
|
|
{
|
|
if (src != null) DestroyImmediate(src);
|
|
if (dst != null) DestroyImmediate(dst);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region 备份
|
|
|
|
/// <summary>备份目录放在工程根目录(Assets 之外),避免被 Unity 导入</summary>
|
|
private static string BackupRoot
|
|
{
|
|
get { return Path.Combine(Directory.GetParent(Application.dataPath).FullName, "TextureFixBackup"); }
|
|
}
|
|
|
|
private static void BackupFile(string assetPath, byte[] bytes)
|
|
{
|
|
string stamp = System.DateTime.Now.ToString("yyyyMMdd_HHmmss");
|
|
string dst = Path.Combine(BackupRoot, stamp, assetPath.Replace('/', Path.DirectorySeparatorChar));
|
|
string dir = Path.GetDirectoryName(dst);
|
|
if (!string.IsNullOrEmpty(dir)) Directory.CreateDirectory(dir);
|
|
File.WriteAllBytes(dst, bytes);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region 工具
|
|
|
|
/// <summary>把绝对路径转成 Assets 相对路径;不在 Assets 内返回 null</summary>
|
|
private static string ToAssetRelativePath(string absolutePath)
|
|
{
|
|
string dataPath = Application.dataPath.Replace('\\', '/');
|
|
string full = absolutePath.Replace('\\', '/');
|
|
if (full == dataPath) return "Assets";
|
|
if (!full.StartsWith(dataPath + "/")) return null;
|
|
return "Assets" + full.Substring(dataPath.Length);
|
|
}
|
|
|
|
/// <summary>把 Assets 相对路径转成绝对路径</summary>
|
|
private static string ToAbsolutePath(string assetPath)
|
|
{
|
|
string projectRoot = Directory.GetParent(Application.dataPath).FullName;
|
|
return Path.Combine(projectRoot, assetPath.Replace('/', Path.DirectorySeparatorChar));
|
|
}
|
|
|
|
private void Ping(string path)
|
|
{
|
|
Object asset = AssetDatabase.LoadAssetAtPath<Object>(path);
|
|
if (asset == null) return;
|
|
EditorGUIUtility.PingObject(asset);
|
|
}
|
|
|
|
private void Select(string path)
|
|
{
|
|
Object asset = AssetDatabase.LoadAssetAtPath<Object>(path);
|
|
if (asset == null) return;
|
|
Selection.activeObject = asset;
|
|
EditorGUIUtility.PingObject(asset);
|
|
}
|
|
|
|
private void CopyInvalidPaths()
|
|
{
|
|
StringBuilder sb = new StringBuilder();
|
|
foreach (TextureCheckInfo info in results)
|
|
{
|
|
if (!info.isMultipleOf4) sb.AppendLine(info.path);
|
|
}
|
|
EditorGUIUtility.systemCopyBuffer = sb.ToString();
|
|
Log($"已复制 {invalidCount} 条不合规路径到剪贴板");
|
|
}
|
|
|
|
private void ExportCsv()
|
|
{
|
|
string file = EditorUtility.SaveFilePanel("导出纹理尺寸检查结果", Application.dataPath, "TextureDimensionReport", "csv");
|
|
if (string.IsNullOrEmpty(file)) return;
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.AppendLine("状态,宽度,高度,路径");
|
|
foreach (TextureCheckInfo info in results)
|
|
{
|
|
if (onlyShowInvalid && info.isMultipleOf4) continue;
|
|
string state = info.loadFailed ? "加载失败" : (info.isMultipleOf4 ? "合规" : "不合规");
|
|
sb.AppendLine($"{state},{(info.loadFailed ? "-" : info.width.ToString())},{(info.loadFailed ? "-" : info.height.ToString())},{info.path}");
|
|
}
|
|
|
|
File.WriteAllText(file, sb.ToString(), new UTF8Encoding(true));
|
|
Log($"报告已导出: {file}");
|
|
EditorUtility.RevealInFinder(file);
|
|
}
|
|
|
|
#endregion
|
|
} |