diff --git a/Assets/Editor/iOSFixDuplicateShellScript.cs b/Assets/Editor/iOSFixDuplicateShellScript.cs index cc8786b..3da4ce8 100644 --- a/Assets/Editor/iOSFixDuplicateShellScript.cs +++ b/Assets/Editor/iOSFixDuplicateShellScript.cs @@ -31,19 +31,45 @@ public class iOSFixDuplicateShellScript string content = File.ReadAllText(projPath); - string pattern = @"(\t+([A-F0-9]+) /\* ShellScript \*/,)\r?\n\t+\2 /\* ShellScript \*,/"; - string newContent = Regex.Replace(content, pattern, "$1"); - - if (newContent != content) + // 1. 移除连续重复的 ShellScript 行 + string pattern1 = @"\t+[A-F0-9]+ /\* ShellScript \*/,(\r?\n\t+[A-F0-9]+ /\* ShellScript \*,)+"; + content = Regex.Replace(content, pattern1, m => { - File.WriteAllText(projPath, newContent); - Debug.Log("[PostProcess] Removed duplicate ShellScript from GameAssembly target"); + Debug.Log($"[PostProcess] 移除连续重复 ShellScript 行: {m.Value.TrimEnd().Length} chars"); + return m.Groups[0].Value.Split('\n')[0]; // 只保留第一行 + }); + + // 2. 移除整个文件中非连续的重复 ShellScript(按 ID 去重) + var lines = content.Split('\n'); + var seenShellScriptIds = new System.Collections.Generic.HashSet(); + var outputLines = new System.Collections.Generic.List(); + int removedCount = 0; + + foreach (string line in lines) + { + var match = Regex.Match(line, @"^\t+([A-F0-9]+) /\* ShellScript \*/,?$"); + if (match.Success) + { + string id = match.Groups[1].Value; + if (!seenShellScriptIds.Add(id)) + { + removedCount++; + continue; // 跳过重复 + } + } + outputLines.Add(line); } - // 修复光子矩阵 app 大小写 - newContent = newContent.Replace("photonmatrix.app", "PhotonMatrix.app"); + if (removedCount > 0) + { + content = string.Join("\n", outputLines); + Debug.Log($"[PostProcess] 按 ID 去重移除了 {removedCount} 个 ShellScript 条目"); + } - File.WriteAllText(projPath, newContent); + // 3. 修复光子矩阵 app 大小写 + content = content.Replace("photonmatrix.app", "PhotonMatrix.app"); + + File.WriteAllText(projPath, content); } private static void FixPodfile(string pathToBuiltProject) diff --git a/Assets/Editor/iOSIconPostProcessor.cs b/Assets/Editor/iOSIconPostProcessor.cs index 0803d6a..d793fd8 100644 --- a/Assets/Editor/iOSIconPostProcessor.cs +++ b/Assets/Editor/iOSIconPostProcessor.cs @@ -1,91 +1,162 @@ #if UNITY_IOS using UnityEditor; -using UnityEditor.Callbacks; +using UnityEditor.Build; +using UnityEditor.Build.Reporting; using System.IO; using System.Text; +using System.Collections.Generic; +using UnityEngine; /// /// iOS 出包后自动替换高清 App 图标,解决 Unity 压缩导致图标模糊的问题。 +/// 使用 IPostprocessBuildWithReport 确保在所有 PostProcessBuild 之后执行。 /// 使用方式:将精确尺寸的 PNG 图标放入 Assets/Icons/iOS/ 目录下,打包时自动生效。 +/// 命名规则:iPhone-App-180.png(设备-用途-像素尺寸)可被 Xcode Asset Catalog 自动识别。 /// -public class iOSIconPostProcessor +public class iOSIconPostProcessor : IPostprocessBuildWithReport { + public int callbackOrder => int.MaxValue; + private const string SOURCE_ICON_DIR = "Assets/Icons/iOS/"; - // 各尺寸图标配置 (idiom, scale, size, 文件名) - private static readonly (string idiom, string scale, string size, string file)[] ICON_CONFIGS = + // 图标配置: (idiom, scale, 点尺寸, 源文件名, 目标文件名) + // 源文件在 Assets/Icons/iOS/ 下,目标文件写入 Xcode 的 AppIcon.appiconset + private static readonly (string idiom, string scale, string size, string src, string dst)[] ICON_CONFIGS = { - ("iphone", "2x", "20x20", "icon-20@2x.png"), - ("iphone", "3x", "20x20", "icon-20@3x.png"), - ("iphone", "2x", "29x29", "icon-29@2x.png"), - ("iphone", "3x", "29x29", "icon-29@3x.png"), - ("iphone", "2x", "40x40", "icon-40@2x.png"), - ("iphone", "3x", "40x40", "icon-40@3x.png"), - ("iphone", "2x", "60x60", "icon-60@2x.png"), - ("iphone", "3x", "60x60", "icon-60@3x.png"), - ("ipad", "2x", "20x20", "icon-20@2x.png"), - ("ipad", "2x", "29x29", "icon-29@2x.png"), - ("ipad", "2x", "40x40", "icon-40@2x.png"), - ("ipad", "2x", "76x76", "icon-76@2x.png"), - ("ipad", "2x", "83.5x83.5", "icon-83.5@2x.png"), - ("ios-marketing", "1x", "1024x1024", "icon-1024.png"), + // --- iPhone --- + ("iphone", "2x", "20x20", "iPhone-Notification-40.png", "Icon-iPhone-Notification-40.png"), + ("iphone", "3x", "20x20", "iPhone-Notification-60.png", "Icon-iPhone-Notification-60.png"), + ("iphone", "2x", "29x29", "iPhone-Settings-58.png", "Icon-iPhone-Settings-58.png"), + ("iphone", "3x", "29x29", "iPhone-Settings-87.png", "Icon-iPhone-Settings-87.png"), + ("iphone", "2x", "40x40", "iPhone-Spotlight-80.png", "Icon-iPhone-Spotlight-80.png"), + ("iphone", "3x", "40x40", "iPhone-Spotlight-120.png", "Icon-iPhone-Spotlight-120.png"), + ("iphone", "2x", "60x60", "iPhone-App-120.png", "Icon-iPhone-App-120.png"), + ("iphone", "3x", "60x60", "iPhone-App-180.png", "Icon-iPhone-App-180.png"), + + // --- iPad(复用 iPhone 同名图标作为回退) --- + ("ipad", "2x", "20x20", "iPhone-Notification-40.png", "Icon-iPad-Notification-40.png"), + ("ipad", "2x", "29x29", "iPhone-Settings-58.png", "Icon-iPad-Settings-58.png"), + ("ipad", "2x", "40x40", "iPhone-Spotlight-80.png", "Icon-iPad-Spotlight-80.png"), + ("ipad", "2x", "76x76", "iPhone-Spotlight-80.png", "Icon-iPad-App-76.png"), + ("ipad", "2x", "83.5x83.5", "iPhone-App-120.png", "Icon-iPad-Pro-App-167.png"), + + // --- App Store --- + ("ios-marketing", "1x", "1024x1024", "AppStore-1024.png", "Icon-AppStore-1024.png"), }; - [PostProcessBuild(int.MaxValue)] - public static void OnPostProcessBuild(BuildTarget target, string pathToBuiltProject) + public void OnPostprocessBuild(BuildReport report) { - if (target != BuildTarget.iOS) return; + if (report.summary.platform != BuildTarget.iOS) return; - string iconSetPath = Path.Combine(pathToBuiltProject, "Unity-iPhone/Images.xcassets/AppIcon.appiconset"); - if (!Directory.Exists(iconSetPath)) + string pathToBuiltProject = report.summary.outputPath; + string iconSetPath = FindAppIconPath(pathToBuiltProject); + if (string.IsNullOrEmpty(iconSetPath)) { - UnityEngine.Debug.LogWarning($"[iOSIconPostProcessor] 未找到 AppIcon.appiconset 目录: {iconSetPath}"); + Debug.LogWarning($"[iOSIconPostProcessor] 未找到 AppIcon.appiconset 目录,跳过图标替换"); return; } - int copied = 0; + EditorApplication.delayCall += () => ReplaceIcons(iconSetPath); + } + + private static void ReplaceIcons(string iconSetPath) + { + if (!Directory.Exists(iconSetPath)) + { + Debug.LogWarning($"[iOSIconPostProcessor] AppIcon.appiconset 目录已不存在: {iconSetPath}"); + return; + } + + string projectRoot = Path.GetDirectoryName(Application.dataPath); + string sourceDir = Path.Combine(projectRoot, SOURCE_ICON_DIR); + + // 清空旧图标 + foreach (string oldFile in Directory.GetFiles(iconSetPath, "*.png")) + { + File.Delete(oldFile); + } + Debug.Log($"[iOSIconPostProcessor] 已清空旧图标"); + + // 复制新图标(源文件 → 目标 Xcode 标准名称) + var copiedDstFiles = new HashSet(); foreach (var cfg in ICON_CONFIGS) { - string src = Path.GetFullPath(Path.Combine(SOURCE_ICON_DIR, cfg.file)); - string dst = Path.Combine(iconSetPath, cfg.file); + string src = Path.Combine(sourceDir, cfg.src); if (File.Exists(src)) { + string dst = Path.Combine(iconSetPath, cfg.dst); File.Copy(src, dst, overwrite: true); - copied++; + copiedDstFiles.Add(cfg.dst); } else { - UnityEngine.Debug.LogWarning($"[iOSIconPostProcessor] 缺少图标文件: {src}"); + Debug.LogWarning($"[iOSIconPostProcessor] 缺少源图标: {cfg.src}"); } } - if (copied > 0) + if (copiedDstFiles.Count > 0) { - WriteContentsJson(iconSetPath); - UnityEngine.Debug.Log($"[iOSIconPostProcessor] 已替换 {copied} 个 App 图标到 Xcode 项目"); + WriteContentsJson(iconSetPath, copiedDstFiles); + Debug.Log($"[iOSIconPostProcessor] 已替换 {copiedDstFiles.Count} 个 App 图标"); + } + else + { + Debug.LogError("[iOSIconPostProcessor] 未找到任何可用图标"); } } - private static void WriteContentsJson(string iconSetPath) + private static string FindAppIconPath(string buildPath) + { + string[] candidates = + { + Path.Combine(buildPath, "Unity-iPhone/Images.xcassets/AppIcon.appiconset"), + Path.Combine(buildPath, "Images.xcassets/AppIcon.appiconset"), + Path.Combine(buildPath, "Unity-iPhone/Unity-iPhone/Images.xcassets/AppIcon.appiconset"), + }; + + foreach (string path in candidates) + { + if (Directory.Exists(path)) + { + Debug.Log($"[iOSIconPostProcessor] 找到 AppIcon 目录: {path}"); + return path; + } + } + + string searchRoot = Path.Combine(buildPath, "Unity-iPhone"); + if (!Directory.Exists(searchRoot)) searchRoot = buildPath; + foreach (string dir in Directory.GetDirectories(searchRoot, "AppIcon.appiconset", SearchOption.AllDirectories)) + { + Debug.Log($"[iOSIconPostProcessor] 搜索找到 AppIcon 目录: {dir}"); + return dir; + } + + return null; + } + + private static void WriteContentsJson(string iconSetPath, HashSet dstFiles) { var sb = new StringBuilder(); sb.AppendLine("{"); sb.AppendLine(" \"images\" : ["); - for (int i = 0; i < ICON_CONFIGS.Length; i++) + bool first = true; + foreach (var cfg in ICON_CONFIGS) { - var cfg = ICON_CONFIGS[i]; - sb.Append(" {"); - sb.Append($"\"idiom\" : \"{cfg.idiom}\", "); + if (!dstFiles.Contains(cfg.dst)) + continue; + + if (!first) sb.AppendLine(","); + else sb.AppendLine(); + first = false; + + sb.Append($" {{\"idiom\" : \"{cfg.idiom}\", "); sb.Append($"\"scale\" : \"{cfg.scale}\", "); - sb.Append($"\"size\" : \"{cfg.size}\""); - if (!string.IsNullOrEmpty(cfg.file)) - sb.Append($", \"filename\" : \"{cfg.file}\""); - sb.Append("}"); - if (i < ICON_CONFIGS.Length - 1) sb.Append(","); - sb.AppendLine(); + sb.Append($"\"size\" : \"{cfg.size}\", "); + sb.Append($"\"filename\" : \"{cfg.dst}\"}}"); } + sb.AppendLine(); sb.AppendLine(" ],"); sb.AppendLine(" \"info\" : {"); sb.AppendLine(" \"version\" : 1,"); diff --git a/Assets/Icons/iOS/icon-1024.png b/Assets/Icons/iOS/AppStore-1024.png similarity index 100% rename from Assets/Icons/iOS/icon-1024.png rename to Assets/Icons/iOS/AppStore-1024.png diff --git a/Assets/Icons/iOS/icon-1024.png.meta b/Assets/Icons/iOS/AppStore-1024.png.meta similarity index 100% rename from Assets/Icons/iOS/icon-1024.png.meta rename to Assets/Icons/iOS/AppStore-1024.png.meta diff --git a/Assets/Icons/iOS/icon-40@3x.png b/Assets/Icons/iOS/iPhone-App-120.png similarity index 100% rename from Assets/Icons/iOS/icon-40@3x.png rename to Assets/Icons/iOS/iPhone-App-120.png diff --git a/Assets/Icons/iOS/icon-60@2x.png.meta b/Assets/Icons/iOS/iPhone-App-120.png.meta similarity index 100% rename from Assets/Icons/iOS/icon-60@2x.png.meta rename to Assets/Icons/iOS/iPhone-App-120.png.meta diff --git a/Assets/Icons/iOS/icon-60@3x.png b/Assets/Icons/iOS/iPhone-App-180.png similarity index 100% rename from Assets/Icons/iOS/icon-60@3x.png rename to Assets/Icons/iOS/iPhone-App-180.png diff --git a/Assets/Icons/iOS/icon-60@3x.png.meta b/Assets/Icons/iOS/iPhone-App-180.png.meta similarity index 100% rename from Assets/Icons/iOS/icon-60@3x.png.meta rename to Assets/Icons/iOS/iPhone-App-180.png.meta diff --git a/Assets/Icons/iOS/icon-20@2x.png b/Assets/Icons/iOS/iPhone-Notification-40.png similarity index 100% rename from Assets/Icons/iOS/icon-20@2x.png rename to Assets/Icons/iOS/iPhone-Notification-40.png diff --git a/Assets/Icons/iOS/icon-20@2x.png.meta b/Assets/Icons/iOS/iPhone-Notification-40.png.meta similarity index 100% rename from Assets/Icons/iOS/icon-20@2x.png.meta rename to Assets/Icons/iOS/iPhone-Notification-40.png.meta diff --git a/Assets/Icons/iOS/icon-20@3x.png b/Assets/Icons/iOS/iPhone-Notification-60.png similarity index 100% rename from Assets/Icons/iOS/icon-20@3x.png rename to Assets/Icons/iOS/iPhone-Notification-60.png diff --git a/Assets/Icons/iOS/icon-20@3x.png.meta b/Assets/Icons/iOS/iPhone-Notification-60.png.meta similarity index 100% rename from Assets/Icons/iOS/icon-20@3x.png.meta rename to Assets/Icons/iOS/iPhone-Notification-60.png.meta diff --git a/Assets/Icons/iOS/icon-29@2x.png b/Assets/Icons/iOS/iPhone-Settings-58.png similarity index 100% rename from Assets/Icons/iOS/icon-29@2x.png rename to Assets/Icons/iOS/iPhone-Settings-58.png diff --git a/Assets/Icons/iOS/icon-29@2x.png.meta b/Assets/Icons/iOS/iPhone-Settings-58.png.meta similarity index 100% rename from Assets/Icons/iOS/icon-29@2x.png.meta rename to Assets/Icons/iOS/iPhone-Settings-58.png.meta diff --git a/Assets/Icons/iOS/icon-29@3x.png b/Assets/Icons/iOS/iPhone-Settings-87.png similarity index 100% rename from Assets/Icons/iOS/icon-29@3x.png rename to Assets/Icons/iOS/iPhone-Settings-87.png diff --git a/Assets/Icons/iOS/icon-29@3x.png.meta b/Assets/Icons/iOS/iPhone-Settings-87.png.meta similarity index 100% rename from Assets/Icons/iOS/icon-29@3x.png.meta rename to Assets/Icons/iOS/iPhone-Settings-87.png.meta diff --git a/Assets/Icons/iOS/icon-60@2x.png b/Assets/Icons/iOS/iPhone-Spotlight-120.png similarity index 100% rename from Assets/Icons/iOS/icon-60@2x.png rename to Assets/Icons/iOS/iPhone-Spotlight-120.png diff --git a/Assets/Icons/iOS/icon-40@3x.png.meta b/Assets/Icons/iOS/iPhone-Spotlight-120.png.meta similarity index 100% rename from Assets/Icons/iOS/icon-40@3x.png.meta rename to Assets/Icons/iOS/iPhone-Spotlight-120.png.meta diff --git a/Assets/Icons/iOS/icon-40@2x.png b/Assets/Icons/iOS/iPhone-Spotlight-80.png similarity index 100% rename from Assets/Icons/iOS/icon-40@2x.png rename to Assets/Icons/iOS/iPhone-Spotlight-80.png diff --git a/Assets/Icons/iOS/icon-40@2x.png.meta b/Assets/Icons/iOS/iPhone-Spotlight-80.png.meta similarity index 100% rename from Assets/Icons/iOS/icon-40@2x.png.meta rename to Assets/Icons/iOS/iPhone-Spotlight-80.png.meta diff --git a/ProjectSettings/ProjectSettings.asset b/ProjectSettings/ProjectSettings.asset index 7e77665..bac4d58 100644 --- a/ProjectSettings/ProjectSettings.asset +++ b/ProjectSettings/ProjectSettings.asset @@ -174,7 +174,7 @@ PlayerSettings: buildNumber: Standalone: 0 VisionOS: 0 - iPhone: 2 + iPhone: 3 tvOS: 0 overrideDefaultApplicationIdentifier: 1 AndroidBundleVersionCode: 1 @@ -417,6 +417,30 @@ PlayerSettings: - {fileID: 0} m_Width: 120 m_Height: 120 + m_Kind: 0 + m_SubKind: iPhone + - m_Textures: + - {fileID: 0} + m_Width: 167 + m_Height: 167 + m_Kind: 0 + m_SubKind: iPad + - m_Textures: + - {fileID: 0} + m_Width: 152 + m_Height: 152 + m_Kind: 0 + m_SubKind: iPad + - m_Textures: + - {fileID: 0} + m_Width: 76 + m_Height: 76 + m_Kind: 0 + m_SubKind: iPad + - m_Textures: + - {fileID: 2800000, guid: a7dc74acf58f7154f9a4a438ad70df1d, type: 3} + m_Width: 120 + m_Height: 120 m_Kind: 3 m_SubKind: iPhone - m_Textures: @@ -453,14 +477,12 @@ PlayerSettings: m_Height: 29 m_Kind: 1 m_SubKind: iPhone - - m_Textures: - - {fileID: 0} + - m_Textures: [] m_Width: 58 m_Height: 58 m_Kind: 1 m_SubKind: iPad - - m_Textures: - - {fileID: 0} + - m_Textures: [] m_Width: 29 m_Height: 29 m_Kind: 1