• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Unity编辑器扩展包体优化神器,图片压缩,批量生成图集/图集变体,动画压缩

武飞扬头像
TopGames
帮助1

功能介绍:

1. 压缩工具支持对图片原文件压缩(支持png/jpg),也支持使用Unity内置图片压缩批量对图片设置压缩参数。

2. 支持以文件夹或及其子文件夹为单位批量生成图集(SpriteAtlas), 支持同时生成图集变体(SpriteAtlas Variant),支持忽略像素宽高大于限定值的图片打进图集。

3. 批量给现有图集(SpriteAtlas)生成图集变体,生成图集变体后可以调整图集的缩放

4. 动画压缩,降低animation clip序列化文件的浮点型精度,保留较少的小数以降低文件大小。

工具预览:

学新通

工具完整代码参见开源框架GF_HybridCLR


图片无疑是游戏资源大户,无论数量还是文件大小占比都非常高。使用TinyPng等压缩工具压缩,近乎疯狂的压缩比,通常可以将图片文件大小降低70%左右。

对于Cocos2d-x时代的项目通常都会使用TinyPng进行图片压缩

然而对于Unity来说,压缩图片虽然能大幅降低图片文件大小,但是最终打出的包(AssetBundle或Addressables)文件大小并不会明显降低,甚至会比压缩图片前还大。这是因为Unity针对不同平台都有对应的图片压缩模式,无论你再怎么压缩,Unity导入图片或打包时都会再次使用对应平台的压缩方式重新压缩图片,这就导致在图片分辨率不变的情况下,最终打包后的资源大小并不能有效降低。

学新通
OpenAI的问答结果

但是。。。没错,还有但是,对于庞大的项目来说,合理压缩图片原文件可以有效降低工程大小,提高打开工程的加载速度等。然后通过工具的Unity内置图片压缩批量操作可以快速方便设置图片压缩参数。

AssetBundle提供了LZ4和LZMA两种压缩方式:

LZ4: 压缩/解压较快,压缩后的文件大。适用于在线压缩/解压、网络数据等需要频繁压缩/解压的情况。

LZMA: 压缩/解压较慢,压缩后的文件小。适用于对文件大小要求高,且不频繁压缩/解压的情况。

以上两种压缩再结合GF的额外压缩,又能进一步降低包体大小。当然,也需要根据需求平衡文件加载速度和文件资源大小的取舍。

模式一:图片原文件压缩模式

一,图片原文件压缩工具功能设计:

 1. 压缩算法的选择:

tinypng在线压缩 pngquantImageSharp离线压缩:

tinypng压缩比极高,支持png/jpg/webp, 并且提供了包括.Net的多种编程语言API支持,适合做批处理。但是,tinypng需要上传图片到服务器,压缩完后还要下载压缩后的图片。图片较大较多时处理过程会巨慢。如果有离线压缩算法就完美了,离线压缩库使用的是pngquantImageSharp,都是开源压缩算法:

pngquant: 只支持png压缩,对png的压缩比接近tinypng;也可从官网可以下载命令行工具,支持windows和mac;

ImageSharp:C#实现,跨平台。对jpg的压缩比tinypng还要好。

Tinypng API : TinyPNG – API Reference

 2. 添加需要压缩的文件/文件夹,并在列表中显示已经添加的文件/文件夹,支持添加/删除:

如上图,用户可以点击列表的" "号弹出Unity自带的资源选择界面(支持选择文件夹/图片文件),

但是Unity自带选择界面仅支持单选,所以还需要做个拖拽功能以支持批量添加。

3. 压缩设置项:

对于tinypng,需要注册序列号,每个序列号可以免费压缩500张。可以一次配置多个序列号,压缩时取首行序列号。

学新通

离线压缩:对于png格式,勾选离线压缩后使用pngquant本地压缩。

覆盖原图片:勾选后压缩后的图片直接覆盖原图。

压缩质量(仅对pngquant离线压缩有效):为区间数值(min, max),当压缩质量小于min时则不对该图片压缩,其实就是为了把图片控制在一定质量范围,不至于太糊。

快压等级:等级越高,压缩处理速度越快,但压缩比随之小幅降低。一般为了极致压缩比会把快压等级调到最低。

输出路径:压缩后的图片存放路径。

备份路径:点击备份会自动把当前选择的原图备份到指定目录,以便后续还原需求。

4. 功能:

功能按钮包含压缩、备份、还原、保存当前设置。

二,功能实现:

下载tinypng压缩库:可以在Visual Studio的NuGet中搜索下载tinypng库,然后把dll放入Unity工程。

学新通

 下载pngquant命令行版(有Window,Mac版本),放入Unity工程。

1. tinypng在线压缩:

  1.  
    /// <summary>
  2.  
    /// 使用TinyPng在线压缩,支持png,jpg,webp
  3.  
    /// </summary>
  4.  
    private async Task<bool> CompressOnlineAsync(string imgFileName, string outputFileName)
  5.  
    {
  6.  
    if (string.IsNullOrWhiteSpace(TinifyAPI.Tinify.Key))
  7.  
    {
  8.  
    return false;
  9.  
    }
  10.  
     
  11.  
    var srcImg = TinifyAPI.Tinify.FromFile(imgFileName);
  12.  
    await srcImg.ToFile(outputFileName);
  13.  
    return srcImg.IsCompletedSuccessfully;
  14.  
    }

 2. pngquant和ImageSharp本地压缩:

  1.  
    /// <summary>
  2.  
    /// 使用ImageSharp压缩jpg图片
  3.  
    /// </summary>
  4.  
    /// <param name="imgFileName"></param>
  5.  
    /// <param name="outputFileName"></param>
  6.  
    /// <returns></returns>
  7.  
    private static bool CompressJpgOffline(string imgFileName, string outputFileName)
  8.  
    {
  9.  
    using (var img = SixLabors.ImageSharp.Image.Load(imgFileName))
  10.  
    {
  11.  
    var encoder = new SixLabors.ImageSharp.Formats.Jpeg.JpegEncoder()
  12.  
    {
  13.  
    Quality = (int)AppBuildSettings.Instance.CompressImgToolQualityLv
  14.  
    };
  15.  
    using (var outputStream = new FileStream(outputFileName, FileMode.Create))
  16.  
    {
  17.  
    img.Save(outputStream, encoder);
  18.  
    }
  19.  
     
  20.  
    }
  21.  
     
  22.  
    return true;
  23.  
    }
  24.  
    /// <summary>
  25.  
    /// 使用pngquant压缩png图片
  26.  
    /// </summary>
  27.  
    /// <param name="imgFileName"></param>
  28.  
    /// <param name="outputFileName"></param>
  29.  
    /// <returns></returns>
  30.  
    private static bool CompressPngOffline(string imgFileName, string outputFileName)
  31.  
    {
  32.  
    string pngquant = Path.Combine(Directory.GetParent(Application.dataPath).FullName, pngquantTool);
  33.  
     
  34.  
    StringBuilder strBuilder = new StringBuilder();
  35.  
    strBuilder.AppendFormat(" --force --quality {0}-{1}", (int)AppBuildSettings.Instance.CompressImgToolQualityMinLv, (int)AppBuildSettings.Instance.CompressImgToolQualityLv);
  36.  
    strBuilder.AppendFormat(" --speed {0}", AppBuildSettings.Instance.CompressImgToolFastLv);
  37.  
    strBuilder.AppendFormat(" --output \"{0}\"", outputFileName);
  38.  
    strBuilder.AppendFormat(" -- \"{0}\"", imgFileName);
  39.  
     
  40.  
    var proceInfo = new System.Diagnostics.ProcessStartInfo(pngquant, strBuilder.ToString());
  41.  
    proceInfo.CreateNoWindow = true;
  42.  
    proceInfo.UseShellExecute = false;
  43.  
    bool success;
  44.  
    using (var proce = System.Diagnostics.Process.Start(proceInfo))
  45.  
    {
  46.  
    proce.WaitForExit();
  47.  
    success = proce.ExitCode == 0;
  48.  
    if (!success)
  49.  
    {
  50.  
    Debug.LogWarningFormat("离线压缩图片:{0}失败,ExitCode:{1}", imgFileName, proce.ExitCode);
  51.  
    }
  52.  
    }
  53.  
    return success;
  54.  
    }

3. 弹出Unity编辑器内置资源选择窗口:

学新通

 通过反射调用Unity编辑器内置选文件窗口,需要注意反射调用不支持重载函数,所以需要把参数填写完整才能成功调用:

  1.  
    public class EditorUtilityExtension
  2.  
    {
  3.  
     
  4.  
    /// <summary>
  5.  
    /// 选择相对工程路径文件夹
  6.  
    /// </summary>
  7.  
    /// <param name="title">标题</param>
  8.  
    /// <param name="relativePath">默认打开的路径(相对路径)</param>
  9.  
    /// <returns></returns>
  10.  
    public static string OpenRelativeFolderPanel(string title, string relativePath)
  11.  
    {
  12.  
    var rootPath = Directory.GetParent(Application.dataPath).FullName;
  13.  
    var curFullPath = Path.Combine(rootPath, relativePath);
  14.  
    var selectPath = EditorUtility.OpenFolderPanel(title, curFullPath, curFullPath);
  15.  
     
  16.  
    return string.IsNullOrWhiteSpace(selectPath) ? selectPath : Path.GetRelativePath(rootPath, selectPath);
  17.  
    }
  18.  
     
  19.  
    /// <summary>
  20.  
    /// 打开UnityEditor内置文件选择界面
  21.  
    /// </summary>
  22.  
    /// <param name="assetTp"></param>
  23.  
    /// <param name="searchFilter"></param>
  24.  
    /// <param name="onObjectSelectorClosed"></param>
  25.  
    /// <param name="objectSelectorID"></param>
  26.  
    /// <returns></returns>
  27.  
    public static bool OpenAssetSelector(Type assetTp, string searchFilter = null, Action<UnityEngine.Object> onObjectSelectorClosed = null, int objectSelectorID = 0)
  28.  
    {
  29.  
    var objSelector = Utility.Assembly.GetType("UnityEditor.ObjectSelector");
  30.  
    var objSelectorInst = objSelector?.GetProperty("get", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public)?.GetValue(objSelector);
  31.  
    if (objSelectorInst == null) return false;
  32.  
     
  33.  
    var objSelectorInstTp = objSelectorInst.GetType();
  34.  
    var showFunc = objSelectorInstTp.GetMethod("Show", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance, null, new System.Type[] { typeof(UnityEngine.Object), typeof(Type), typeof(UnityEngine.Object), typeof(bool), typeof(List<int>), typeof(Action<UnityEngine.Object>), typeof(Action<UnityEngine.Object>) }, null);
  35.  
    if (showFunc == null) return false;
  36.  
     
  37.  
    showFunc.Invoke(objSelectorInst, new object[] { null, assetTp, null, false, null, onObjectSelectorClosed, null });
  38.  
    if (!string.IsNullOrEmpty(searchFilter))
  39.  
    {
  40.  
    objSelectorInstTp.GetProperty("searchFilter", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(objSelectorInst, searchFilter);
  41.  
    }
  42.  
     
  43.  
    objSelectorInstTp.GetField("objectSelectorID", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).SetValue(objSelectorInst, objectSelectorID);
  44.  
     
  45.  
    return true;
  46.  
    }
  47.  
    }

4. 拖拽批量添加功能:

  1.  
    private void DrawDropArea()
  2.  
    {
  3.  
    var dragRect = EditorGUILayout.BeginVertical("box");
  4.  
    {
  5.  
    GUILayout.FlexibleSpace();
  6.  
    EditorGUILayout.LabelField(dragAreaContent, centerLabelStyle);
  7.  
    if (dragRect.Contains(Event.current.mousePosition))
  8.  
    {
  9.  
    if (Event.current.type == EventType.DragUpdated)
  10.  
    {
  11.  
    DragAndDrop.visualMode = DragAndDropVisualMode.Generic;
  12.  
    }
  13.  
    else if (Event.current.type == EventType.DragExited)
  14.  
    {
  15.  
    if (DragAndDrop.objectReferences != null && DragAndDrop.objectReferences.Length > 0)
  16.  
    {
  17.  
    OnItemsDrop(DragAndDrop.objectReferences);
  18.  
    }
  19.  
     
  20.  
    }
  21.  
    }
  22.  
    GUILayout.FlexibleSpace();
  23.  
    EditorGUILayout.EndVertical();
  24.  
    }
  25.  
    }
  26.  
     
  27.  
    /// <summary>
  28.  
    /// 拖拽松手
  29.  
    /// </summary>
  30.  
    /// <param name="objectReferences"></param>
  31.  
    /// <exception cref="NotImplementedException"></exception>
  32.  
    private void OnItemsDrop(UnityEngine.Object[] objectReferences)
  33.  
    {
  34.  
    foreach (var item in objectReferences)
  35.  
    {
  36.  
    if (CheckItemType(item) == ItemType.NoSupport)
  37.  
    {
  38.  
    Debug.LogWarningFormat("添加失败! 不支持的文件格式:{0}", AssetDatabase.GetAssetPath(item));
  39.  
    continue;
  40.  
    }
  41.  
    AddItem(item);
  42.  
    }
  43.  
    }

模式二:Unity内置压缩批处理

学新通

 1. 通过编辑器脚本批量修改图片TextureImporter属性:

学新通

 通过下面代码可以获取上图红色区域的设置参数:

  1.  
    var texSetting = new TextureImporterSettings();
  2.  
    texImporter.ReadTextureSettings(texSetting);

 通过下面代码可以获取针对各个平台的设置参数:

  1.  
    var texImporter = AssetImporter.GetAtPath(assetName) as TextureImporter;
  2.  
    var texPlatformSetting = texImporter.GetPlatformTextureSettings(EditorUserBuildSettings.activeBuildTarget.ToString());

 需要注意的是,不同平台支持的图片压缩方式(Format)不同,可以通过反射调用Unity内置API获取对应平台支持的所有Format类型以供下拉选择:

  1.  
    var getOptionsFunc = Utility.Assembly.GetType("UnityEditor.TextureImportValidFormats").GetMethod("GetPlatformTextureFormatValuesAndStrings", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
  2.  
    var paramsObjs = new object[] { TextureImporterType.Sprite, EditorUserBuildSettings.activeBuildTarget, null, null };
  3.  
    getOptionsFunc.Invoke(null, paramsObjs);
  4.  
     
  5.  
    var formatValues = paramsObjs[2] as int[];
  6.  
    var formatDisplayOptions = paramsObjs[3] as string[];
  7.  
     
  8.  
     
  9.  
    ...
  10.  
    //Format
  11.  
    EditorGUILayout.BeginHorizontal();
  12.  
    {
  13.  
    overrideFormat = EditorGUILayout.ToggleLeft("Format", overrideFormat, GUILayout.Width(150));
  14.  
    EditorGUI.BeginDisabledGroup(!overrideFormat);
  15.  
    {
  16.  
    compressPlatformSettings.format = (TextureImporterFormat)EditorGUILayout.IntPopup((int)compressPlatformSettings.format, formatDisplayOptions, formatValues);
  17.  
    EditorGUI.EndDisabledGroup();
  18.  
    }
  19.  
    EditorGUILayout.EndHorizontal();
  20.  
    }

2. 通过EditorUtility.FormatBytes(UnityEditor.TextureUtil.GetStorageMemorySizeLong(texture))方法可以获取到对应压缩格式的文件占用大小,这样就可以通过自动比对筛选出最合适的压缩格式。此方法不是公开方法,需要通过反射调用。

3. 编辑器代码判断贴图是否符合压缩格式要求

学新通 

比如ETC2要求图片像素宽高必须是4的倍数,Crunch格式要求图片宽高必须为POT(即2的N次方),对于不支持的压缩的贴图Unity还给了贴心警告,压缩失败时压缩格式会回滚到默认的通用格式,会造成贴图大小不降反升。所以需要判断贴图是否压缩成功,如果失败了就设置一个相对通用的压缩格式。

遗憾的是在Unity开源代码中并没有找到直接获取是否压缩成功的方法,但是可以通过判断是否有警告字符以判断是否压缩成功:

  1.  
    /// <summary>
  2.  
    /// 检测贴图是否适用压缩格式
  3.  
    /// </summary>
  4.  
    /// <param name="texImporter"></param>
  5.  
    /// <param name="warning"></param>
  6.  
    /// <returns></returns>
  7.  
    bool CheckTexFormatValid(TextureImporter texImporter, out string warning)
  8.  
    {
  9.  
    var impWarningFunc = texImporter.GetType().GetMethod("GetImportWarnings", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
  10.  
    warning = impWarningFunc.Invoke(texImporter, null) as string;
  11.  
    return string.IsNullOrWhiteSpace(warning);
  12.  
    }

模式三:创建图集,图集变体

 功能需求:

1. 根据用户选定的文件夹,支持以文件夹或文件夹及其子文件夹为单位批量创建图集(每个文件夹生成一个图集文件),并且支持忽略把像素宽/高大于限制大小的图片打进图集。

2. 创建AtlasVariant,AtlasVariant是用来按比例缩放SpriteAtlas的,用于资源大小优化。勾选AtlasVariant后,生成图集同时生成AtlasVariant。

3. 其他图集设置参数,同图集的Inspector设置面板。

需要注意的是SpriteAtlas目前有v1和v2两个版本,图集格式分别为spriteatlas和spriteatlasv2:

学新通

 两个版本的图集创建方法不同, v1是SpriteAtlas,v2是SpriteAtlasAsset,可通过EditorSettings.spritePackerMode获取当前使用的图集版本。

使用编辑器代码创建图集(SpriteAtlas),支持v1和v2:

  1.  
    /// <summary>
  2.  
    /// 创建图集
  3.  
    /// </summary>
  4.  
    /// <param name="atlasFilePath"></param>
  5.  
    /// <param name="settings"></param>
  6.  
    /// <param name="objectsForPack"></param>
  7.  
    /// <param name="createAtlasVariant"></param>
  8.  
    /// <param name="atlasVariantScale"></param>
  9.  
    /// <returns></returns>
  10.  
    public static SpriteAtlas CreateAtlas(string atlasName, AtlasSettings settings, UnityEngine.Object[] objectsForPack, bool createAtlasVariant = false, float atlasVariantScale = 1f)
  11.  
    {
  12.  
    CreateEmptySpriteAtlas(atlasName);
  13.  
    SpriteAtlas result;
  14.  
    if (EditorSettings.spritePackerMode == SpritePackerMode.SpriteAtlasV2)
  15.  
    {
  16.  
    var atlas = SpriteAtlasAsset.Load(atlasName);
  17.  
    atlas.SetIncludeInBuild(settings.includeInBuild ?? true);
  18.  
    atlas.Add(objectsForPack);
  19.  
    var packSettings = atlas.GetPackingSettings();
  20.  
    var texSettings = atlas.GetTextureSettings();
  21.  
    var platformSettings = atlas.GetPlatformSettings(EditorUserBuildSettings.activeBuildTarget.ToString());
  22.  
    ModifySpriteAtlasSettings(settings, ref packSettings, ref texSettings, ref platformSettings);
  23.  
    atlas.SetPackingSettings(packSettings);
  24.  
    atlas.SetTextureSettings(texSettings);
  25.  
    atlas.SetPlatformSettings(platformSettings);
  26.  
    SpriteAtlasAsset.Save(atlas, atlasName);
  27.  
     
  28.  
    result = AssetDatabase.LoadAssetAtPath<SpriteAtlas>(atlasName);
  29.  
    }
  30.  
    else
  31.  
    {
  32.  
    var atlas = AssetDatabase.LoadAssetAtPath<SpriteAtlas>(atlasName);
  33.  
    atlas.SetIncludeInBuild(settings.includeInBuild ?? true);
  34.  
    atlas.Add(objectsForPack);
  35.  
    var packSettings = atlas.GetPackingSettings();
  36.  
    var texSettings = atlas.GetTextureSettings();
  37.  
    var platformSettings = atlas.GetPlatformSettings(EditorUserBuildSettings.activeBuildTarget.ToString());
  38.  
    ModifySpriteAtlasSettings(settings, ref packSettings, ref texSettings, ref platformSettings);
  39.  
    atlas.SetPackingSettings(packSettings);
  40.  
    atlas.SetTextureSettings(texSettings);
  41.  
    atlas.SetPlatformSettings(platformSettings);
  42.  
    result = atlas;
  43.  
    AssetDatabase.SaveAssets();
  44.  
    }
  45.  
     
  46.  
    if (createAtlasVariant)
  47.  
    {
  48.  
    var atlasVarSets = new AtlasVariantSettings()
  49.  
    {
  50.  
    variantScale = atlasVariantScale,
  51.  
    readWrite = settings.readWrite,
  52.  
    mipMaps = settings.mipMaps,
  53.  
    sRGB = settings.sRGB,
  54.  
    filterMode = settings.filterMode,
  55.  
    texFormat = settings.texFormat,
  56.  
    compressQuality = settings.compressQuality
  57.  
    };
  58.  
    CreateAtlasVariant(result, atlasVarSets);
  59.  
    }
  60.  
    return result;
  61.  
    }

使用编辑器代码为指定图集创建图集变体:

  1.  
    /// <summary>
  2.  
    /// 根据图集对象生成图集变体
  3.  
    /// </summary>
  4.  
    /// <param name="atlas"></param>
  5.  
    /// <param name="settings"></param>
  6.  
    /// <returns></returns>
  7.  
    public static SpriteAtlas CreateAtlasVariant(SpriteAtlas atlasMaster, AtlasVariantSettings settings)
  8.  
    {
  9.  
    if (atlasMaster == null || atlasMaster.isVariant) return atlasMaster;
  10.  
    var atlasFileName = AssetDatabase.GetAssetPath(atlasMaster);
  11.  
    if (string.IsNullOrEmpty(atlasFileName))
  12.  
    {
  13.  
    Debug.LogError($"atlas '{atlasMaster.name}' is not a asset file.");
  14.  
    return null;
  15.  
    }
  16.  
     
  17.  
    var atlasVariantName = UtilityBuiltin.ResPath.GetCombinePath(Path.GetDirectoryName(atlasFileName), $"{Path.GetFileNameWithoutExtension(atlasFileName)}_Variant{Path.GetExtension(atlasFileName)}");
  18.  
     
  19.  
    SpriteAtlas varAtlas;
  20.  
    if (EditorSettings.spritePackerMode == SpritePackerMode.SpriteAtlasV2)
  21.  
    {
  22.  
    var atlas = SpriteAtlasAsset.Load(atlasFileName);
  23.  
    atlas.SetIncludeInBuild(false);
  24.  
    var packSettings = atlas.GetPackingSettings();
  25.  
    var texSettings = atlas.GetTextureSettings();
  26.  
    var platformSettings = atlas.GetPlatformSettings(EditorUserBuildSettings.activeBuildTarget.ToString());
  27.  
    ModifySpriteAtlasSettings(settings, ref packSettings, ref texSettings, ref platformSettings);
  28.  
    atlas.SetPackingSettings(packSettings);
  29.  
    atlas.SetTextureSettings(texSettings);
  30.  
    atlas.SetPlatformSettings(platformSettings);
  31.  
    SpriteAtlasAsset.Save(atlas, atlasFileName);
  32.  
     
  33.  
    CreateEmptySpriteAtlas(atlasVariantName);
  34.  
    var tmpVarAtlas = SpriteAtlasAsset.Load(atlasVariantName);
  35.  
    tmpVarAtlas.SetIncludeInBuild(true);
  36.  
    tmpVarAtlas.SetIsVariant(true);
  37.  
    packSettings = tmpVarAtlas.GetPackingSettings();
  38.  
    texSettings = tmpVarAtlas.GetTextureSettings();
  39.  
    platformSettings = tmpVarAtlas.GetPlatformSettings(EditorUserBuildSettings.activeBuildTarget.ToString());
  40.  
    ModifySpriteAtlasSettings(settings, ref packSettings, ref texSettings, ref platformSettings);
  41.  
    tmpVarAtlas.SetPackingSettings(packSettings);
  42.  
    tmpVarAtlas.SetTextureSettings(texSettings);
  43.  
    tmpVarAtlas.SetPlatformSettings(platformSettings);
  44.  
    tmpVarAtlas.SetMasterAtlas(atlasMaster);
  45.  
    tmpVarAtlas.SetVariantScale(settings.variantScale);
  46.  
    SpriteAtlasAsset.Save(tmpVarAtlas, atlasVariantName);
  47.  
     
  48.  
    varAtlas = AssetDatabase.LoadAssetAtPath<SpriteAtlas>(atlasVariantName);
  49.  
    }
  50.  
    else
  51.  
    {
  52.  
    var atlas = AssetDatabase.LoadAssetAtPath<SpriteAtlas>(atlasFileName);
  53.  
    atlas.SetIncludeInBuild(false);
  54.  
    var packSettings = atlas.GetPackingSettings();
  55.  
    var texSettings = atlas.GetTextureSettings();
  56.  
    var platformSettings = atlas.GetPlatformSettings(EditorUserBuildSettings.activeBuildTarget.ToString());
  57.  
    ModifySpriteAtlasSettings(settings, ref packSettings, ref texSettings, ref platformSettings);
  58.  
    atlas.SetPackingSettings(packSettings);
  59.  
    atlas.SetTextureSettings(texSettings);
  60.  
    atlas.SetPlatformSettings(platformSettings);
  61.  
     
  62.  
    CreateEmptySpriteAtlas(atlasVariantName);
  63.  
    var tmpVarAtlas = AssetDatabase.LoadAssetAtPath<SpriteAtlas>(atlasVariantName);
  64.  
    tmpVarAtlas.SetIncludeInBuild(true);
  65.  
    tmpVarAtlas.SetIsVariant(true);
  66.  
    packSettings = tmpVarAtlas.GetPackingSettings();
  67.  
    texSettings = tmpVarAtlas.GetTextureSettings();
  68.  
    platformSettings = tmpVarAtlas.GetPlatformSettings(EditorUserBuildSettings.activeBuildTarget.ToString());
  69.  
    ModifySpriteAtlasSettings(settings, ref packSettings, ref texSettings, ref platformSettings);
  70.  
    tmpVarAtlas.SetPackingSettings(packSettings);
  71.  
    tmpVarAtlas.SetTextureSettings(texSettings);
  72.  
    tmpVarAtlas.SetPlatformSettings(platformSettings);
  73.  
    tmpVarAtlas.SetMasterAtlas(atlasMaster);
  74.  
    tmpVarAtlas.SetVariantScale(settings.variantScale);
  75.  
    AssetDatabase.SaveAssets();
  76.  
     
  77.  
    varAtlas = tmpVarAtlas;
  78.  
    }
  79.  
     
  80.  
    return varAtlas;
  81.  
    }

模式四:Animation Clip动画文件大小优化

原理非常简单,动画文件的位置、旋转、缩放等数据以浮点型保存在动画文件,默认精度太高,保留了一大串小数点后的数字,实际上不需要精度过大,保留3位小数即可。降低浮点型精度可以降低动画文件大小以减少打包后包体大小。

我这里直接偷懒使用正则匹配动画文件里的小数并降低小数的精度(注意,此方式只适用于Asset Serialization位Force Text模式,不支持Force Binary,Unity工程默认是Force Text模式):

  1.  
    public static void OptimizeAnimationClips(List<string> list, int precision)
  2.  
    {
  3.  
    string pattern = $"(\\d \\.[\\d]{{{precision},}})";
  4.  
     
  5.  
    int totalCount = list.Count;
  6.  
    int finishCount = 0;
  7.  
    foreach (var itmName in list)
  8.  
    {
  9.  
    if (File.GetAttributes(itmName) != FileAttributes.ReadOnly)
  10.  
    {
  11.  
    if (Path.GetExtension(itmName).ToLower().CompareTo(".anim") == 0)
  12.  
    {
  13.  
    finishCount ;
  14.  
    if (EditorUtility.DisplayCancelableProgressBar(string.Format("压缩浮点精度({0}/{1})", finishCount, totalCount), itmName, finishCount / (float)totalCount))
  15.  
    {
  16.  
    break;
  17.  
    }
  18.  
    var allTxt = File.ReadAllText(itmName);
  19.  
    // 将匹配到的浮点型数字替换为精确到3位小数的浮点型数字
  20.  
    string outputString = Regex.Replace(allTxt, pattern, match =>
  21.  
    float.Parse(match.Value).ToString($"F{precision}"));
  22.  
    File.WriteAllText(itmName, outputString);
  23.  
    Debug.LogFormat("----->压缩动画浮点精度:{0}", itmName);
  24.  
    }
  25.  
    }
  26.  
    }
  27.  
    EditorUtility.ClearProgressBar();
  28.  
    AssetDatabase.Refresh();
  29.  
    }

 工具的功能代码:

  1.  
    using UnityEditor.U2D;
  2.  
    using UnityEditor;
  3.  
    using UnityEngine;
  4.  
    using UnityEngine.U2D;
  5.  
    using System.IO;
  6.  
    using System.Text;
  7.  
    using System.Threading.Tasks;
  8.  
    using TinifyAPI;
  9.  
    using SixLabors.ImageSharp.Processing;
  10.  
    using SixLabors.ImageSharp;
  11.  
    using GameFramework;
  12.  
    using System.Collections.Generic;
  13.  
    using System.Text.RegularExpressions;
  14.  
     
  15.  
    namespace UGF.EditorTools
  16.  
    {
  17.  
    public class AtlasSettings : IReference
  18.  
    {
  19.  
    public bool? includeInBuild = null;
  20.  
    public bool? allowRotation = null;
  21.  
    public bool? tightPacking = null;
  22.  
    public bool? alphaDilation = null;
  23.  
    public int? padding = null;
  24.  
    public bool? readWrite = null;
  25.  
    public bool? mipMaps = null;
  26.  
    public bool? sRGB = null;
  27.  
    public FilterMode? filterMode = null;
  28.  
    public int? maxTexSize = null;
  29.  
    public TextureImporterFormat? texFormat = null;
  30.  
    public int? compressQuality = null;
  31.  
    public virtual void Clear()
  32.  
    {
  33.  
    includeInBuild = null;
  34.  
    allowRotation = null;
  35.  
    tightPacking = null;
  36.  
    alphaDilation = null;
  37.  
    padding = null;
  38.  
    readWrite = null;
  39.  
    mipMaps = null;
  40.  
    sRGB = null;
  41.  
    filterMode = null;
  42.  
    maxTexSize = null;
  43.  
    texFormat = null;
  44.  
    compressQuality = null;
  45.  
    }
  46.  
    }
  47.  
    public class AtlasVariantSettings : AtlasSettings
  48.  
    {
  49.  
    public float variantScale = 0.5f;
  50.  
    public override void Clear()
  51.  
    {
  52.  
    base.Clear();
  53.  
    variantScale = 0.5f;
  54.  
    }
  55.  
    public static AtlasVariantSettings CreateFrom(AtlasSettings atlasSettings, float scale = 1f)
  56.  
    {
  57.  
    var settings = ReferencePool.Acquire<AtlasVariantSettings>();
  58.  
    settings.includeInBuild = atlasSettings.includeInBuild;
  59.  
    settings.allowRotation = atlasSettings.allowRotation;
  60.  
    settings.tightPacking = atlasSettings.tightPacking;
  61.  
    settings.alphaDilation = atlasSettings.alphaDilation;
  62.  
    settings.padding = atlasSettings.padding;
  63.  
    settings.readWrite = atlasSettings.readWrite;
  64.  
    settings.mipMaps = atlasSettings.mipMaps;
  65.  
    settings.sRGB = atlasSettings.sRGB;
  66.  
    settings.filterMode = atlasSettings.filterMode;
  67.  
    settings.maxTexSize = atlasSettings.maxTexSize;
  68.  
    settings.texFormat = atlasSettings.texFormat;
  69.  
    settings.compressQuality = atlasSettings.compressQuality;
  70.  
    settings.variantScale = scale;
  71.  
    return settings;
  72.  
    }
  73.  
    }
  74.  
    public class CompressTool
  75.  
    {
  76.  
    #if UNITY_EDITOR_WIN
  77.  
    const string pngquantTool = "Tools/CompressImageTools/pngquant_win/pngquant.exe";
  78.  
    #elif UNITY_EDITOR_OSX
  79.  
    const string pngquantTool = "Tools/CompressImageTools/pngquant_mac/pngquant";
  80.  
    #endif
  81.  
    /// <summary>
  82.  
    /// 使用TinyPng在线压缩,支持png,jpg,webp
  83.  
    /// </summary>
  84.  
    public static async Task<bool> CompressOnlineAsync(string imgFileName, string outputFileName, string tinypngKey)
  85.  
    {
  86.  
    if (string.IsNullOrWhiteSpace(tinypngKey))
  87.  
    {
  88.  
    return false;
  89.  
    }
  90.  
    Tinify.Key = tinypngKey;
  91.  
    var srcImg = TinifyAPI.Tinify.FromFile(imgFileName);
  92.  
    await srcImg.ToFile(outputFileName);
  93.  
    return srcImg.IsCompletedSuccessfully;
  94.  
    }
  95.  
     
  96.  
    /// <summary>
  97.  
    /// 使用pngquant离线压缩,只支持png
  98.  
    /// </summary>
  99.  
    public static bool CompressImageOffline(string imgFileName, string outputFileName)
  100.  
    {
  101.  
    var fileExt = Path.GetExtension(imgFileName).ToLower();
  102.  
    switch (fileExt)
  103.  
    {
  104.  
    case ".png":
  105.  
    return CompressPngOffline(imgFileName, outputFileName);
  106.  
    case ".jpg":
  107.  
    return CompressJpgOffline(imgFileName, outputFileName);
  108.  
    }
  109.  
    return false;
  110.  
    }
  111.  
    /// <summary>
  112.  
    /// 按比例缩放图片尺寸
  113.  
    /// </summary>
  114.  
    /// <param name="imgFileName"></param>
  115.  
    /// <param name="outputFileName"></param>
  116.  
    /// <param name="scale"></param>
  117.  
    /// <returns></returns>
  118.  
    public static bool ResizeImage(string imgFileName, string outputFileName, float scale)
  119.  
    {
  120.  
    using (var img = SixLabors.ImageSharp.Image.Load(imgFileName))
  121.  
    {
  122.  
    int scaleWidth = (int)(img.Width * scale);
  123.  
    int scaleHeight = (int)(img.Height * scale);
  124.  
    img.Mutate(x => x.Resize(scaleWidth, scaleHeight));
  125.  
    img.Save(outputFileName);
  126.  
    }
  127.  
    return true;
  128.  
    }
  129.  
    /// <summary>
  130.  
    /// 设置图片尺寸
  131.  
    /// </summary>
  132.  
    /// <param name="imgFileName"></param>
  133.  
    /// <param name="outputFileName"></param>
  134.  
    /// <param name="width"></param>
  135.  
    /// <param name="height"></param>
  136.  
    /// <returns></returns>
  137.  
    public static bool ResizeImage(string imgFileName, string outputFileName, int width, int height)
  138.  
    {
  139.  
    using (var img = SixLabors.ImageSharp.Image.Load(imgFileName))
  140.  
    {
  141.  
    img.Mutate(x => x.Resize(width, height));
  142.  
    img.Save(outputFileName);
  143.  
    }
  144.  
    return true;
  145.  
    }
  146.  
    /// <summary>
  147.  
    /// 使用ImageSharp压缩jpg图片
  148.  
    /// </summary>
  149.  
    /// <param name="imgFileName"></param>
  150.  
    /// <param name="outputFileName"></param>
  151.  
    /// <returns></returns>
  152.  
    private static bool CompressJpgOffline(string imgFileName, string outputFileName)
  153.  
    {
  154.  
    using (var img = SixLabors.ImageSharp.Image.Load(imgFileName))
  155.  
    {
  156.  
    var encoder = new SixLabors.ImageSharp.Formats.Jpeg.JpegEncoder()
  157.  
    {
  158.  
    Quality = (int)EditorToolSettings.Instance.CompressImgToolQualityLv
  159.  
    };
  160.  
    using (var outputStream = new FileStream(outputFileName, FileMode.Create))
  161.  
    {
  162.  
    img.Save(outputStream, encoder);
  163.  
    }
  164.  
     
  165.  
    }
  166.  
     
  167.  
    return true;
  168.  
    }
  169.  
    /// <summary>
  170.  
    /// 使用pngquant压缩png图片
  171.  
    /// </summary>
  172.  
    /// <param name="imgFileName"></param>
  173.  
    /// <param name="outputFileName"></param>
  174.  
    /// <returns></returns>
  175.  
    private static bool CompressPngOffline(string imgFileName, string outputFileName)
  176.  
    {
  177.  
    string pngquant = Path.Combine(Directory.GetParent(Application.dataPath).FullName, pngquantTool);
  178.  
     
  179.  
    StringBuilder strBuilder = new StringBuilder();
  180.  
    strBuilder.AppendFormat(" --force --quality {0}-{1}", (int)EditorToolSettings.Instance.CompressImgToolQualityMinLv, (int)EditorToolSettings.Instance.CompressImgToolQualityLv);
  181.  
    strBuilder.AppendFormat(" --speed {0}", EditorToolSettings.Instance.CompressImgToolFastLv);
  182.  
    strBuilder.AppendFormat(" --output \"{0}\"", outputFileName);
  183.  
    strBuilder.AppendFormat(" -- \"{0}\"", imgFileName);
  184.  
     
  185.  
    var proceInfo = new System.Diagnostics.ProcessStartInfo(pngquant, strBuilder.ToString());
  186.  
    proceInfo.CreateNoWindow = true;
  187.  
    proceInfo.UseShellExecute = false;
  188.  
    bool success;
  189.  
    using (var proce = System.Diagnostics.Process.Start(proceInfo))
  190.  
    {
  191.  
    proce.WaitForExit();
  192.  
    success = proce.ExitCode == 0;
  193.  
    if (!success)
  194.  
    {
  195.  
    Debug.LogWarningFormat("离线压缩图片:{0}失败,ExitCode:{1}", imgFileName, proce.ExitCode);
  196.  
    }
  197.  
    }
  198.  
    return success;
  199.  
    }
  200.  
    /// <summary>
  201.  
    /// 创建图集
  202.  
    /// </summary>
  203.  
    /// <param name="atlasFilePath"></param>
  204.  
    /// <param name="settings"></param>
  205.  
    /// <param name="objectsForPack"></param>
  206.  
    /// <param name="createAtlasVariant"></param>
  207.  
    /// <param name="atlasVariantScale"></param>
  208.  
    /// <returns></returns>
  209.  
    public static SpriteAtlas CreateAtlas(string atlasName, AtlasSettings settings, UnityEngine.Object[] objectsForPack, bool createAtlasVariant = false, float atlasVariantScale = 1f)
  210.  
    {
  211.  
    CreateEmptySpriteAtlas(atlasName);
  212.  
    SpriteAtlas result;
  213.  
    if (EditorSettings.spritePackerMode == SpritePackerMode.SpriteAtlasV2)
  214.  
    {
  215.  
    var atlas = SpriteAtlasAsset.Load(atlasName);
  216.  
    atlas.SetIncludeInBuild(settings.includeInBuild ?? true);
  217.  
    atlas.Add(objectsForPack);
  218.  
    var packSettings = atlas.GetPackingSettings();
  219.  
    var texSettings = atlas.GetTextureSettings();
  220.  
    var platformSettings = atlas.GetPlatformSettings(EditorUserBuildSettings.activeBuildTarget.ToString());
  221.  
    ModifySpriteAtlasSettings(settings, ref packSettings, ref texSettings, ref platformSettings);
  222.  
    atlas.SetPackingSettings(packSettings);
  223.  
    atlas.SetTextureSettings(texSettings);
  224.  
    atlas.SetPlatformSettings(platformSettings);
  225.  
    SpriteAtlasAsset.Save(atlas, atlasName);
  226.  
     
  227.  
    result = AssetDatabase.LoadAssetAtPath<SpriteAtlas>(atlasName);
  228.  
    }
  229.  
    else
  230.  
    {
  231.  
    var atlas = AssetDatabase.LoadAssetAtPath<SpriteAtlas>(atlasName);
  232.  
    atlas.SetIncludeInBuild(settings.includeInBuild ?? true);
  233.  
    atlas.Add(objectsForPack);
  234.  
    var packSettings = atlas.GetPackingSettings();
  235.  
    var texSettings = atlas.GetTextureSettings();
  236.  
    var platformSettings = atlas.GetPlatformSettings(EditorUserBuildSettings.activeBuildTarget.ToString());
  237.  
    ModifySpriteAtlasSettings(settings, ref packSettings, ref texSettings, ref platformSettings);
  238.  
    atlas.SetPackingSettings(packSettings);
  239.  
    atlas.SetTextureSettings(texSettings);
  240.  
    atlas.SetPlatformSettings(platformSettings);
  241.  
    result = atlas;
  242.  
    AssetDatabase.SaveAssets();
  243.  
    }
  244.  
     
  245.  
    if (createAtlasVariant)
  246.  
    {
  247.  
    var atlasVarSets = new AtlasVariantSettings()
  248.  
    {
  249.  
    variantScale = atlasVariantScale,
  250.  
    readWrite = settings.readWrite,
  251.  
    mipMaps = settings.mipMaps,
  252.  
    sRGB = settings.sRGB,
  253.  
    filterMode = settings.filterMode,
  254.  
    texFormat = settings.texFormat,
  255.  
    compressQuality = settings.compressQuality
  256.  
    };
  257.  
    CreateAtlasVariant(result, atlasVarSets);
  258.  
    }
  259.  
    return result;
  260.  
    }
  261.  
    private static void ModifySpriteAtlasSettings(AtlasSettings input, ref SpriteAtlasPackingSettings packSets, ref SpriteAtlasTextureSettings texSets, ref TextureImporterPlatformSettings platSets)
  262.  
    {
  263.  
    packSets.enableRotation = input.allowRotation ?? packSets.enableRotation;
  264.  
    packSets.enableTightPacking = input.tightPacking ?? packSets.enableTightPacking;
  265.  
    packSets.enableAlphaDilation = input.alphaDilation ?? packSets.enableAlphaDilation;
  266.  
    packSets.padding = input.padding ?? packSets.padding;
  267.  
    texSets.readable = input.readWrite ?? texSets.readable;
  268.  
    texSets.generateMipMaps = input.mipMaps ?? texSets.generateMipMaps;
  269.  
    texSets.sRGB = input.sRGB ?? texSets.sRGB;
  270.  
    texSets.filterMode = input.filterMode ?? texSets.filterMode;
  271.  
    platSets.overridden = null != input.maxTexSize || null != input.texFormat || null != input.compressQuality;
  272.  
    platSets.maxTextureSize = input.maxTexSize ?? platSets.maxTextureSize;
  273.  
    platSets.format = input.texFormat ?? platSets.format;
  274.  
    platSets.compressionQuality = input.compressQuality ?? platSets.compressionQuality;
  275.  
    }
  276.  
    /// <summary>
  277.  
    /// 根据文件夹名字返回一个图集名
  278.  
    /// </summary>
  279.  
    /// <param name="folder"></param>
  280.  
    /// <returns></returns>
  281.  
    public static string GetAtlasExtensionV1V2()
  282.  
    {
  283.  
    return EditorSettings.spritePackerMode == SpritePackerMode.SpriteAtlasV2 ? ".spriteatlasv2" : ".spriteatlas";
  284.  
    }
  285.  
    public static void CreateEmptySpriteAtlas(string atlasAssetName)
  286.  
    {
  287.  
    if (EditorSettings.spritePackerMode == SpritePackerMode.SpriteAtlasV2)
  288.  
    {
  289.  
    SpriteAtlasAsset.Save(new SpriteAtlasAsset(), atlasAssetName);
  290.  
    }
  291.  
    else
  292.  
    {
  293.  
    AssetDatabase.CreateAsset(new SpriteAtlas(), atlasAssetName);
  294.  
    }
  295.  
    AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
  296.  
    }
  297.  
    /// <summary>
  298.  
    /// 根据图集对象生成图集变体
  299.  
    /// </summary>
  300.  
    /// <param name="atlas"></param>
  301.  
    /// <param name="settings"></param>
  302.  
    /// <returns></returns>
  303.  
    public static SpriteAtlas CreateAtlasVariant(SpriteAtlas atlasMaster, AtlasVariantSettings settings)
  304.  
    {
  305.  
    if (atlasMaster == null || atlasMaster.isVariant) return atlasMaster;
  306.  
    var atlasFileName = AssetDatabase.GetAssetPath(atlasMaster);
  307.  
    if (string.IsNullOrEmpty(atlasFileName))
  308.  
    {
  309.  
    Debug.LogError($"atlas '{atlasMaster.name}' is not a asset file.");
  310.  
    return null;
  311.  
    }
  312.  
     
  313.  
    var atlasVariantName = UtilityBuiltin.ResPath.GetCombinePath(Path.GetDirectoryName(atlasFileName), $"{Path.GetFileNameWithoutExtension(atlasFileName)}_Variant{Path.GetExtension(atlasFileName)}");
  314.  
     
  315.  
    SpriteAtlas varAtlas;
  316.  
    if (EditorSettings.spritePackerMode == SpritePackerMode.SpriteAtlasV2)
  317.  
    {
  318.  
    var atlas = SpriteAtlasAsset.Load(atlasFileName);
  319.  
    atlas.SetIncludeInBuild(false);
  320.  
    var packSettings = atlas.GetPackingSettings();
  321.  
    var texSettings = atlas.GetTextureSettings();
  322.  
    var platformSettings = atlas.GetPlatformSettings(EditorUserBuildSettings.activeBuildTarget.ToString());
  323.  
    ModifySpriteAtlasSettings(settings, ref packSettings, ref texSettings, ref platformSettings);
  324.  
    atlas.SetPackingSettings(packSettings);
  325.  
    atlas.SetTextureSettings(texSettings);
  326.  
    atlas.SetPlatformSettings(platformSettings);
  327.  
    SpriteAtlasAsset.Save(atlas, atlasFileName);
  328.  
     
  329.  
    CreateEmptySpriteAtlas(atlasVariantName);
  330.  
    var tmpVarAtlas = SpriteAtlasAsset.Load(atlasVariantName);
  331.  
    tmpVarAtlas.SetIncludeInBuild(true);
  332.  
    tmpVarAtlas.SetIsVariant(true);
  333.  
    packSettings = tmpVarAtlas.GetPackingSettings();
  334.  
    texSettings = tmpVarAtlas.GetTextureSettings();
  335.  
    platformSettings = tmpVarAtlas.GetPlatformSettings(EditorUserBuildSettings.activeBuildTarget.ToString());
  336.  
    ModifySpriteAtlasSettings(settings, ref packSettings, ref texSettings, ref platformSettings);
  337.  
    tmpVarAtlas.SetPackingSettings(packSettings);
  338.  
    tmpVarAtlas.SetTextureSettings(texSettings);
  339.  
    tmpVarAtlas.SetPlatformSettings(platformSettings);
  340.  
    tmpVarAtlas.SetMasterAtlas(atlasMaster);
  341.  
    tmpVarAtlas.SetVariantScale(settings.variantScale);
  342.  
    SpriteAtlasAsset.Save(tmpVarAtlas, atlasVariantName);
  343.  
     
  344.  
    varAtlas = AssetDatabase.LoadAssetAtPath<SpriteAtlas>(atlasVariantName);
  345.  
    }
  346.  
    else
  347.  
    {
  348.  
    var atlas = AssetDatabase.LoadAssetAtPath<SpriteAtlas>(atlasFileName);
  349.  
    atlas.SetIncludeInBuild(false);
  350.  
    var packSettings = atlas.GetPackingSettings();
  351.  
    var texSettings = atlas.GetTextureSettings();
  352.  
    var platformSettings = atlas.GetPlatformSettings(EditorUserBuildSettings.activeBuildTarget.ToString());
  353.  
    ModifySpriteAtlasSettings(settings, ref packSettings, ref texSettings, ref platformSettings);
  354.  
    atlas.SetPackingSettings(packSettings);
  355.  
    atlas.SetTextureSettings(texSettings);
  356.  
    atlas.SetPlatformSettings(platformSettings);
  357.  
     
  358.  
    CreateEmptySpriteAtlas(atlasVariantName);
  359.  
    var tmpVarAtlas = AssetDatabase.LoadAssetAtPath<SpriteAtlas>(atlasVariantName);
  360.  
    tmpVarAtlas.SetIncludeInBuild(true);
  361.  
    tmpVarAtlas.SetIsVariant(true);
  362.  
    packSettings = tmpVarAtlas.GetPackingSettings();
  363.  
    texSettings = tmpVarAtlas.GetTextureSettings();
  364.  
    platformSettings = tmpVarAtlas.GetPlatformSettings(EditorUserBuildSettings.activeBuildTarget.ToString());
  365.  
    ModifySpriteAtlasSettings(settings, ref packSettings, ref texSettings, ref platformSettings);
  366.  
    tmpVarAtlas.SetPackingSettings(packSettings);
  367.  
    tmpVarAtlas.SetTextureSettings(texSettings);
  368.  
    tmpVarAtlas.SetPlatformSettings(platformSettings);
  369.  
    tmpVarAtlas.SetMasterAtlas(atlasMaster);
  370.  
    tmpVarAtlas.SetVariantScale(settings.variantScale);
  371.  
    AssetDatabase.SaveAssets();
  372.  
     
  373.  
    varAtlas = tmpVarAtlas;
  374.  
    }
  375.  
     
  376.  
    return varAtlas;
  377.  
    }
  378.  
    /// <summary>
  379.  
    /// 根据Atlas文件名为Atlas生成Atlas变体(Atlas Variant)
  380.  
    /// </summary>
  381.  
    /// <param name="atlasFile"></param>
  382.  
    /// <param name="settings"></param>
  383.  
    /// <returns></returns>
  384.  
    public static SpriteAtlas CreateAtlasVariant(string atlasFile, AtlasVariantSettings settings)
  385.  
    {
  386.  
    var atlas = AssetDatabase.LoadAssetAtPath<SpriteAtlas>(atlasFile);
  387.  
     
  388.  
    return CreateAtlasVariant(atlas, settings);
  389.  
    }
  390.  
     
  391.  
    /// <summary>
  392.  
    /// 批量重新打包图集
  393.  
    /// </summary>
  394.  
    /// <param name="spriteAtlas"></param>
  395.  
    public static void PackAtlases(SpriteAtlas[] spriteAtlas)
  396.  
    {
  397.  
    SpriteAtlasUtility.PackAtlases(spriteAtlas, EditorUserBuildSettings.activeBuildTarget);
  398.  
    }
  399.  
     
  400.  
    public static void OptimizeAnimationClips(List<string> list, int precision)
  401.  
    {
  402.  
    string pattern = $"(\\d \\.[\\d]{{{precision},}})";
  403.  
     
  404.  
    int totalCount = list.Count;
  405.  
    int finishCount = 0;
  406.  
    foreach (var itmName in list)
  407.  
    {
  408.  
    if (File.GetAttributes(itmName) != FileAttributes.ReadOnly)
  409.  
    {
  410.  
    if (Path.GetExtension(itmName).ToLower().CompareTo(".anim") == 0)
  411.  
    {
  412.  
    finishCount ;
  413.  
    if (EditorUtility.DisplayCancelableProgressBar(string.Format("压缩浮点精度({0}/{1})", finishCount, totalCount), itmName, finishCount / (float)totalCount))
  414.  
    {
  415.  
    break;
  416.  
    }
  417.  
    var allTxt = File.ReadAllText(itmName);
  418.  
    // 将匹配到的浮点型数字替换为精确到3位小数的浮点型数字
  419.  
    string outputString = Regex.Replace(allTxt, pattern, match =>
  420.  
    float.Parse(match.Value).ToString($"F{precision}"));
  421.  
    File.WriteAllText(itmName, outputString);
  422.  
    Debug.LogFormat("----->压缩动画浮点精度:{0}", itmName);
  423.  
    }
  424.  
    }
  425.  
    }
  426.  
    EditorUtility.ClearProgressBar();
  427.  
    AssetDatabase.Refresh();
  428.  
    }
  429.  
    }
  430.  
    }
  431.  
     

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhfhbgig
系列文章
更多 icon
同类精品
更多 icon
继续加载