Reset Animator (usage with Pooled Object)

Hello,

On my project I have pooled objects

  • instead of destroying objects, objects are deactivated using SetActive(false)
  • when I need a new object I look for an inactive object and I active it using SetActive(true)e

Everything seems ok but when my pooled gameObject as animator attached to it sometimes things goes wrong. Animation and state are not “coherent”. I suppose I need to correctly reset/stop the Animator when I deactivate or reactivate my object but I did not now how to do it.

If found other people (on this forum, stackoverflow, …) having similar problems but no one seems to have to a solution.

Is there a solution to save the animator in Awake and restore it latter ?

2 Likes

When you disable and enable an Animator component, it resets the animator controller state. How about setting the parameters to the values you want in OnEnable()?

[EDIT: No longer the case in Unity 5.x. In Unity 5.6 (maybe earlier), deactivating & reactivating the GameObject doesn’t reset it either, nor does Rebind().]

2 Likes

The parameter value is set just after SetActive(true).
But I will check it when I am back to home.
Thanks.

I am also thinking about trying to “copy” value of Animator component on Awake and restore it. Like explained here
http://www.jakel168.com/2013/08/unity3dc-copy-all-values-from-existing.html

There is no solution right now to restore the state of an animator. Every time an animator is disabled, all resources owned by this animator are free.

We are looking at what could be done so we would like to get more information on your user case.

In your particular user case it look like you are using the first free animator from the pool but you probably don’t want to restore the state of this animator but rather the state of another animator.

We were looking at adding some new API function that would allow you to push and pop an Animator’s memory state. Just before calling deactivate you would push the state and right after reactivating the object you would pop the state but I think that in your case that wouldn’t work right?

In your case I think you would need to own and manage the animator memory state, so it would be better if we create a scripting object that hold the animator state that you could save on disk and then later push it on your next free animator from the pool right?

something like this

       Animator animator = GetComponent<Animator>();

        AnimatorSnapshot snapShot = animator.snapShot;
        animator.SetActive (false);
        .....
        animator.SetActive (true);
        animator.snapShot = snapShot;

Using reflection won’t help you here because the animator internal memory state is not exposed at all, neither by script or through Unity transfer function.

I’ll give you some details :

  • I have got a class called PooledObject which looks like this
public abstract class PooledObject : MonoBehaviour {
   public int pooledAmount = 20;
   private Transform _poolRoot;
   private PooledObject[] _pooledObjects;
   private bool _terminate = false;
   public abstract void CloneFromOriginal(PooledObject original);
   public GameObject GetObject(PooledObject original)
   {
     for (int i=0 ; i<_pooledObjects.Length ; i++) {
       if (_pooledObjects.gameObject.activeInHierarchy==false) {
         _pooledObjects.gameObject.transform.parent = null;
         _pooledObjects.CloneFromOriginal(original);
         _pooledObjects.gameObject.SetActive(true);
         return _pooledObjects.gameObject;
       }
     }
     Debug.LogError("Not enough pooled Objects for " + gameObject.name + " : " + _pooledObjects.Length);
     return null;
   }
   public void Release()
   {
     gameObject.transform.parent = _poolRoot;
     gameObject.SetActive(false);
   }
   ...
   more code here
   ...
   
}

- I have a class called EnnemyColorSwitch which inherit from PooledObject
- The CloneFromOriginal method of this class is empty … but if I have to do something about Animator I will do it here.
- I have got a prefab which a EnneColorSwitch Component and an Animator component.
- To create “ennemy” I do it like this

```csharp
*PooledObject ennemiePooled = prefab.GetComponent();
GameObject p = ennemiePooled.GetObject(ennemiePooled);

p.transform.parent = transform;
p.transform.localPosition = gd.position;

EnnemyColorSwitch ennemy = p.GetComponent();
if (ennemy!=null) {
ennemy.CheckChapoColor();
}

_*_</em></em></em> <em><em><em>_*with*_</em></em></em> <em><em><em>_*csharp*
* public void CheckChapoColor()
{
anim.SetBool(“sameColor”,ComputeBooleanValue());
}*

```

- Here is two screenshots “showing” the problem. Both “monsters” have the parameter “sameColor” to true and the animation “monster_A_Blue_Off” running which is normal in this configuration (the Player is also blue). But the first monster looks different than the second one. This my problem … the first monster looks like if the animation “monster_A_Blue_On” is running.
- Do you think I can use a transition from “Any State” to help me solve my problem ?
Any help is welcome.
Regards


And to answer your question : yes “AnimatorSnapshot” can be a great help.

I try to use a transition from “Any State” but it does not help.

Hello @Mecanim-Dev do you have any work around to solve my problem. Today I discuss with a coworker and he proposed me some change to do in my mecanim graph … I will try it tonigh at home. But if you have any recommendations it can be helpful. Thanks again.

It a little bit hard to find the problem without the project. Both are using the same controller and both monster are instantiated from the same prefab so it should be the same unless you have some script triggering something.

If you think there is a bug please log it with your project so we can repro.

ok
I’ll try to create a minimal project to reproduce the problem as it not easy to reproduce and understand it with the full project because you have to play sometimes before it occurs.

Hello @Mecanim-Dev .

I have created a minimal project and report a bug though the unity editor (Case 663363).
While working of the minimal project I found a workaround but I am not sure it will be easy to integrate to my full project.
The idea was to “reset” the animator to it is default state before “releasing” the pooled object. In my case it means set “sameColor” parameter to true. But I cannot called SetBool and Release in the same so I have to wait one frame before releasing my pooled object. I explained it in my bug report and in the project attached to it.

Thanks again.

That worked perfectly for me too in the Unity editor. But once I tried to run this on an iOS device it never seemed to “reset” it. I starts up a loading screen at the same time tho… but I also tried changing the Animator to “Always Update” so it shouldn’t cull the transform.

Tried it on an iOS device too by any chance?
Or any updates from Unity on your Case yet.

Hi Nicolas,

So I had some time today to look at your issue and effectively it look wrong.

One way to fix this for you very easily is to make sure that every clip have at least one curve to animate your sprite renderer. You first clip monster_A_blue_Off doesn’t write any values in SpriteRender component to set correctly the sprite to render. So if you add those curve on you monster_A_Blue_off clip you will always have the same behaviour when you activate any of your pooled object.

For long term we are thinking that we should add a new API function Animator.WriteDefaultValues() that would ease the workflow for anybody using pooled object that want a deterministic behaviour when polling a new object.

We can’t enforce this when the animator is deactivated because there is many other use case where some user want their object to keep the last evaluated animation frame.

ho … I missed your answer … I will look at it …
and thanks again for your time

I wrote a script that will repair an animators child objects to their original state OnDisable. It can be adopted easily to repiar any kind of property you need to restore. Hopefully this saves some time for someone.

using UnityEngine;
using System.Collections.Generic;

namespace Anim {
    // OnDisable this will repair all first level transforms to their original state
    // Fixes an issue where the animator loses all notion of enabled/disabled GameObject states
    public class AnimatorRepair : MonoBehaviour {
        [Tooltip("Defaults to self")]
        [SerializeField] Transform target;

        Dictionary<Transform, bool> transRecord = new Dictionary<Transform, bool>();

        void Awake () {
            if (target == null) target = transform;

            // Cache and record the default state of the character           
            foreach (Transform child in target) {
                transRecord[child] = child.gameObject.activeInHierarchy;
            }
        }

        void OnDisable () {
            foreach (Transform child in target) {
                if (transRecord.ContainsKey(child)) {
                    child.gameObject.SetActive(transRecord[child]);
                }
            }
        }
    }
}

I actually tried Rebind in Unity 2017.3 and it works perfectly! Thank You so much.

Any solution to this yet?
Its driving me crazy! The Unity team should stop sleeping

I found the best workaround for this. whenever my enemy gets deactivated i put him in reset state where I put every object that’s on him to the prefabs’ rotation position scale etc.
And that works perfectly.

This is such an annoying issue. Especially since you can’t keyframe multiple transforms at each by simply selecting them and pressing keyframe. This causes transforms to assume their last state if they aren’t updated in the next animation. So while the animation may look fine while you record because you’re building off the default pose, it may not play correctly because some bones will be that of the last animation rather than the prefabs pose.

This NEEDS to be an option; so many issue threads about it.

Worth noting I tried to reset all of the transforms positions, scales, rotations, and enabled state OnEnable and they’re still behaving very oddly. It’s almost like bones which aren’t modified in animations are stuck on the last animation played before disabled (I’m pooling them).

1 Like