Unity AB package body size optimization

1 Model Texture map optimization

In the project AB, there are a lot of characters, so the AB size of the character model is also relatively large. On the one hand, the character model must control the number of vertices and triangles, and on the other hand, it is the optimization of model texture resources. When art is used as resources, in order to pursue quality , the model textures are often made very large. Let’s take our current project as an example. The model textures used in art are all 4096*4096. In Unity, you must not do this. 4096 you let the low-end machine I'm still playing with a bird, so the Max Size of the texture is limited to 2048.
The original resource format is as follows. A model contains 3 maps in this format, namely uv map, normal map, and a grayscale map. The typed AB resource is 5.7M.
insert image description here
insert image description here
Considering that our project is an RPG turn-based game, there will only be multiple characters in the battle scene, and the distance from the camera will not change significantly, so it is recommended to remove MipMap. Then package AB, the resource size is 4.1M.
The next thing to deal with is the texture compression. I mainly tested Android here. Since the textures do not have a transparent channel, the UV texture and grayscale texture adopt the RGBCrunchedETC format, and the normal texture adopts the RGBCompressETC4bit method. The AB resource size is 2M
If the normal map is also compressed according to the RGBCrunchedETC method, the size of the AB resource can be reduced to 1.4M, which depends on the display effect.
There is also the setting of Max Size, it is best to carry out classification control according to the display effect of the model

2TimeLine resource optimization

Project combat skills, we use TimeLine to make it. I checked the TimeLine resources today. A TimeLine prefab resource has reached more than 1M, and some of them are 2M. My God, all of you art friends What did I do to TimeLine, so I used the AssetBundleBorswer tool to analyze the TimeLine AB resources of the No. 00003 character's ultimate move. Why are there a bunch of animation and timeline resources of other characters.
insert image description here
Then I took a look at the original resources in the unity editor
insert image description here
insert image description here
, and it seemed that there was nothing wrong with it. Where did this messy resource come from? Since AB thinks that they have a reference relationship, there must be a problem. The verification is being carried out in the meta file. This time it didn't disappoint me. I saw this bunch of referenced resources under the m_SceneBindings attribute, and I was speechless.
insert image description here
Then go back to unity, turn on the debug mode, re-examine the resources, blame me for being too young, not understanding the sinister human heart, and almost being fooled by you, these 106 resource references, can the typed AB be small, hey!
insert image description here
The resources I actually need are only the three resources displayed when the debug mode is turned off. What
insert image description here
should I do? Since I am a code farmer, let’s use the spirit of moving bricks, Du Niang Du Niang, I want to move bricks , and then Du Niang gave me a link
https://zhuanlan.zhihu.com/p/396526134
It really is a fellow, thank you to the host for helping us, and then copy and paste, a modification tool is ready

    [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();
    }

Done! By the way, for the animaton resources used by TimeLine, remember to compress their floating-point precision values ​​and scale channels. Usually, we don’t use such a high precision. The AB was relatively large before, except for the redundant resources, there is no animation compression. In the end, the AB resource was reduced from 1.6M to 132k by me, Nice.
insert image description here
insert image description here

Animation optimization

core code

    [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;
    }

Guess you like

Origin blog.csdn.net/u011484013/article/details/119839565