
这是一个 Unity 编辑器脚本,用于导出 PSD/PSB 文件中通过 PSD Importer 生成的所有子 Sprite 为独立的 PNG 图片。
菜单入口
Tools/PSD/导出选中PSB的所有子Sprite
功能流程
- 获取当前选中的
.psb 文件
- 加载该 PSD 文件包含的所有 Asset(通过
AssetDatabase.LoadAllAssetsAtPath)
- 筛选出所有
Sprite 类型的资源
- 在 PSD 同目录下创建
<文件名>_SubSprites 文件夹
- 将每个 Sprite 裁剪为独立 PNG 导出
原始使用场景
Unity 的 PSD Importer 可以将 PSD 文件作为多图层 Sprite 图集导入,每个图层自动生成为一个子 Sprite。但 Unity 没有提供反向导出功能,这个脚本就是用来解决这个痛点——将已导入的 Sprite 重新导出为独立的 PNG 图片。
典型用途
- 导出 PSD 分层后的 Sprite 供其他工具/项目使用
- 备份或检查 PSD Importer 实际生成的 Sprite 内容
- 将图集拆分为单独的资源文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
| using System; using System.IO; using System.Linq; using UnityEditor; using UnityEngine; using Object = UnityEngine.Object;
public class ExportPsbSubSprites { [MenuItem("Tools/PSD/导出选中PSB的所有子Sprite")] public static void ExportSelectedPsbSprites() { Object selected = Selection.activeObject; if (selected == null) { Debug.LogError("请先选中 .psb 文件"); return; }
string path = AssetDatabase.GetAssetPath(selected); var assets = AssetDatabase.LoadAllAssetsAtPath(path);
string outputDir = Path.Combine(Path.GetDirectoryName(path), Path.GetFileNameWithoutExtension(path) + "_SubSprites"); Directory.CreateDirectory(outputDir);
foreach (var a in assets) { if (a is Sprite sprite) { ExportSprite(sprite, SafeCombine(outputDir, sprite.name + ".png")); } }
AssetDatabase.Refresh(); Debug.Log("导出完成: " + outputDir); }
public static string SafeCombine(string directory, string fileName) { if (string.IsNullOrWhiteSpace(directory)) throw new ArgumentException("directory 不能为空", nameof(directory));
if (string.IsNullOrWhiteSpace(fileName)) throw new ArgumentException("fileName 不能为空", nameof(fileName));
var invalidChars = Path.GetInvalidFileNameChars(); var safeFileName = new string(fileName .Where(c => !invalidChars.Contains(c)) .ToArray());
if (string.IsNullOrWhiteSpace(safeFileName)) throw new ArgumentException("fileName 清理后为空,原始文件名可能全是非法字符");
return Path.Combine(directory, safeFileName); }
static void ExportSprite(Sprite sprite, string outputPath) { Texture2D tex = sprite.texture; Rect rect = sprite.rect;
string texPath = AssetDatabase.GetAssetPath(tex); var importer = AssetImporter.GetAtPath(texPath) as TextureImporter; bool reset = false;
if (importer != null && !importer.isReadable) { importer.isReadable = true; importer.SaveAndReimport(); reset = true; }
Texture2D outTex = new Texture2D((int)rect.width, (int)rect.height, TextureFormat.RGBA32, false); outTex.SetPixels(tex.GetPixels((int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height)); outTex.Apply();
File.WriteAllBytes(outputPath, outTex.EncodeToPNG()); Object.DestroyImmediate(outTex);
if (reset && importer != null) { importer.isReadable = false; importer.SaveAndReimport(); } } }
|