Animator any state transitions added by script potentially broken in 5.5 + fix

It’s possible to add any state transitions through scripts. Example:

var transition = stateMachine.AddAnyStateTransition(state);
transition.conditions = new[] {
    new AnimatorCondition {
        mode = AnimatorConditionMode.If,
        parameter = someParam
    }
};

In 5.4, this worked just fine. In 5.5, this only works if stateMachine is the base machine in a layer. If it’s a sub state machine, the transition will not show up, and not do anything. It will still be added to the asset, but be ignored by both the Animator window and the game. If you create the transition in 5.5, and then open the project in 5.4, it will show up and work again.

The really bad thing here is that if you created such a transition in 5.4 or earlier, it will now be broken.

The fix is pretty simple - transfer the transitions to the main state machine. Here’s a fix, put it in an editor window:

public class AnimatorControllerFix : EditorWindow {

    private AnimatorController controller;

    [MenuItem("Window/Custom/Animator Controller Fix", false)]
    public static void ShowWindow() {
        GetWindow<AnimatorControllerFix>();
    }

    public void OnGUI() {
        controller = EditorGUILayout.ObjectField("Controller", controller, typeof (AnimatorController), false) as AnimatorController;

        if (GUILayout.Button("Do the fix")) {
            Undo.RecordObject(controller, "Transfering transitions");

            foreach (var layer in controller.layers) {
                var mainMachine = layer.stateMachine;
                foreach (var submachineWrapper in mainMachine.stateMachines) {

                    var submachine = submachineWrapper.stateMachine;
                    var subTransitions = submachine.anyStateTransitions;

                    for (int i = 0; i < subTransitions.Length; i++) {
                        //Must copy, as the submachine.RemoveAnyStateTransition destroys the transition.
                        var copy = mainMachine.AddAnyStateTransition(subTransitions[i].destinationState);

                        copy.canTransitionToSelf = subTransitions[i].canTransitionToSelf;
                        copy.duration = subTransitions[i].duration;
                        copy.exitTime = subTransitions[i].exitTime;
                        copy.hasExitTime = subTransitions[i].hasExitTime;
                        copy.hasFixedDuration = subTransitions[i].hasFixedDuration;
                        copy.interruptionSource = subTransitions[i].interruptionSource;
                        copy.offset = subTransitions[i].offset;
                        copy.orderedInterruption = subTransitions[i].orderedInterruption;
                        copy.conditions = subTransitions[i].conditions;
                        copy.destinationState = subTransitions[i].destinationState;
                        copy.destinationStateMachine = subTransitions[i].destinationStateMachine;
                        copy.isExit = subTransitions[i].isExit;
                        copy.mute = subTransitions[i].mute;
                        copy.solo = subTransitions[i].solo;
                    }

                    for (int i = 0; i < subTransitions.Length; i++) {
                        var transition = subTransitions[i];
                        submachine.RemoveAnyStateTransition(transition);
                    }
                }
            }
        }
    }
}

I’ve run this on our affected controllers, and it seems to work fine. I’ve got no guarantees though - always use source control!

if @DavidGeoffroy or @Mecanim-Dev are interested, the bug report for this is 867568.

IIRC, those transitions looked like they were working in 5.4, but they were actually ignored. Only the ones on the top level SM were effective.

We hid them in 5.5, since they were misleading. I could swear I had added a warning somewhere that informed you of that fact, probably at build time (where we can verify that your SM is indeed the top level one).

We had a discussion about supporting any state transitions on other SMs (by transposing them to the base SM automatically) but this would limit the possibility of supporting any state transitions per state machine in the future.

Thanks Baste to alert on this.
I was following the documentation Unity - Scripting API: AnimatorController and I was getting no transition using the AddAnyStateTransition method in Unity 5.5.0f3.
When I switch to Unity 5.4.4 it worked so I start looking until I found this and like you said the solution is to change the AddAnyStateTransition to the main state machine instead of the child one.

I alert that unity documentation is wrong, where it has: var resetTransition = stateMachineA.AddAnyStateTransition(stateA1); it should be: var resetTransition = rootStateMachine.AddAnyStateTransition(stateA1);

Indeed.
I’ll get this fixed ASAP

@DavidGeoffroy Any news on this issue? Since it still doesn’t seem to work in Unity 2018.4.
Is there a case number in the issue tracker?

Hey guys, just found a pretty nasty bug related to the Animator State Machine - if you mute a transition - the animation gets executed.
More details about the issue can be found here Unity Issue Tracker - The &#39;Mute&#39; transition flag in the Sub-State Machine gets ignored after switching muted transitions

I would really appreciate your help in voting up this issue in order to be solved asap.

Thank you!