Unity 包体如何缩减优化(二)

396 阅读4分钟

       上一篇《Unity 包体如何缩减优化(一)》我们讨论了Resouces文件夹的缩减以及纹理格式的缩减,这一篇将讨论余下的内容。

       如果我们游戏内有很多的小图片,又不能比4整除,也不是2的N次方,这样就算我们把它们设置成ETC2 8bits 也无济于事,unity在构建过程中会自动变为RGBA32或RGBA16。要避免这种情况出现,我们可以这么做

1、按被4整除或2的次方制作贴图

2、打图集

       被4整除好理解,图片宽高整除4就可以了,我们重点讨论一下图集。

       图集简单来说有两个优点,一是将各种小图打包到一张符合规则的大图中,相对来说可以缩减一小部分包体,二是可以减少大部分Drawcall,提升性能。下面我们讨论一下如何打图集:

点击Edit 菜单,打开Project Settings 窗口,如下图:

上面这个设置是开启图集功能,开启后可以创建图集,如下图:

创建完图集,可以将要打包的图片放进去,如下图:

      这样一个图集就打好了,但每次都手动这么操作,显然也是不明智的,我们还是要提倡自动化。下面我们通过脚本来自动生成图集,如下:

打开我们上篇的贴图管理器,添加如下代码:

using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEditor.U2D;
using UnityEngine.U2D;
using UnityEngine;

namespace EditorAsset
{
    /// <summary>
    /// 贴图管理器
    /// </summary>
    public class AssetTextureMgr
    {       
        private static int _maxAltalSize = 2048;
        //按文件夹创建图集,不包括子文件夹
        public static void CreateAtlas(string dir)
        {
            var files = AssetDir.GetAssetChildePath(dir, new string[] { ".png" }, null);
            if (files.Length > 0)
            {
                var packSettting = new SpriteAtlasPackingSettings();
                packSettting.enableRotation = true;
                packSettting.padding = 4;
                packSettting.enableTightPacking = false;
                packSettting.blockOffset = 1;
                packSettting.enableAlphaDilation = true;

                var textureSetting = new SpriteAtlasTextureSettings();
                textureSetting.sRGB = true;
                textureSetting.filterMode = UnityEngine.FilterMode.Bilinear;

                var androidSettings = new TextureImporterPlatformSettings();
                androidSettings.name = "Android";
                androidSettings.overridden = true;
                androidSettings.maxTextureSize = _maxAltalSize;
                androidSettings.format = _androidAlphaDefaultFormat;

                var iosSetting = new TextureImporterPlatformSettings();
                iosSetting.name = "iPhone";
                iosSetting.overridden = true;
                iosSetting.maxTextureSize = _maxAltalSize;
                iosSetting.format = _iosDefaultFormat;

                var atlas = new SpriteAtlas();
                atlas.IsIncludeInBuild();
                atlas.SetPackingSettings(packSettting);
                atlas.SetTextureSettings(textureSetting);
                atlas.SetPlatformSettings(iosSetting);
                atlas.SetPlatformSettings(androidSettings);
           
                var atlasName = Path.GetFileName(dir); 
                var altasFile = Path.Combine(dir, atlasName + ".spriteatlas");
          
                var sprites = new List<Sprite>();
                foreach (var f in files)
                {
                    var sprite = AssetDatabase.LoadAssetAtPath<Sprite>(f);
                    if(sprite != null )
                    { sprites.Add(sprite); }
                }
                if(sprites.Count > 0)
                    atlas.Add(sprites.ToArray());

                AssetDatabase.CreateAsset(atlas, altasFile);
                AssetDatabase.SaveAssets();
                Selection.activeObject = AssetDatabase.LoadAssetAtPath<SpriteAtlas>(altasFile);
            }              
        }
        //按文件夹创建图集,包括所有子文件夹
        public static void CreateAtlasAndChilde(string dir)
        {
            var dirs = AssetDir.GetAssetDir(dir, null);
            foreach (var d in dirs)
            {
                CreateAtlas(d);
            }
        }
    }

}

这样我们创建图集的代码就OK了,下面我们再做一个菜单功能,只要点击某个文件夹就可以将该文件夹下面的图片全部打包进图集,如下:

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;

namespace EditorAsset
{
    public class AssetTextureAutomation
    {
        [MenuItem("Assets/刷新TextureSettings")]
        private static void RefreshTextureSettings()
        {
            var dir = AssetDatabase.GUIDToAssetPath(Selection.assetGUIDs[0]);
            var files = AssetDir.GetAssetPath(dir, new string[] { ".png" }, null, null);
            foreach (var f in files)
            {
                AssetTextureMgr.SetAndroidSettings(f);
                AssetTextureMgr.SetIosSettings(f);
            }
            Debug.Log($"----------Fresh settings ok. count {files.Length}");
        }

        [MenuItem("Assets/刷新TextureSettings", true)]
        private static bool IsSelected1()
        {
            return Selection.assetGUIDs != null && Selection.assetGUIDs.Length > 0;
        }

        [MenuItem("Assets/创建和刷新Atlas")]
        private static void CreateAtlas()
        {
            var dir = AssetDatabase.GUIDToAssetPath(Selection.assetGUIDs[0]);                    
            AssetTextureMgr.CreateAtlas(dir);
            Debug.Log($"----------Create atlas ok, name {Path.GetFileName(dir)}");
        }
        [MenuItem("Assets/创建和刷新Atlas", true)]
        private static bool IsSelected2()
        {
            return Selection.assetGUIDs != null && Selection.assetGUIDs.Length > 0;
        }

        [MenuItem("Assets/创建和刷新所有Atlas")]
        private static void RefreshAtlas()
        {
            var dir = AssetDatabase.GUIDToAssetPath(Selection.assetGUIDs[0]);
            AssetTextureMgr.CreateAtlasAndChilde(dir);

        }

        [MenuItem("Assets/创建和刷新所有Atlas", true)]
        private static bool IsSelected3()
        {
            return Selection.assetGUIDs != null && Selection.assetGUIDs.Length > 0;
        }
    }
}

最终的效果如下图所示:

自动化操作下来,效果是不是杠杠的呢。

做完这一步,纹理的缩减优化基本上告一段落了。下面我们继续讨论代码和引用库的缩减。

三、代码、引用库的缩减

       代码和引用库,Unity会自动分析引用和依赖关系,将要用到的代码库等打进包体,没用到的剥离。根据这一原则,我们可以做以下优化:

1、将没用的包删除(保险一点),如下图:

这部分插件包,有些我们项目根本不会用到的,要删掉。如果不删除,unity会把代码和插件内的贴图都会打进去,无端端的多了几百K甚至几M,不值得。如timeline包,如果项目内不会用到,unity还是会打包进去,打开打包日志就可以查看到记录。

2、发布平台时,指定目标架构,这一点可以省不少,如下图:

3、代码裁剪

       如果前面所有的步骤都操作完了,还是没有达到缩减要求,可以考虑代码裁剪。代码裁剪有一定的要求和风险,建议大家谨慎操作。一般情况下做完上面提到的要点,包体基本上符合要求了,在此代码裁剪就不再展开讨论。

       笔者经过文章中一系列的操作,成功将包体从120多M缩减到70M左右,如下图所示。其实还有继续缩减的空间,如重复资源的打包策略,图集的打包策略等,这些都和包体大小有关。

(完)