天天看點

unity AB包體大小優化

1模型Texture 貼圖優化

項目AB裡面,角色的數量比較多,進而角色模型的AB體積也比較龐大,角色模型一方面要控制好頂點 三角面數量,另一方面就是模型貼圖資源優化,美術做資源的時候,為了追求品質,經常會把模型貼圖搞的特别大,就拿我們現在這個項目來說吧,美術用的的模型貼圖都是4096*4096的,到了unity裡面,肯定不能這樣搞要,4096你讓低端機還玩個鳥呀,是以限定了貼圖 Max Size為2048。

原始的資源格式如下,一個模型裡面包含3張這樣格式的貼圖,分别是uv貼圖,法線貼圖,和一張灰階貼圖,打出來的AB資源是5.7M。

unity AB包體大小優化
unity AB包體大小優化

考慮到我們的項目是RPG 回合制遊戲,角色隻有在戰鬥場景中會存在多個,而且距離錄影機距離沒有特别明顯的變動,是以就建議去掉MipMap。然後打包AB,資源大小為4.1M。

接下來要處理的就是貼圖壓縮了,我這裡主要測試了Android的,由于貼圖都不帶透明通道,是以UV貼圖和灰階貼圖采用了RGBCrunchedETC格式,法線貼圖采用了RGBCompressETC4bit 方式,AB資源大小為2M

如果法線貼圖也按照RGBCrunchedETC方式進行壓縮的話,AB資源大小可以降到1.4M,這個根據根據顯示效果而定。

還有就是 Max Size的設定,最好是根據模型顯示效果進行分類控制

2TimeLine 資源優化

項目戰鬥技能,我們采用的是TimeLine制作的,今天檢視了一下TimeLine資源,一個TimeLine 的prefab資源,竟然達到了1M多,有個别的都2M了,我的天呢,你們這群美術小夥伴都對TimeLine做了啥,于是我就用AssetBundleBorswer工具分析了一下00003号角色大招的 TimeLine AB資源,不看不知道,一看吓一跳,這些都是什麼鬼呀。為什麼會有一堆其他角色的animation和timeline資源。

unity AB包體大小優化

然後我看了一眼 unity 編輯器中的原始資源

unity AB包體大小優化
unity AB包體大小優化

資源好像也沒啥毛病,那這一堆亂七八糟的資源是從哪裡來的,既然AB認為他們有引用關系,那肯定還是有問題的,隻能從meta檔案中在進行求證了。這次倒是沒有讓我失望,在m_SceneBindings屬性下面看到了這一堆被引用的資源,無語了。

unity AB包體大小優化

然後回到unity,開啟debug 模式,重新審視資源,怪我太年輕,不懂人心險惡,差點兒就被你蒙混過關了,這106個資源引用,打出來的AB能小嗎,哎!

unity AB包體大小優化

我實際需要的資源,隻有關閉debug模式的時候,顯示的這三個資源呀

unity AB包體大小優化

我該腫麼辦,既然是碼農,那就發揮出搬磚精神吧,度娘度娘,我要搬磚了,然後度娘給了我一個連結

https://zhuanlan.zhihu.com/p/396526134

果然是同道中人,感謝樓主幫我們趟坑,然後複制粘貼,一個修改工具就做好了

[MenuItem("Assets/CleanUpPlayableBind")]
    private static void CleanUpPlayableBind()
    {
        GameObject gob = Selection.activeGameObject as GameObject;
        if (gob != null) {
            var playable = gob.GetComponent<PlayableDirector>();
            CleanUpBind(playable);
        }
    }

    public static void CleanUpBind(PlayableDirector playable)
    {
        if (playable == null) return;
        Dictionary<UnityEngine.Object, UnityEngine.Object> bindings = new Dictionary<UnityEngine.Object, UnityEngine.Object>();
        foreach (var pb in playable.playableAsset.outputs)
        {
            var key = pb.sourceObject;
            var value = playable.GetGenericBinding(key);
            if (!bindings.ContainsKey(key) && key != null)
            {
                bindings.Add(key, value);
            }
        }

        var dirSO = new UnityEditor.SerializedObject(playable);
        var sceneBindings = dirSO.FindProperty("m_SceneBindings");
        for (var i = sceneBindings.arraySize - 1; i >= 0; i--)
        {
            var binding = sceneBindings.GetArrayElementAtIndex(i);
            var key = binding.FindPropertyRelative("key");
            if (key.objectReferenceValue == null || !bindings.ContainsKey(key.objectReferenceValue))
                sceneBindings.DeleteArrayElementAtIndex(i);
        }
        dirSO.ApplyModifiedProperties();
    }

           

搞定!順便提一嘴,TimeLine用到的animaton資源,記得壓縮它們的浮點數精度值和scale通道,通常情況下我們用不到這麼高的精度,之前打AB比較大,除了多餘的資源,animation也沒有壓縮。最終這個AB資源從1.6M,被我降低到了132k,Nice。

unity AB包體大小優化
unity AB包體大小優化

Animation優化

核心代碼

[MenuItem("Assets/AnimationClipChange/Directory/降低精度并删除Scale通道")]
    private static void ChangeAnimFloatAndScaleDirectory()
    {
        Object gob = Selection.activeObject;
        string path = AssetDatabase.GetAssetPath(gob);
        if (Directory.Exists(path))
        {
            string[] udids = AssetDatabase.FindAssets("t:AnimationClip", new string[] { path });
            int index = 0;
            foreach (var item in udids)
            {
                string itemPath = AssetDatabase.GUIDToAssetPath(item);
                EditorUtility.DisplayProgressBar("執行中..." + path, itemPath, (float)index / udids.Length);

                AnimationClip clip = AssetDatabase.LoadAssetAtPath<AnimationClip>(itemPath);
                optmizeAnimationFloatAndScale(clip);
                index++;
            }
            EditorUtility.ClearProgressBar();
            Debug.LogColor("AnimationClip change float finish!");
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();
        }
        else
        {
            Debug.LogError("select target is not a directory:" + path);
        }

    }


    static void optmizeAnimationFloatAndScale(AnimationClip targetAnimClip)
    {
        optmizeAnimationScaleCurve(targetAnimClip);
        optmizeAnimationFloat(targetAnimClip);
        Resources.UnloadUnusedAssets();
        
    }


    /// <summary>
    /// 優化浮點數精度
    /// </summary>
    static AnimationClip optmizeAnimationFloat(AnimationClip clip)
    {
        //浮點數精度壓縮到f3
        AnimationClipCurveData[] curves = null;
        curves = AnimationUtility.GetAllCurves(clip);
        Keyframe key;
        Keyframe[] keyFrames;
        for (int ii = 0; ii < curves.Length; ++ii)
        {
            AnimationClipCurveData curveDate = curves[ii];
            if (curveDate.curve == null || curveDate.curve.keys == null)
            {
                //Debug.LogWarning(string.Format("AnimationClipCurveData {0} don't have curve; Animation name {1} ", curveDate, animationPath));
                continue;
            }
            keyFrames = curveDate.curve.keys;
            for (int i = 0; i < keyFrames.Length; i++)
            {
                key = keyFrames[i];
                key.value = float.Parse(key.value.ToString("f3"));
                key.inTangent = float.Parse(key.inTangent.ToString("f3"));
                key.outTangent = float.Parse(key.outTangent.ToString("f3"));
                keyFrames[i] = key;
            }
            curveDate.curve.keys = keyFrames;
            clip.SetCurve(curveDate.path, curveDate.type, curveDate.propertyName, curveDate.curve);
        }

        return clip;
    }

    /// <summary>
    /// 優化scale曲線
    /// </summary>
    static AnimationClip optmizeAnimationScaleCurve(AnimationClip clip)
    {
        //去除scale曲線
        foreach (EditorCurveBinding theCurveBinding in AnimationUtility.GetCurveBindings(clip))
        {
            string name = theCurveBinding.propertyName.ToLower();
            if (name.Contains("scale"))
            {
                AnimationUtility.SetEditorCurve(clip, theCurveBinding, null);
            }
        }

        return clip;
    }