Parameters and States of Animator Reset on Object's Disable

Hi whenever I disable an object with an animator or its parent the animator states and all its parameters go back to default. In addition the object still stays where I animated it, for example is I have the object animated in its x axis + 5; the object still stays in the positive 5 position even if the states and all its booleans are reset. I was wondering if there is a work around to this so that when I reenable the objects the states stay the same from where I left it.

For those who hit this thread recently (and use at least Unity 2018.1), use Animator.keepAnimatorControllerStateOnDisable = true.

I had the same problem, where I wanted to hide the character during teleportation; I did create this workaround. Unfortunately, by the time OnDisable is called, the anim.parameters array is empty (probably another bug), so you actually have to call OnAnimDisable before disabling the object. Then, the state will be saved and restored OnEnable. Just attach this script to the object with the Animator on it. I don’t know if this will work for triggers though, as I don’t use any in my project. It works for bools, floats and ints though.

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class PreserveAnimatorOnDisable : MonoBehaviour {

	private class AnimParam {
		public AnimatorControllerParameterType type;
		public string paramName;
		object data;

		public AnimParam (Animator anim, string paramName, AnimatorControllerParameterType type) {
			this.type = type;
			this.paramName = paramName;
			switch (type) {
			case AnimatorControllerParameterType.Int: 
				this.data = (int)anim.GetInteger(paramName);
				break;
			case AnimatorControllerParameterType.Float: 
				this.data = (float)anim.GetFloat(paramName);
				break;
			case AnimatorControllerParameterType.Bool: 
				this.data = (bool)anim.GetBool(paramName);
				break;
			}
		}
		public object getData() {
			return data;
		}
	}

	Animator anim;
	List<AnimParam> parms = new List<AnimParam>();

	void Start() {
		anim = GetComponent<Animator>();
	}

	public void OnAnimDisable() {
		Debug.Log("Saving Animator state: " + anim.parameters.Length);
		for (int i = 0; i < anim.parameters.Length; i++)
		{
			AnimatorControllerParameter p = anim.parameters*;*
  •  	AnimParam ap = new AnimParam(anim, p.name, p.type);*
    
  •  	parms.Add(ap);*
    
  •  }*
    
  • }*

  • void OnEnable() {*

  •  Debug.Log("Restoring Animator state.");*
    
  •  foreach(AnimParam p in parms)*
    
  •  {*
    
  •  	switch (p.type)*
    
  •  	{*
    
  •  	case AnimatorControllerParameterType.Int:*
    
  •  		anim.SetInteger(p.paramName,(int)p.getData());*
    
  •  		break;*
    
  •  	case AnimatorControllerParameterType.Float:*
    
  •  		anim.SetFloat(p.paramName,(float)p.getData());*
    
  •  		break;*
    
  •  	case AnimatorControllerParameterType.Bool:*
    
  •  		anim.SetBool(p.paramName,(bool)p.getData());*
    
  •  		break;*
    
  •  	}*
    
  •  }*
    
  •  parms.Clear();*
    
  • }*
    }

Might be useful for somebody else: I used @Skeldal’s solution but couldn’t use @FamerJoe’s solution (not working in this case) to swap the animation override controller at runtime, so I also used parts of @wallstop’s answer. I also needed the layer weights to remain so I added this as well. I expressed the code as an extension of the Animator class, so you can call the function on the animator itself:

animator.SwitchAnimatorController(newController);

With the extension looking like this:

using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public static class AnimatorEx
{
    public static void SwitchAnimatorController(this Animator animator, RuntimeAnimatorController animatorController)
    {
        // save states
        List<AnimParam> parms = SaveAnimationStates(animator);
        float[] layerWeights = SaveLayerWeights(animator);
        AnimatorStateInfo[] animatorStateInfo = SaveLayerStateInfo(animator);
        
        // swap
        animator.runtimeAnimatorController = animatorController;
        
        // restore states
        RerstoreAnimationState(parms, animator);
        RestoreLayerWeights(animator, layerWeights);
        RestaureLayerStateInfor(animator, animatorStateInfo);
    }

    private static AnimatorStateInfo[] SaveLayerStateInfo(Animator animator)
    {
        int animatorLayerCount = animator.layerCount;
        AnimatorStateInfo[] animatorStateInfo = new AnimatorStateInfo[animatorLayerCount];
        for (int i = 0; i < animatorLayerCount; i++)
        {
            animatorStateInfo *= animator.GetCurrentAnimatorStateInfo(i);*

}
return animatorStateInfo;
}

private static void RestaureLayerStateInfor(Animator animator, AnimatorStateInfo[] animatorStateInfo)
{
for (int i = 0; i < animator.layerCount; i++)
{
animator.Play(animatorStateInfo_.shortNameHash, i, animatorStateInfo*.normalizedTime);
}
}*_

private static float[] SaveLayerWeights(Animator animator)
{
int animatorLayerCount = animator.layerCount;
float[] layerWeights = new float[animatorLayerCount];
for (int i = 0; i < animatorLayerCount; i++)
{
layerWeights = animator.GetLayerWeight(i);
}
return layerWeights;
}

private static void RestoreLayerWeights(Animator animator, float[] layerWeights)
{
for (int i = 0; i < layerWeights.Length; i++)
{
animator.SetLayerWeight(i, layerWeights*);*
}
}

private class AnimParam
{
public AnimatorControllerParameterType type;
public string paramName;
object data;

public AnimParam(Animator animator, string paramName, AnimatorControllerParameterType type)
{
this.type = type;
this.paramName = paramName;
switch (type)
{
case AnimatorControllerParameterType.Int:
this.data = (int) animator.GetInteger(paramName);
break;
case AnimatorControllerParameterType.Float:
this.data = (float) animator.GetFloat(paramName);
break;
case AnimatorControllerParameterType.Bool:
this.data = (bool) animator.GetBool(paramName);
break;
}
}

public object getData()
{
return data;
}
}

static List SaveAnimationStates(Animator animator)
{
List parms = new List();
foreach (AnimatorControllerParameter p in animator.parameters)
{
AnimParam ap = new AnimParam(animator, p.name, p.type);
parms.Add(ap);
}
return parms;
}

static void RerstoreAnimationState(List parms, Animator animator)
{
foreach (AnimParam p in parms)
{
switch (p.type)
{
case AnimatorControllerParameterType.Int:
animator.SetInteger(p.paramName, (int) p.getData());
break;
case AnimatorControllerParameterType.Float:
animator.SetFloat(p.paramName, (float) p.getData());
break;
case AnimatorControllerParameterType.Bool:
animator.SetBool(p.paramName, (bool) p.getData());
break;
}
}
}
}
It might be a bit more heavy computationally though since it creates a new list each times, but I don’t think it’s much, and it’s a bit more practical since you don’t need to add the script to the GameObject. Hope it helps somebody :slight_smile:

Here’s a script that will restores full state, including current animation and progress within the animation, as well as trigger values. @deeds0l

    /// <summary>
    /// Will restore an Animator to it's full state whenever it's game object is disabled or enabled.
    /// </summary>
    /// <note>
    /// Requires the animator to be BENEATH this component in the component heirarchy on the object.
    /// Nothing special required for script execution order.
    /// </note>
    [RequireComponent(typeof (Animator))]
    public sealed class RehydrateAnimator : MonoBehaviour
    {
        private Animator _animator;

        private readonly Dictionary<int, AnimatorStateInfo> _stateInfoByLayer;
        private readonly Dictionary<AnimatorControllerParameter, object> _parameterValues;

        public RehydrateAnimator()
        {
            _stateInfoByLayer = new Dictionary<int, AnimatorStateInfo>();
            _parameterValues = new Dictionary<AnimatorControllerParameter, object>();
        }

        private void Start()
        {
            _animator = GetComponent<Animator>();
        }

        private void OnEnable()
        {
            if (!_animator)
            {
                return;
            }

            foreach (KeyValuePair<int, AnimatorStateInfo> layerAndStateInfo in _stateInfoByLayer)
            {
                int layer = layerAndStateInfo.Key;
                AnimatorStateInfo stateInfo = layerAndStateInfo.Value;
                _animator.Play(stateInfo.shortNameHash, layer, stateInfo.normalizedTime);
            }

            foreach (KeyValuePair<AnimatorControllerParameter, object> parameterAndValue in _parameterValues)
            {
                AnimatorControllerParameter parameter = parameterAndValue.Key;
                object value = parameterAndValue.Value;
                switch (parameter.type)
                {
                    case AnimatorControllerParameterType.Bool:
                        _animator.SetBool(parameter.name, (bool) value);
                        break;
                    case AnimatorControllerParameterType.Float:
                        _animator.SetFloat(parameter.name, (float) value);
                        break;
                    case AnimatorControllerParameterType.Int:
                        _animator.SetInteger(parameter.name, (int) value);
                        break;
                    case AnimatorControllerParameterType.Trigger:
                        if ((bool) value)
                        {
                            _animator.SetTrigger(parameter.name);
                        }

                        break;
                    default:
                        continue;
                }
            }

            ResetInternalState();
        }

        private void OnDisable()
        {
            ResetInternalState();
            if (!_animator)
            {
                return;
            }

            for (int i = 0; i < _animator.layerCount; ++i)
            {
                _stateInfoByLayer *= _animator.GetCurrentAnimatorStateInfo(i);*

}

foreach (AnimatorControllerParameter parameter in _animator.parameters)
{
object value;
switch (parameter.type)
{
case AnimatorControllerParameterType.Bool:
case AnimatorControllerParameterType.Trigger:
value = _animator.GetBool(parameter.name);
break;
case AnimatorControllerParameterType.Float:
value = _animator.GetFloat(parameter.name);
break;
case AnimatorControllerParameterType.Int:
value = _animator.GetInteger(parameter.name);
break;
default:
continue;
}

_parameterValues[parameter] = value;
}
}

private void ResetInternalState()
{
_stateInfoByLayer.Clear();
_parameterValues.Clear();
}
}