For some reason, when I set a bool mecanim parameter as “true” through code, that setting disappears (becomes false) upon deactivating/reactivating the object through code with SetActive(). HOWEVER, if that parameter was set to “true” through the state machine editor (and then deactivated/reactivated through code), its setting persists through both deactivation AND reactivation (in my case, this is the desired behavior – except I want it to do this through code too!)
Any idea how to get the setting through code to persist through deactivation/reactivation too?
Or is this a bug?
If so, is there any workaround? (I’m using 4.6.1 if that helps!) I need it to be able to remember the bool setting from code in order to fork my state transition at the start of the animation the next time it plays (i.e. after being reactivated). I’m playing an intro animation only once, then it should transition directly to the other animation state every time after that (upon being reactivated), making this decision initially upon gameobject reactivation!
I started a conversation with him with the same title as this topic.
Not sure if that’s the right/fastest way to go about reporting this, but I can’t find any issues on the unity issue tracker that even remotely relates to this Bool Parameter / SetActive(false); issue, therefore I’m not sure if it is indeed a known issue or not.
It definitely is an issue though – and I need a work around ASAP.
Though, since mecanim is a closed system, I don’t know how much I can do… if anything at all. D:
I doubt you’ll find it in the bug tracker, as one could claim that it’s “by design.” Although, if I remember correctly, I think they’re planning to change this in a future version.
For a workaround, you can use a script this like:
public class SaveTheBools : MonoBehaviour {
public string boolName;
private Animator animator = null;
private bool boolValue;
void Start() {
animator = GetComponent<Animator>();
if (animator != null) boolValue = animator.GetBool(boolName);
}
void OnEnable() {
if (animator != null) animator.SetBool(boolName, boolValue);
}
void OnDisable() {
if (animator != null) boolValue = animator.GetBool(boolName);
}
}
This saves the bool just before you deactivate the GameObject, and restores the value when you re-activate it. Pardon typos; I typed this straight into the forum editor, so I also haven’t tested it for all cases. For example, I’m not sure what happens if the Animator gets disabled before this script does.
Thank you for that workaround. To avoid the Animator getting disabled first, I had to move it below the calling script (otherwise I got an error/warning about parameters – which can’t be checked whether they exist or not). I had completely forgotten about the OnEnable/Disable events. They work for my case for right now, but I really hope Unity fixes such an obvious bug. I would hope they wouldn’t use that whole “it’s by design” thing. That is such an obvious cop-out for such wildly inconsistent behavior.
@Mecanim Devs (if you guys are reading this):
This bug should probably raise a flag that Mecanim is too walled-in. Just the fact that people still use the old animation system because it’s easier to understand and work with should be a solid case that Mecanim really does need some reworking in the “consistency / accessibility” department – (more akin to the new GUI system treatment in 4.6 where it was made open-source and also more consistent with standard workflows in Unity by using gameobjects for the gui, in which something similar could be done with Mecanim to allow it to be more malleable and standardized w/the rest of the Unity interface.)
Logic-driven animation, for example, is such a vital part of modern interactive applications, and yet, because Mecanim is such a walled-garden, this is painfully hard to accomplish a lot of the time. Attaching behaviors to states is a great first step in Unity 5, but the Animator is still a black-box in cases where you might want to modify its internal behavior (or add your own – which might potentially even be completely decoupled from any Animation at all!) – while retaining the ability to set and even define states and parameters – through code if desired – to go alongside Mecanim’s native state logic flow.
Without a core logic state machine to couple with the animation state machine, what you end up with is a ton of bastard-child code to handle logic used alongside Unity’s Mecanim to handle animation, tossing parameters and blendtrees to the side (except in cases where they’re really needed, which, until Unity 5, used to require me to Frankenstein all other systems together somehow with Mecanim – if possible – or write my own sub-par version of Mecanim if it wasn’t.)
I love Unity, but this could be SO much better if you could seamlessly provide the comfort and control of a native state system like Mecanim while still allowing users to modify its functionality through code – even down to individual curves – if we wanted to.
Not complaining – Just offering some food for thought!
You setup the default value of all you parameters in the editor, when your game object is enabled everything is reset to the default value: parameter, state machine current state is reset to the default state.
There is no built in workaround for this. But it should be easy to write a small script based on a scriptable object that push and pop all your parameter value on enable and disable like @TonyLi did.
As a service we could add a new feature for the animator. Something like Animator.keepCurrentState that would keep all the internal data. The reason why you lose everything on enable/disable is because normally in unity when an object is disable it does free all the resources used by the gameobject: memory, asset, handle, etc…
Yes, that service would be MUCH appreciated by new users (and old users alike!) – also, maybe consider offering a way to set what data you want to preserve between de/re-activation (to keep things optimized, since optimization was the key reason I was de/re-activating my gameobjects to begin with!)
In my case, I used the state editor to debug my code, so I thought (for weeks) it was somehow my own code having the “bug” somewhere in it, and wasted a ton of time until I (finally) gave up and came on here. Even when I did, nobody seemed to know whether this was a bug or intended behavior – which is a huge problem for Unity’s “user-experience” side of things.
To me, this really did seem like a bug (it’s inconsistent with the mecanim editor’s de/re-activation behavior after all), and anyone not familiar with the internals of Unity (i.e. a non-Unity-Dev) would expect the same thing ( i.e. it’s a bug in their own implementation, or after thorough bug-testing, mecanim is working but SetActive or SetBool is broken somehow.)
I love Unity – it’s an amazing platform! – but tiny quirks like these need TLC too! – (otherwise frustration ensues! D:)
Is there any way to call SetActive(false) and then have the state machine re-enter where I deactivated the object?
I would like to be able to call SetActive(false), but it seems that even if I cache the bool parameters, the statemachine will re-enter into the orange state and not the state where I called SetActive(false).
Is there any way to do this? I would like to be able to call SetActive(false) on UI elements that exit the screen, but they have to re-enter from the direction that they exited
@Mecanim-Dev - I tried implementing a hybrid solution for keeping Animator state across disable/enable via a script like @TonyLi but calling the GetCurrentAnimatorStateInfo() function. Something like this:
However, I find that the AnimatorStateInfo struct that I get back in line 18 has 0 for all of its members - this doesn’t seem like a valid state - is it? One thing to note - my state machine is a pure state machine, that is, it has no animations/avatar. Does this type of state machine not return a valid GetCurrentAnimatorStateInfo()? Or is calling this inside OnDisable() too late?
Any help would be greatly appreciated. For now, I am using a variable outside of the state machine to track what state it’s in and then I call animator.Play(“stateName”), but it would be best if I could get that information from Mecanim somehow instead of duplicating the information in my class.
I’ve been looking around and found no acceptable working solution for general Animator loss of state/params when disabling the gameobject. @Mecanim-Dev : Removing the Animator data when disabling parent gameobjects sounds strange to me as this is convenient way to reduce overhead, in which case it should not immediately mean removing the object resources, after all wouldn’t you be destroying the gameobject if you want that ? (or is that like a Unity no mem leakage absolute rule ?)
Anyways, while writing my own AnimatorStateSaver, I’ve hit the same problem as virtra_asilva with the null animator.GetCurrentAnimatorStateInfo(0) in the OnDisable event method. Even setting a very low script execution order doesn’t change that. So my guess is that the Animator instance is cleaned up even before OnDisable events are handled.
Eventually I think the lesser evil would be to call a static function to save the animation data before disabling a gameobject here’s the code:
using System;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// Saves Animator state and restores it automatically when the object is activated
/// Code must explicitly call Reset() to reinit the animator,
/// and call AnimatorStateSaver.SaveAnimStateRecursive() before deactivating any parent
/// </summary>
public class AnimatorStateSaver : MonoBehaviour
{
private AnimatorStateInfo _savedAnimatorStateInfo;
private AnimatorControllerParameter[] _savedParameters;
private Dictionary<string, System.Object> _savedParameterValues = new Dictionary<string, System.Object>();
private Animator _animator = null;
private bool _isSaveAvailable = false;
public static void SaveAnimStateRecursive(GameObject pGameObject)
{
AnimatorStateSaver[] animatorStateSaversArray = pGameObject.GetComponentsInChildren<AnimatorStateSaver>(false);
for(int i=0; i<animatorStateSaversArray.Length; i++)
{
animatorStateSaversArray[i].SaveAnimState();
}
}
public void Start() {
_animator = GetComponent<Animator>();
Debug.Assert(_animator != null);
}
public void OnEnable() {
if (_isSaveAvailable && _animator != null)
{
_LoadParams();
_animator.Play(_savedAnimatorStateInfo.shortNameHash);
}
}
public void SaveAnimState() {
if (_animator != null)
{
_savedAnimatorStateInfo = _animator.GetCurrentAnimatorStateInfo(0);
_SaveParams();
_isSaveAvailable = true;
}
}
public void Reset()
{
_isSaveAvailable = false;
}
private void _SaveParams()
{
_savedParameters = _animator.parameters;
for (int i=0; i<_savedParameters.Length; i++)
{
switch(_savedParameters[i].type)
{
case AnimatorControllerParameterType.Bool:
_savedParameterValues[_savedParameters[i].name] = _animator.GetBool(_savedParameters[i].name);
break;
case AnimatorControllerParameterType.Float:
_savedParameterValues[_savedParameters[i].name] = _animator.GetFloat(_savedParameters[i].name);
break;
case AnimatorControllerParameterType.Int:
_savedParameterValues[_savedParameters[i].name] = _animator.GetFloat(_savedParameters[i].name);
break;
case AnimatorControllerParameterType.Trigger:
default:
// we don't care
break;
}
}
}
private void _LoadParams()
{
for (int i=0; i<_savedParameters.Length; i++)
{
switch(_savedParameters[i].type)
{
case AnimatorControllerParameterType.Bool:
_animator.SetBool(_savedParameters[i].name, (bool)_savedParameterValues[_savedParameters[i].name]);
break;
case AnimatorControllerParameterType.Float:
_animator.SetFloat(_savedParameters[i].name, (float)_savedParameterValues[_savedParameters[i].name]);
break;
case AnimatorControllerParameterType.Int:
_animator.SetFloat(_savedParameters[i].name, (int)_savedParameterValues[_savedParameters[i].name]);
break;
case AnimatorControllerParameterType.Trigger:
default:
// we don't care
break;
}
}
}
}
with this:
in the editor, place an AnimatorStateSaver component on every Animator gameobject that you want to save
call SaveAnimStateRecursive(yourGameObject) before deactivating any parent gameobject
animation restore is automatic
call yourAnimationStateSaver.Reset() to discard current state/params backup
Limitations:
as you can see this only handles animation on layer 0,
it does not save progress in the animation itself, just the state it’s in
don’t overuse this, as GetComponentsInChildren and therefore SaveAnimStateRecursive() are slow
Seems like a configurable version of the SetActive should exist as a command-set or have flags or something. Being able to call SetActive(false , bool,myClass,etc.); should be possible imo with a single call to allow you to keep a particular set of data active despite the rest of the object being deactivated.
Just curious if this was ever solved with an update as I hit the problem. I have anims to transition UI pages in and out and want to be able to hide and reveal them without it triggering the default transitioning anim. It really seems that this optimisation of clearing out things should be the exception rather than the rule. Freeing and reallocating stuff like this could stress the GC too.
If you disable only the animator component it should keep all the current state/ parameters and the component won’t get ticked anymore by the engine so it won’t consume any cpu but it will left the memory as is.
Is there any consideration about the configurable/customizable SetActive() call mentioned in the above post by myself?
I feel that this would really help with general enable/disable issues, which Unity definitely has when you consider all the overhead the developer has in managing optimization over loads and loads of various types of specially-programmed gameobjects across many spectrums of functionality.
Could you at least pass this request along to the dev or team responsible for its general implementation, if it were considered?
If you have an idea or you would like to give us feedback please do use the proper channel for it so we can at least track it and see if others agree with you. https://feedback.unity3d.com/
Giving feedback in a two years old thread in the animation forum has a low chance to get noticed.