Animator Controller Data leak

The data within the Animator Controller (*.controller) get duplicated and can get out of control

You can see a video of the issue here

Step to reproduce:

first open the Animator controller source file in a text editor and see how many lines it has.

1- Copy the nodes in the animator controller.
2- Delete them
3- Paste
3- Paste (twice)
4- Save & Save the project
5- Undo until the nodes reappears
6- Save & Save the project
7- Open the Animator controller source file in a text editor and see that lines have approx doubled.


Also in the same style, if you create a blend tree that contain blend trees, if you delete the main blend tree, all the data of the other blend trees remains in the source of the .controller file

Use Unity’s inbuilt bug reporter in the Help menu.

I’ve done it too, ofc

Does anyone have a good method of deleting all this duplicated data easily? I also have AnimatorStateTransitions for parameters that I’ve long since deleted.

No, this is an issue everywhere inside unity.

It’s the way the engine works. in the video, I’m highlighting the issue with the animator controller, which also loads in memory all the lost/unlinked animation files.

A similar issue happens to the materials.

Unless you manually clean it, there is no way to fix it on your side. This is a massive flaw in the engine and should be taken seriously.

here is the old bug report I’ve done:

In my file, each AnimatorState or AnimatorStateTransition is preceded by — !u!110#.

It seems like a script could parse through and identify each state following each of these entries, and then check for keywords following m_ConditionEvent: and remove the section if found. I don’t have much experience with reading/writing external files in Unity. Maybe something in Python if this is something Unity’s not able to do?

Yeah, but be careful you need to make sure they are not listed anywhere else.
You will need to parse it all, and then list what is not really used and delete the hierarchy starting with the deepest child.

What I did was to profile the memory, find the unused animations and textures files that were too big in the memory, then look at their uID in the meta, and then do a text search to unlink all of them.

And that took me more than a week… but at least we won 400MB within the RAM, on small devices, it’s huge!

With the help of some other posts, I modified a script to search through the Controller and delete entries.

The easiest way to use it is to rename the Animator Parameters, and then search and delete the old names using the script because those aren’t updated.

using UnityEngine;
using UnityEditor;
using UnityEditor.Animations;
public class CleanAnimatorController
{
    [MenuItem("Tools/Clean AnimationController")]
    public static void CleanAnimationController()
    {
        AnimatorController controller = Selection.activeObject as AnimatorController;
        Object[] assetList = AssetDatabase.LoadAllAssetsAtPath(AssetDatabase.GetAssetPath(controller));
        for (int i = 0; i < assetList.Length; i++) {
            bool deleteAsset = false;
            Object asset = assetList[i];
            if (!AssetDatabase.IsMainAsset(asset)) {

                // Delete by Name.
                if (asset.name.Contains("Launcher")) {
                    Object.DestroyImmediate(assetList[i], true);
                }

                // Animator State Transitions.
                if (asset.GetType() == typeof(AnimatorStateTransition)) {
                    AnimatorStateTransition ast = ( AnimatorStateTransition )asset;

                    //Check Duration.
                    if (ast.duration == 0.25) {
                        Debug.Log(ast.name);
                        deleteAsset = true;
                    }

                    // Single Condition.
                    if (ast.conditions.Length == 1) {

                        for (int a = 0; a < ast.conditions.Length; a++) {
                            if (ast.conditions[a].parameter.Contains("Talk")) {
                                Debug.Log(ast.conditions[a].parameter);
                                deleteAsset = true;
                            }
                        }
                    }

                    // Check Conditions.
                    for (int a = 0; a < ast.conditions.Length; a++) {

                        if (ast.conditions[a].parameter.Contains("Action")) {
                            Debug.Log(ast.conditions[a].parameter);
                            deleteAsset = true;
                        }
                    }
                }

                // Animator States.
                if (asset.GetType() == typeof(AnimatorState)) {
                    AnimatorState animState = ( AnimatorState )asset;

                    // Check Speed.
                    if (animState.speedParameter.Contains("Velocity X")) {
                        Debug.Log(animState.name);
                        deleteAsset = true;
                    }
                }

                //Animator Transition.
                if (asset.GetType() == typeof(AnimatorTransition)) {
                    AnimatorTransition animState = ( AnimatorTransition )asset;
                    if (animState.name.Contains("Trigger")) {
                        Debug.Log(animState.name);
                        deleteAsset = true;
                    }
                }
            }
            if (deleteAsset) {
                Object.DestroyImmediate(asset, true);
            }
        }

        // Refresh the AssetDatabase
        AssetDatabase.Refresh();
    }

    [MenuItem("Tools/Clean AnimationController", true)]
    public static bool ValidateCleanAnimationController()
    {
        Object selection = Selection.activeObject;
        return selection != null && selection.GetType() == typeof(AnimatorController);
    }
}