Search Unity

  1. Get all the Unite Berlin 2018 news on the blog.
    Dismiss Notice
  2. Unity 2018.2 has arrived! Read about it here.
    Dismiss Notice
  3. Improve your Unity skills with a certified instructor in a private, interactive classroom. Learn more.
    Dismiss Notice
  4. ARCore is out of developer preview! Read about it here.
    Dismiss Notice
  5. Magic Leap’s Lumin SDK Technical Preview for Unity lets you get started creating content for Magic Leap One™. Find more information on our blog!
    Dismiss Notice
  6. Want to see the most recent patch releases? Take a peek at the patch release page.
    Dismiss Notice

Reset Animator (usage with Pooled Object)

Discussion in 'Animation' started by NicolasHognon, Jan 13, 2015.

  1. NicolasHognon

    NicolasHognon

    Joined:
    Nov 15, 2010
    Posts:
    31
    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. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    8,032
    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().]
     
    Last edited: Apr 28, 2017
    kantagara likes this.
  3. NicolasHognon

    NicolasHognon

    Joined:
    Nov 15, 2010
    Posts:
    31
  4. Mecanim-Dev

    Mecanim-Dev

    Unity Technologies

    Joined:
    Nov 26, 2012
    Posts:
    1,553
    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
    Code (CSharp):
    1.  
    2.        Animator animator = GetComponent<Animator>();
    3.  
    4.         AnimatorSnapshot snapShot = animator.snapShot;
    5.         animator.SetActive (false);
    6.         .....
    7.         animator.SetActive (true);
    8.         animator.snapShot = snapShot;
    9.  
     
  5. Mecanim-Dev

    Mecanim-Dev

    Unity Technologies

    Joined:
    Nov 26, 2012
    Posts:
    1,553
    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.
     
  6. NicolasHognon

    NicolasHognon

    Joined:
    Nov 15, 2010
    Posts:
    31
    I'll give you some details :

    - I have got a class called PooledObject which looks like this

    Code (CSharp):
    1. public abstract class PooledObject : MonoBehaviour {
    2.    public int pooledAmount = 20;
    3.    private Transform _poolRoot;
    4.    private PooledObject[] _pooledObjects;
    5.    private bool _terminate = false;
    6.    public abstract void CloneFromOriginal(PooledObject original);
    7.    public GameObject GetObject(PooledObject original)
    8.    {
    9.      for (int i=0 ; i<_pooledObjects.Length ; i++) {
    10.        if (_pooledObjects.gameObject.activeInHierarchy==false) {
    11.          _pooledObjects.gameObject.transform.parent = null;
    12.          _pooledObjects.CloneFromOriginal(original);
    13.          _pooledObjects.gameObject.SetActive(true);
    14.          return _pooledObjects.gameObject;
    15.        }
    16.      }
    17.      Debug.LogError("Not enough pooled Objects for " + gameObject.name + " : " + _pooledObjects.Length);
    18.      return null;
    19.    }
    20.    public void Release()
    21.    {
    22.      gameObject.transform.parent = _poolRoot;
    23.      gameObject.SetActive(false);
    24.    }
    25.    ...
    26.    more code here
    27.    ...
    28.    
    29. }

    - 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



    Code (CSharp):
    1. [I][I][I][I][I]PooledObject ennemiePooled = prefab.GetComponent<PooledObject>();[/I][/I][/I][/I][/I]
    2.    GameObject p = ennemiePooled.GetObject(ennemiePooled);
    3.  
    4.    p.transform.parent = transform;
    5.    p.transform.localPosition = gd.position;
    6.      
    7.    EnnemyColorSwitch ennemy = p.GetComponent<EnnemyColorSwitch>();
    8.    if (ennemy!=null) {
    9.      ennemy.CheckChapoColor();
    10.    }
    11.  
    with

    Code (CSharp):
    1.  
    2.    public void CheckChapoColor()
    3.    {
    4.      _anim.SetBool("sameColor",ComputeBooleanValue());
    5.    }
    6.  
    - 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
     

    Attached Files:

  7. NicolasHognon

    NicolasHognon

    Joined:
    Nov 15, 2010
    Posts:
    31
    And to answer your question : yes "AnimatorSnapshot" can be a great help.
     
  8. NicolasHognon

    NicolasHognon

    Joined:
    Nov 15, 2010
    Posts:
    31
    I try to use a transition from "Any State" but it does not help.
     
  9. NicolasHognon

    NicolasHognon

    Joined:
    Nov 15, 2010
    Posts:
    31
    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.
     
  10. Mecanim-Dev

    Mecanim-Dev

    Unity Technologies

    Joined:
    Nov 26, 2012
    Posts:
    1,553
    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.
     
  11. NicolasHognon

    NicolasHognon

    Joined:
    Nov 15, 2010
    Posts:
    31
    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.
     
  12. NicolasHognon

    NicolasHognon

    Joined:
    Nov 15, 2010
    Posts:
    31
    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.
     
  13. Anx

    Anx

    Joined:
    Jan 2, 2014
    Posts:
    6
    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.
     
    Last edited: Jan 27, 2015
  14. Mecanim-Dev

    Mecanim-Dev

    Unity Technologies

    Joined:
    Nov 26, 2012
    Posts:
    1,553
    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.

    monster.png
     
  15. NicolasHognon

    NicolasHognon

    Joined:
    Nov 15, 2010
    Posts:
    31
    ho .... I missed your answer .... I will look at it ....
    and thanks again for your time
     
  16. Ash-Blue

    Ash-Blue

    Joined:
    Aug 18, 2013
    Posts:
    88
    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.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections.Generic;
    3.  
    4. namespace Anim {
    5.     // OnDisable this will repair all first level transforms to their original state
    6.     // Fixes an issue where the animator loses all notion of enabled/disabled GameObject states
    7.     public class AnimatorRepair : MonoBehaviour {
    8.         [Tooltip("Defaults to self")]
    9.         [SerializeField] Transform target;
    10.  
    11.         Dictionary<Transform, bool> transRecord = new Dictionary<Transform, bool>();
    12.  
    13.         void Awake () {
    14.             if (target == null) target = transform;
    15.  
    16.             // Cache and record the default state of the character          
    17.             foreach (Transform child in target) {
    18.                 transRecord[child] = child.gameObject.activeInHierarchy;
    19.             }
    20.         }
    21.  
    22.         void OnDisable () {
    23.             foreach (Transform child in target) {
    24.                 if (transRecord.ContainsKey(child)) {
    25.                     child.gameObject.SetActive(transRecord[child]);
    26.                 }
    27.             }
    28.         }
    29.     }
    30. }
     
  17. kantagara

    kantagara

    Joined:
    Sep 15, 2014
    Posts:
    4
    I actually tried Rebind in Unity 2017.3 and it works perfectly! Thank You so much.
     
  18. PZ4_Bailey

    PZ4_Bailey

    Joined:
    Oct 15, 2017
    Posts:
    6
    Any solution to this yet?
    Its driving me crazy! The Unity team should stop sleeping
     
  19. kantagara

    kantagara

    Joined:
    Sep 15, 2014
    Posts:
    4
    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.
     
  20. Distul

    Distul

    Joined:
    Dec 7, 2014
    Posts:
    73
    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).
     
    Last edited: Jul 9, 2018