三国卡牌客户端基础资源仓库
yyl
2026-09-21 c59ab0cdc415f3871697cde6d897dfdb606c811e
抖音

(cherry picked from commit 62df33fe64fae642c01c7d627c0213d844546e68)
1个文件已修改
444 ■■■■■ 已修改文件
Assets/Editor/Tool/DiagnoseAtlasPaging.cs 444 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Assets/Editor/Tool/DiagnoseAtlasPaging.cs
@@ -1,256 +1,256 @@
using System.Collections.Generic;
using System.Text;
using UnityEditor;
using UnityEditor.U2D;
using UnityEngine;
using UnityEngine.U2D;
using UnityEngine.UI;
// using System.Collections.Generic;
// using System.Text;
// using UnityEditor;
// using UnityEditor.U2D;
// using UnityEngine;
// using UnityEngine.U2D;
// using UnityEngine.UI;
/// <summary>
/// 诊断图集在 maxTextureSize=1024 下的打包情况:
///   - 单页(正常)
///   - 分页(内容超过 1024,Unity 自动切成多页 → 抖音 WebGL AB 链路多页加载可能丢 sprite)
///   - 打包 sprite 数与 packables 内 sprite 数不一致(有 sprite 被丢弃)
/// 菜单:程序/Sprite/诊断图集分页与丢失
/// </summary>
public static class DiagnoseAtlasPaging
{
    private const string TARGET_FOLDER = "Assets/ResourcesOut";
// /// <summary>
// /// 诊断图集在 maxTextureSize=1024 下的打包情况:
// ///   - 单页(正常)
// ///   - 分页(内容超过 1024,Unity 自动切成多页 → 抖音 WebGL AB 链路多页加载可能丢 sprite)
// ///   - 打包 sprite 数与 packables 内 sprite 数不一致(有 sprite 被丢弃)
// /// 菜单:程序/Sprite/诊断图集分页与丢失
// /// </summary>
// public static class DiagnoseAtlasPaging
// {
//     private const string TARGET_FOLDER = "Assets/ResourcesOut";
    [MenuItem("程序/Sprite/诊断选中Image图集归属")]
    public static void DiagnoseSelectedImageAtlas()
    {
        var selectedObject = Selection.activeGameObject;
        var image = selectedObject == null ? null : selectedObject.GetComponent<Image>();
        var sourceSprite = image == null ? null : image.sprite;
        if (sourceSprite == null)
        {
            Debug.LogWarning("[AtlasBinding] 请先在 Hierarchy 中选中带有 Source Image 的 Image 对象。");
            return;
        }
//     [MenuItem("程序/Sprite/诊断选中Image图集归属")]
//     public static void DiagnoseSelectedImageAtlas()
//     {
//         var selectedObject = Selection.activeGameObject;
//         var image = selectedObject == null ? null : selectedObject.GetComponent<Image>();
//         var sourceSprite = image == null ? null : image.sprite;
//         if (sourceSprite == null)
//         {
//             Debug.LogWarning("[AtlasBinding] 请先在 Hierarchy 中选中带有 Source Image 的 Image 对象。");
//             return;
//         }
        string spritePath = AssetDatabase.GetAssetPath(sourceSprite);
        var guids = AssetDatabase.FindAssets("t:SpriteAtlas", new[] { TARGET_FOLDER });
        var sb = new StringBuilder();
        int explicitOwnerCount = 0;
        int bindableCount = 0;
//         string spritePath = AssetDatabase.GetAssetPath(sourceSprite);
//         var guids = AssetDatabase.FindAssets("t:SpriteAtlas", new[] { TARGET_FOLDER });
//         var sb = new StringBuilder();
//         int explicitOwnerCount = 0;
//         int bindableCount = 0;
        sb.AppendLine($"[AtlasBinding] object={selectedObject.name}, sprite={sourceSprite.name}, source={spritePath}");
        for (int i = 0; i < guids.Length; i++)
        {
            string atlasPath = AssetDatabase.GUIDToAssetPath(guids[i]);
            var atlas = AssetDatabase.LoadAssetAtPath<SpriteAtlas>(atlasPath);
            if (atlas == null)
                continue;
//         sb.AppendLine($"[AtlasBinding] object={selectedObject.name}, sprite={sourceSprite.name}, source={spritePath}");
//         for (int i = 0; i < guids.Length; i++)
//         {
//             string atlasPath = AssetDatabase.GUIDToAssetPath(guids[i]);
//             var atlas = AssetDatabase.LoadAssetAtPath<SpriteAtlas>(atlasPath);
//             if (atlas == null)
//                 continue;
            bool explicitOwner = ContainsSpriteSource(atlas, spritePath);
            bool canBind = atlas.CanBindTo(sourceSprite);
            if (!explicitOwner && !canBind)
                continue;
//             bool explicitOwner = ContainsSpriteSource(atlas, spritePath);
//             bool canBind = atlas.CanBindTo(sourceSprite);
//             if (!explicitOwner && !canBind)
//                 continue;
            if (explicitOwner)
                explicitOwnerCount++;
            if (canBind)
                bindableCount++;
//             if (explicitOwner)
//                 explicitOwnerCount++;
//             if (canBind)
//                 bindableCount++;
            var packedSprite = atlas.GetSprite(sourceSprite.name);
            string packedTexture = packedSprite != null && packedSprite.texture != null
                ? packedSprite.texture.name
                : "<null>";
            sb.AppendLine($"  atlas={atlasPath}, explicitPackable={explicitOwner}, canBind={canBind}, " +
                $"spriteCount={atlas.spriteCount}, packedTexture={packedTexture}");
        }
//             var packedSprite = atlas.GetSprite(sourceSprite.name);
//             string packedTexture = packedSprite != null && packedSprite.texture != null
//                 ? packedSprite.texture.name
//                 : "<null>";
//             sb.AppendLine($"  atlas={atlasPath}, explicitPackable={explicitOwner}, canBind={canBind}, " +
//                 $"spriteCount={atlas.spriteCount}, packedTexture={packedTexture}");
//         }
        sb.AppendLine($"[AtlasBinding] explicitOwners={explicitOwnerCount}, bindableAtlases={bindableCount}");
        Debug.LogError(sb.ToString());
        EditorUtility.DisplayDialog("Image 图集归属诊断",
            $"Sprite: {sourceSprite.name}\n显式所属图集: {explicitOwnerCount}\n当前可绑定图集: {bindableCount}\n\n详见 Console (Error)。",
            "OK");
    }
//         sb.AppendLine($"[AtlasBinding] explicitOwners={explicitOwnerCount}, bindableAtlases={bindableCount}");
//         Debug.LogError(sb.ToString());
//         EditorUtility.DisplayDialog("Image 图集归属诊断",
//             $"Sprite: {sourceSprite.name}\n显式所属图集: {explicitOwnerCount}\n当前可绑定图集: {bindableCount}\n\n详见 Console (Error)。",
//             "OK");
//     }
    [MenuItem("程序/Sprite/诊断图集分页与丢失")]
    public static void Diagnose()
    {
        var guids = AssetDatabase.FindAssets("t:SpriteAtlas", new[] { TARGET_FOLDER });
        var sb = new StringBuilder();
        int valid = 0, empty = 0, lost = 0, invalidSettings = 0;
//     [MenuItem("程序/Sprite/诊断图集分页与丢失")]
//     public static void Diagnose()
//     {
//         var guids = AssetDatabase.FindAssets("t:SpriteAtlas", new[] { TARGET_FOLDER });
//         var sb = new StringBuilder();
//         int valid = 0, empty = 0, lost = 0, invalidSettings = 0;
        for (int i = 0; i < guids.Length; i++)
        {
            var path = AssetDatabase.GUIDToAssetPath(guids[i]);
            var atlas = AssetDatabase.LoadAssetAtPath<SpriteAtlas>(path);
            if (atlas == null) continue;
//         for (int i = 0; i < guids.Length; i++)
//         {
//             var path = AssetDatabase.GUIDToAssetPath(guids[i]);
//             var atlas = AssetDatabase.LoadAssetAtPath<SpriteAtlas>(path);
//             if (atlas == null) continue;
            // 统计 packables 内的 sprite 总数
            int expected = CountPackableSprites(atlas);
            int packed = atlas.spriteCount;
//             // 统计 packables 内的 sprite 总数
//             int expected = CountPackableSprites(atlas);
//             int packed = atlas.spriteCount;
            if (packed <= 0)
            {
                sb.AppendLine($"[未生成打包缓存 | sprite 0/{expected}] {path}");
                empty++;
                continue;
            }
//             if (packed <= 0)
//             {
//                 sb.AppendLine($"[未生成打包缓存 | sprite 0/{expected}] {path}");
//                 empty++;
//                 continue;
//             }
            var importer = SpriteAtlasImporter.GetAtPath(path) as SpriteAtlasImporter;
            int expectedMaxTextureSize = TexturePlatformFormatHelper.GetAtlasMaxTextureSize(path);
            bool settingsValid = importer != null
                && HasTargetPlatformSettings(importer, "Android", expectedMaxTextureSize)
                && HasTargetPlatformSettings(importer, "iPhone", expectedMaxTextureSize)
                && HasTargetPlatformSettings(importer, "WebGL", expectedMaxTextureSize);
//             var importer = SpriteAtlasImporter.GetAtPath(path) as SpriteAtlasImporter;
//             int expectedMaxTextureSize = TexturePlatformFormatHelper.GetAtlasMaxTextureSize(path);
//             bool settingsValid = importer != null
//                 && HasTargetPlatformSettings(importer, "Android", expectedMaxTextureSize)
//                 && HasTargetPlatformSettings(importer, "iPhone", expectedMaxTextureSize)
//                 && HasTargetPlatformSettings(importer, "WebGL", expectedMaxTextureSize);
            sb.AppendLine($"[{(settingsValid ? "设置正常" : "设置异常")} | max {expectedMaxTextureSize} | sprite {packed}/{expected}] {path}");
            if (!settingsValid)
            {
                sb.AppendLine($"    !! Android/iPhone/WebGL 必须为 Override + {expectedMaxTextureSize} + ASTC_8x8");
                invalidSettings++;
            }
            else
            {
                valid++;
            }
//             sb.AppendLine($"[{(settingsValid ? "设置正常" : "设置异常")} | max {expectedMaxTextureSize} | sprite {packed}/{expected}] {path}");
//             if (!settingsValid)
//             {
//                 sb.AppendLine($"    !! Android/iPhone/WebGL 必须为 Override + {expectedMaxTextureSize} + ASTC_8x8");
//                 invalidSettings++;
//             }
//             else
//             {
//                 valid++;
//             }
            if (expected > 0 && packed < expected)
            {
                sb.AppendLine($"    !! 疑似丢失 sprite: 期望 {expected} 实际 {packed},差 {expected - packed}");
                lost++;
            }
        }
//             if (expected > 0 && packed < expected)
//             {
//                 sb.AppendLine($"    !! 疑似丢失 sprite: 期望 {expected} 实际 {packed},差 {expected - packed}");
//                 lost++;
//             }
//         }
        sb.Insert(0, $"图集诊断完成:设置正常 {valid},空 {empty},疑似丢失 {lost},设置异常 {invalidSettings},共 {guids.Length}\n");
        sb.AppendLine("---- 结论 ----");
        sb.AppendLine("本工具只判断图集设置和 sprite 数,不根据 Sprite.texture 推断 page 数。");
        sb.AppendLine("疑似丢失 = packables 内 sprite 未能全部打入图集,需要进一步检查导入和打包日志。");
//         sb.Insert(0, $"图集诊断完成:设置正常 {valid},空 {empty},疑似丢失 {lost},设置异常 {invalidSettings},共 {guids.Length}\n");
//         sb.AppendLine("---- 结论 ----");
//         sb.AppendLine("本工具只判断图集设置和 sprite 数,不根据 Sprite.texture 推断 page 数。");
//         sb.AppendLine("疑似丢失 = packables 内 sprite 未能全部打入图集,需要进一步检查导入和打包日志。");
        Debug.LogError(sb.ToString());
        EditorUtility.DisplayDialog("图集诊断",
            $"设置正常 {valid} / 设置异常 {invalidSettings} / 空 {empty} / 疑似丢失 {lost}\n共 {guids.Length} 个图集,详见 Console (Error)。",
            "OK");
    }
//         Debug.LogError(sb.ToString());
//         EditorUtility.DisplayDialog("图集诊断",
//             $"设置正常 {valid} / 设置异常 {invalidSettings} / 空 {empty} / 疑似丢失 {lost}\n共 {guids.Length} 个图集,详见 Console (Error)。",
//             "OK");
//     }
    [MenuItem("程序/Sprite/验证选中图集WebGL打包")]
    public static void PackSelectedAtlasForWebGL()
    {
        var atlas = Selection.activeObject as SpriteAtlas;
        if (atlas == null)
        {
            Debug.LogWarning("[AtlasPack] 请先在 Project 窗口选中一个 SpriteAtlas 图集。");
            return;
        }
//     [MenuItem("程序/Sprite/验证选中图集WebGL打包")]
//     public static void PackSelectedAtlasForWebGL()
//     {
//         var atlas = Selection.activeObject as SpriteAtlas;
//         if (atlas == null)
//         {
//             Debug.LogWarning("[AtlasPack] 请先在 Project 窗口选中一个 SpriteAtlas 图集。");
//             return;
//         }
        var path = AssetDatabase.GetAssetPath(atlas);
        var importer = SpriteAtlasImporter.GetAtPath(path) as SpriteAtlasImporter;
        var webGLSettings = importer == null ? null : importer.GetPlatformSettings("WebGL");
        Debug.Log($"[AtlasPack] 开始仅打包: {path}, target=WebGL");
        if (webGLSettings != null)
        {
            Debug.Log($"[AtlasPack] WebGL设置: overridden={webGLSettings.overridden}, " +
                $"max={webGLSettings.maxTextureSize}, format={webGLSettings.format}({(int)webGLSettings.format})");
        }
//         var path = AssetDatabase.GetAssetPath(atlas);
//         var importer = SpriteAtlasImporter.GetAtPath(path) as SpriteAtlasImporter;
//         var webGLSettings = importer == null ? null : importer.GetPlatformSettings("WebGL");
//         Debug.Log($"[AtlasPack] 开始仅打包: {path}, target=WebGL");
//         if (webGLSettings != null)
//         {
//             Debug.Log($"[AtlasPack] WebGL设置: overridden={webGLSettings.overridden}, " +
//                 $"max={webGLSettings.maxTextureSize}, format={webGLSettings.format}({(int)webGLSettings.format})");
//         }
        SpriteAtlasUtility.PackAtlases(new[] { atlas }, BuildTarget.WebGL, true);
//         SpriteAtlasUtility.PackAtlases(new[] { atlas }, BuildTarget.WebGL, true);
        var packed = atlas.spriteCount;
        var expected = CountPackableSprites(atlas);
        Debug.Log($"[AtlasPack] 完成: {path}, sprite={packed}/{expected}");
        EditorUtility.DisplayDialog("图集验证完成",
            $"{path}\nWebGL 打包结果:sprite {packed}/{expected}\n\n未执行全量图集打包。",
            "OK");
    }
//         var packed = atlas.spriteCount;
//         var expected = CountPackableSprites(atlas);
//         Debug.Log($"[AtlasPack] 完成: {path}, sprite={packed}/{expected}");
//         EditorUtility.DisplayDialog("图集验证完成",
//             $"{path}\nWebGL 打包结果:sprite {packed}/{expected}\n\n未执行全量图集打包。",
//             "OK");
//     }
    [MenuItem("程序/Sprite/诊断选中图集源图尺寸")]
    public static void DiagnoseSelectedAtlasSourceSizes()
    {
        var atlas = Selection.activeObject as SpriteAtlas;
        if (atlas == null)
        {
            Debug.LogWarning("[AtlasSize] 请先在 Project 窗口选中一个 SpriteAtlas 图集。");
            return;
        }
//     [MenuItem("程序/Sprite/诊断选中图集源图尺寸")]
//     public static void DiagnoseSelectedAtlasSourceSizes()
//     {
//         var atlas = Selection.activeObject as SpriteAtlas;
//         if (atlas == null)
//         {
//             Debug.LogWarning("[AtlasSize] 请先在 Project 窗口选中一个 SpriteAtlas 图集。");
//             return;
//         }
        var sb = new StringBuilder();
        sb.AppendLine($"[AtlasSize] {AssetDatabase.GetAssetPath(atlas)}");
        foreach (var packable in SpriteAtlasExtensions.GetPackables(atlas))
        {
            if (packable == null)
                continue;
//         var sb = new StringBuilder();
//         sb.AppendLine($"[AtlasSize] {AssetDatabase.GetAssetPath(atlas)}");
//         foreach (var packable in SpriteAtlasExtensions.GetPackables(atlas))
//         {
//             if (packable == null)
//                 continue;
            var path = AssetDatabase.GetAssetPath(packable);
            var texture = AssetDatabase.LoadAssetAtPath<Texture2D>(path);
            var importer = AssetImporter.GetAtPath(path) as TextureImporter;
            sb.AppendLine($"{path} | object={packable.GetType().Name} | " +
                $"texture={(texture == null ? "null" : $"{texture.width}x{texture.height}")} | " +
                $"importMax={(importer == null ? "null" : importer.maxTextureSize.ToString())} | " +
                $"mode={(importer == null ? "null" : importer.spriteImportMode.ToString())}");
//             var path = AssetDatabase.GetAssetPath(packable);
//             var texture = AssetDatabase.LoadAssetAtPath<Texture2D>(path);
//             var importer = AssetImporter.GetAtPath(path) as TextureImporter;
//             sb.AppendLine($"{path} | object={packable.GetType().Name} | " +
//                 $"texture={(texture == null ? "null" : $"{texture.width}x{texture.height}")} | " +
//                 $"importMax={(importer == null ? "null" : importer.maxTextureSize.ToString())} | " +
//                 $"mode={(importer == null ? "null" : importer.spriteImportMode.ToString())}");
            if (texture == null || importer == null || importer.spriteImportMode != SpriteImportMode.Multiple)
                continue;
//             if (texture == null || importer == null || importer.spriteImportMode != SpriteImportMode.Multiple)
//                 continue;
            foreach (var sprite in AssetDatabase.LoadAllAssetRepresentationsAtPath(path))
            {
                if (sprite is Sprite sourceSprite)
                    sb.AppendLine($"    sprite={sourceSprite.name} rect={sourceSprite.rect.width}x{sourceSprite.rect.height}");
            }
        }
//             foreach (var sprite in AssetDatabase.LoadAllAssetRepresentationsAtPath(path))
//             {
//                 if (sprite is Sprite sourceSprite)
//                     sb.AppendLine($"    sprite={sourceSprite.name} rect={sourceSprite.rect.width}x{sourceSprite.rect.height}");
//             }
//         }
        Debug.Log(sb.ToString());
    }
//         Debug.Log(sb.ToString());
//     }
    private static bool HasTargetPlatformSettings(SpriteAtlasImporter importer, string platform, int expectedMaxTextureSize)
    {
        var settings = importer.GetPlatformSettings(platform);
        return settings.overridden
            && settings.maxTextureSize == expectedMaxTextureSize
            && ((int)settings.format == (int)TexturePlatformFormatHelper.ATLAS_FORMAT
                || (int)settings.format == 57);
    }
//     private static bool HasTargetPlatformSettings(SpriteAtlasImporter importer, string platform, int expectedMaxTextureSize)
//     {
//         var settings = importer.GetPlatformSettings(platform);
//         return settings.overridden
//             && settings.maxTextureSize == expectedMaxTextureSize
//             && ((int)settings.format == (int)TexturePlatformFormatHelper.ATLAS_FORMAT
//                 || (int)settings.format == 57);
//     }
    private static bool ContainsSpriteSource(SpriteAtlas atlas, string spritePath)
    {
        foreach (var packable in SpriteAtlasExtensions.GetPackables(atlas))
        {
            if (packable == null)
                continue;
//     private static bool ContainsSpriteSource(SpriteAtlas atlas, string spritePath)
//     {
//         foreach (var packable in SpriteAtlasExtensions.GetPackables(atlas))
//         {
//             if (packable == null)
//                 continue;
            string packablePath = AssetDatabase.GetAssetPath(packable).Replace('\\', '/');
            if (string.Equals(packablePath, spritePath, System.StringComparison.OrdinalIgnoreCase))
                return true;
            if (AssetDatabase.IsValidFolder(packablePath)
                && spritePath.StartsWith(packablePath.TrimEnd('/') + "/", System.StringComparison.OrdinalIgnoreCase))
                return true;
        }
//             string packablePath = AssetDatabase.GetAssetPath(packable).Replace('\\', '/');
//             if (string.Equals(packablePath, spritePath, System.StringComparison.OrdinalIgnoreCase))
//                 return true;
//             if (AssetDatabase.IsValidFolder(packablePath)
//                 && spritePath.StartsWith(packablePath.TrimEnd('/') + "/", System.StringComparison.OrdinalIgnoreCase))
//                 return true;
//         }
        return false;
    }
//         return false;
//     }
    private static int CountPackableSprites(SpriteAtlas atlas)
    {
        int count = 0;
        foreach (var packable in SpriteAtlasExtensions.GetPackables(atlas))
        {
            if (packable == null) continue;
            var packPath = AssetDatabase.GetAssetPath(packable);
            var texImporter = AssetImporter.GetAtPath(packPath) as TextureImporter;
            if (texImporter == null) continue;
//     private static int CountPackableSprites(SpriteAtlas atlas)
//     {
//         int count = 0;
//         foreach (var packable in SpriteAtlasExtensions.GetPackables(atlas))
//         {
//             if (packable == null) continue;
//             var packPath = AssetDatabase.GetAssetPath(packable);
//             var texImporter = AssetImporter.GetAtPath(packPath) as TextureImporter;
//             if (texImporter == null) continue;
            if (texImporter.spriteImportMode == SpriteImportMode.Single)
                count++;
            else if (texImporter.spriteImportMode == SpriteImportMode.Multiple)
                count += CountSpritesInTexture(packPath);
            // None / Polygon 不打包为 sprite
        }
        return count;
    }
//             if (texImporter.spriteImportMode == SpriteImportMode.Single)
//                 count++;
//             else if (texImporter.spriteImportMode == SpriteImportMode.Multiple)
//                 count += CountSpritesInTexture(packPath);
//             // None / Polygon 不打包为 sprite
//         }
//         return count;
//     }
    private static int CountSpritesInTexture(string texturePath)
    {
        var subAssets = AssetDatabase.LoadAllAssetRepresentationsAtPath(texturePath);
        int count = 0;
        for (int i = 0; i < subAssets.Length; i++)
        {
            if (subAssets[i] is Sprite)
                count++;
        }
        return count;
    }
}
//     private static int CountSpritesInTexture(string texturePath)
//     {
//         var subAssets = AssetDatabase.LoadAllAssetRepresentationsAtPath(texturePath);
//         int count = 0;
//         for (int i = 0; i < subAssets.Length; i++)
//         {
//             if (subAssets[i] is Sprite)
//                 count++;
//         }
//         return count;
//     }
// }