Search Unity

Animation layer syncing and using override animator controllers not working.

Discussion in 'Animation' started by GloriaVictis, Jun 21, 2017.

  1. GloriaVictis

    GloriaVictis

    Joined:
    Sep 1, 2016
    Posts:
    133
    Hello,

    When I am trying to use layer syncing and than override animator controller the synced layer have all animations nulled.

    I found this topic:
    https://forum.unity3d.com/threads/solved-mechanim-animatoroverridecontroller-layer-access.431382/

    But as we have lots (200+) animations I would need to set by hand it would be both really hard to maintain and also would be really bad performance wise as they switch everytime someone is spawning/changing weapon. As our game is MMO it happens alot.

    I would like to ask if there's a solution to attach those animations automaticly using override animator controller.

    At this moment I am copying layer from one to another but transitions going from Any State cannot be copied.

    Halp!
     
  2. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
  3. GloriaVictis

    GloriaVictis

    Joined:
    Sep 1, 2016
    Posts:
    133
    Thats what I am actually doing but when I get a Synced layer its not being overwritten by AnimatorOverrideController. I believe its just a lack of feature probably.
     
  4. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    I just tried, and yes, the OverrideController doesn't even display the animation in a synced layer.

    You should probably file a bug.

    On the other hand, I believe you don't need a sync layer for what you're doing.
     
  5. GloriaVictis

    GloriaVictis

    Joined:
    Sep 1, 2016
    Posts:
    133
    Filled a bug, thanks.

    We need to do this, we are using the copy of layer to play additive topbody of player while he's in battle stance. Don't want to speak of details atm. but we need to do this to make it look good.
     
  6. amxxvi

    amxxvi

    Joined:
    Dec 23, 2017
    Posts:
    12
    Any solutions found?
     
  7. GloriaVictis

    GloriaVictis

    Joined:
    Sep 1, 2016
    Posts:
    133
    No, sadly not.
     
  8. kyuskoj

    kyuskoj

    Joined:
    Aug 28, 2013
    Posts:
    56
    so disappointed...
     
  9. Whatever560

    Whatever560

    Joined:
    Jan 5, 2016
    Posts:
    515
    I'm also trying to figure how to best switch complexe animations at runtime. Couldn't you make an editor script to help you create empty animations for the sync layer ?

    ATM I've extracted the StateCollector from Cinemachine state driven cameras to be able to retrieve state names and clips at edit time.

    I believe you can try to use that.
    Code (CSharp):
    1. #if UNITY_EDITOR
    2. using AnimatorController = UnityEditor.Animations.AnimatorController;
    3. using BlendTree = UnityEditor.Animations.BlendTree;
    4.  
    5. using UnityEditor;
    6. using UnityEditor.Animations;
    7. using UnityEditorInternal;
    8.  
    9. public class StateEditorCollector
    10. {
    11.    
    12.    
    13.  
    14.     public IAnimatorLayerSelection Target { get; }
    15.     public SerializedObject serializedObject { get; }
    16.  
    17.     public StateEditorCollector(IAnimatorLayerSelection Target, SerializedObject serializedObject)
    18.     {
    19.         this.Target = Target;
    20.         this.serializedObject = serializedObject;
    21.     }
    22.    
    23.     public static AnimatorController GetControllerFromAnimator(Animator animator)
    24.     {
    25.         if (animator == null)
    26.             return null;
    27.         var ovr = animator.runtimeAnimatorController as AnimatorOverrideController;
    28.         if (ovr)
    29.             return ovr.runtimeAnimatorController as AnimatorController;
    30.         return animator.runtimeAnimatorController as AnimatorController;
    31.     }
    32.  
    33.     private string[]             mLayerNames;
    34.     private int[]                mTargetStates;
    35.     private string[]             mTargetStateNames;
    36.     private Dictionary<int, int> mStateIndexLookup;
    37.  
    38.     internal void UpdateTargetStates()
    39.     {
    40.         // Scrape the Animator Controller for states
    41.         AnimatorController ac        = GetControllerFromAnimator(Target.Animator);
    42.         StateCollector     collector = new StateCollector();
    43.         collector.CollectStates(ac, Target.AnimatorLayer);
    44.         mTargetStates     = collector.mStates.ToArray();
    45.         mTargetStateNames = collector.mStateNames.ToArray();
    46.         mStateIndexLookup = collector.mStateIndexLookup;
    47.  
    48.         if (ac == null)
    49.             mLayerNames = new string[0];
    50.         else
    51.         {
    52.             mLayerNames = new string[ac.layers.Length];
    53.             for (int i = 0; i < ac.layers.Length; ++i)
    54.                 mLayerNames[i] = ac.layers[i].name;
    55.         }
    56.  
    57.         // // Create the parent map in the target
    58.         // List<CinemachineStateDrivenCamera.ParentHash> parents
    59.         //     = new List<CinemachineStateDrivenCamera.ParentHash>();
    60.         // foreach (var i in collector.mStateParentLookup)
    61.         //     parents.Add(new CinemachineStateDrivenCamera.ParentHash(i.Key, i.Value));
    62.         // CinemachineTargetGroup.Target.m_ParentHash = parents.ToArray();
    63.     }
    64.  
    65.     private ReorderableList mInstructionList;
    66.  
    67.     internal void SetupInstructionList()
    68.     {
    69.         mInstructionList = new ReorderableList(serializedObject,
    70.                                                serializedObject.FindProperty(nameof(StateDrivenEvent._instructions)),
    71.                                                true, true, true, true);
    72.        
    73.        
    74.             float vSpace = 2;
    75.             float hSpace = 3;
    76.             float floatFieldWidth = EditorGUIUtility.singleLineHeight * 2.5f;
    77.             float hBigSpace = EditorGUIUtility.singleLineHeight * 2 / 3;
    78.             mInstructionList.drawHeaderCallback = (Rect rect) =>
    79.                 {
    80.                     float sharedWidth = rect.width - EditorGUIUtility.singleLineHeight
    81.                         - 2 * (hBigSpace + floatFieldWidth) - hSpace;
    82.                     rect.x += EditorGUIUtility.singleLineHeight; rect.width = sharedWidth / 2;
    83.                     EditorGUI.LabelField(rect, "State");
    84.  
    85.                     rect.x += rect.width + hSpace;
    86.                     EditorGUI.LabelField(rect, "Event");
    87.                     //
    88.                     // rect.x += rect.width + hBigSpace; rect.width = floatFieldWidth;
    89.                     // EditorGUI.LabelField(rect, "Wait");
    90.                     //
    91.                     // rect.x += rect.width + hBigSpace;
    92.                     // EditorGUI.LabelField(rect, "Min");
    93.                 };
    94.  
    95.             mInstructionList.elementHeightCallback = index =>
    96.             {
    97.                 var uEvent = Target.Instructions[index].Event;
    98.                 int eCount = uEvent.GetPersistentEventCount();
    99.                 return EditorGUIUtility.singleLineHeight * (1 + (eCount == 0 ? 5 : (2 + eCount * 3)));
    100.                
    101.             };
    102.  
    103.             mInstructionList.drawElementCallback
    104.                 = (Rect rect, int index, bool isActive, bool isFocused) =>
    105.                 {
    106.                     SerializedProperty instProp
    107.                         = mInstructionList.serializedProperty.GetArrayElementAtIndex(index);
    108.                     float sharedWidth = rect.width /*- 2 * (hBigSpace + floatFieldWidth)*/ - hSpace;
    109.                     rect.y += vSpace; rect.height = EditorGUIUtility.singleLineHeight;
    110.  
    111.                    
    112.                    
    113.  
    114.                     rect.width = sharedWidth / 2;
    115.                     var stateSelProp = instProp.FindPropertyRelative(nameof(EventInstruction.FullHash));
    116.                     var readNameProp = instProp.FindPropertyRelative(nameof(EventInstruction.ReadableName));
    117.                     int currentState = GetStateHashIndex(stateSelProp.intValue);
    118.                     int stateSelection = EditorGUI.Popup(rect, currentState, mTargetStateNames);
    119.                     if (currentState != stateSelection)
    120.                     {
    121.                         stateSelProp.intValue = mTargetStates[stateSelection];
    122.                         readNameProp.stringValue = mTargetStateNames[stateSelection];
    123.                     }
    124.  
    125.                     var enabledProp = instProp.FindPropertyRelative(nameof(EventInstruction.Enabled));
    126.                     EditorGUI.PropertyField(new Rect(rect) {x = rect.x + rect.width}, enabledProp);
    127.                    
    128.                     rect.y += vSpace + EditorGUIUtility.singleLineHeight;
    129.                     rect.width = sharedWidth;
    130.                     var unityEventProperty =
    131.                         instProp.FindPropertyRelative(nameof(EventInstruction.Event));
    132.                     EditorGUI.PropertyField(rect, unityEventProperty);
    133.                     // SerializedProperty vcamSelProp = instProp.FindPropertyRelative(() => def.m_VirtualCamera);
    134.                     // int currentVcam = GetCameraIndex(vcamSelProp.objectReferenceValue);
    135.                     // int vcamSelection = EditorGUI.Popup(rect, currentVcam, mCameraCandidates);
    136.                     // if (currentVcam != vcamSelection)
    137.                     //     vcamSelProp.objectReferenceValue = (vcamSelection == 0)
    138.                     //         ? null : Target.ChildCameras[vcamSelection - 1];
    139.  
    140.                     float oldWidth = EditorGUIUtility.labelWidth;
    141.                     EditorGUIUtility.labelWidth = hBigSpace;
    142.  
    143.                     // rect.x += rect.width; rect.width = floatFieldWidth + hBigSpace;
    144.                     // SerializedProperty activeAfterProp = instProp.FindPropertyRelative(() => def.m_ActivateAfter);
    145.                     // EditorGUI.PropertyField(rect, activeAfterProp, new GUIContent(" ", activeAfterProp.tooltip));
    146.                     //
    147.                     // rect.x += rect.width;
    148.                     // SerializedProperty minDurationProp = instProp.FindPropertyRelative(() => def.m_MinDuration);
    149.                     // EditorGUI.PropertyField(rect, minDurationProp, new GUIContent(" ", minDurationProp.tooltip));
    150.  
    151.                     EditorGUIUtility.labelWidth = oldWidth;
    152.                 };
    153.  
    154.             mInstructionList.onAddDropdownCallback = (Rect buttonRect, UnityEditorInternal.ReorderableList l) =>
    155.                 {
    156.                     var menu = new GenericMenu();
    157.                     menu.AddItem(new GUIContent("New State"),
    158.                         false, (object data) =>
    159.                     {
    160.                         ++mInstructionList.serializedProperty.arraySize;
    161.                         serializedObject.ApplyModifiedProperties();
    162.                         // Target.ValidateInstructions();
    163.                     },
    164.                         null);
    165.                     menu.AddItem(new GUIContent("All Unhandled States"),
    166.                         false, (object data) =>
    167.                     {
    168.                         int len = mInstructionList.serializedProperty.arraySize;
    169.                         for (int i = 0; i < mTargetStates.Length; ++i)
    170.                         {
    171.                             int hash = mTargetStates[i];
    172.                             if (hash == 0)
    173.                                 continue;
    174.                             bool alreadyThere = false;
    175.                             for (int j = 0; j < len; ++j)
    176.                             {
    177.                                 if (Target.Instructions[j].FullHash == hash)
    178.                                 {
    179.                                     alreadyThere = true;
    180.                                     break;
    181.                                 }
    182.                             }
    183.                             if (!alreadyThere)
    184.                             {
    185.                                 int index = mInstructionList.serializedProperty.arraySize;
    186.                                 ++mInstructionList.serializedProperty.arraySize;
    187.                                 SerializedProperty p = mInstructionList.serializedProperty.GetArrayElementAtIndex(index);
    188.                                 p.FindPropertyRelative(nameof(EventInstruction.FullHash)).intValue = hash;
    189.                             }
    190.                         }
    191.                         serializedObject.ApplyModifiedProperties();
    192.                         // Target.ValidateInstructions();
    193.                     },
    194.                         null);
    195.                     menu.ShowAsContext();
    196.                 };
    197.         }
    198.  
    199.     public class StateCollector
    200.     {
    201.         public List<int>            mStates;
    202.         public List<string>         mStateNames;
    203.         public Dictionary<int, int> mStateIndexLookup;
    204.         public Dictionary<int, int> mStateParentLookup;
    205.  
    206.         public void CollectStates(AnimatorController ac, int layerIndex)
    207.         {
    208.             mStates            = new List<int>();
    209.             mStateNames        = new List<string>();
    210.             mStateIndexLookup  = new Dictionary<int, int>();
    211.             mStateParentLookup = new Dictionary<int, int>();
    212.  
    213.             mStateIndexLookup[0] = mStates.Count;
    214.             mStateNames.Add("(default)");
    215.             mStates.Add(0);
    216.  
    217.             if (ac != null && layerIndex >= 0 && layerIndex < ac.layers.Length)
    218.             {
    219.                 AnimatorStateMachine fsm  = ac.layers[layerIndex].stateMachine;
    220.                 string               name = fsm.name;
    221.                 int                  hash = Animator.StringToHash(name);
    222.                 CollectStatesFromFSM(fsm, name + ".", hash, string.Empty);
    223.             }
    224.         }
    225.  
    226.         void AddTransitions(AnimatorStateTransition[] transitions, string statepath, int statehash, string stateDisplayName)
    227.         {
    228.             for (var j = 0; j < transitions.Length; j++)
    229.             {
    230.                 var transition = transitions[j];
    231.                 AddState(Animator.StringToHash(statepath + "." + transition.name), statehash,
    232.                          stateDisplayName + "." + transition.name);
    233.             }
    234.         }
    235.  
    236.         void CollectStatesFromFSM(
    237.             AnimatorStateMachine fsm, string hashPrefix, int parentHash, string displayPrefix)
    238.         {
    239.          
    240.             ChildAnimatorState[] states = fsm.states;
    241.             for (int i = 0; i < states.Length; i++)
    242.             {
    243.                 AnimatorState state = states[i].state;
    244.                 var statepath = hashPrefix + state.name;
    245.                 var stateDisplayName = displayPrefix + state.name;
    246.                 var statehash = Animator.StringToHash(statepath);
    247.                 int hash = AddState(statehash,
    248.                                     parentHash, stateDisplayName);
    249.  
    250.                 //Collect Transitions
    251.                 AddTransitions(state.transitions, statepath, statehash, stateDisplayName);
    252.                
    253.  
    254.                 // Also process clips as pseudo-states, if more than 1 is present.
    255.                 // Since they don't have hashes, we can manufacture some.
    256.                 var clips = CollectClips(state.motion);
    257.                 if (clips.Count > 1)
    258.                 {
    259.                     string substatePrefix = stateDisplayName + ".";
    260.                     foreach (AnimationClip c in clips)
    261.                         AddState(
    262.                             CinemachineStateDrivenCamera.CreateFakeHash(hash, c),
    263.                             hash, substatePrefix + c.name);
    264.                 }
    265.             }
    266.  
    267.             ChildAnimatorStateMachine[] fsmChildren = fsm.stateMachines;
    268.             foreach (var child in fsmChildren)
    269.             {
    270.                 string name        = hashPrefix + child.stateMachine.name;
    271.                 string displayName = displayPrefix + child.stateMachine.name;
    272.                 int    hash        = AddState(Animator.StringToHash(name), parentHash, displayName);
    273.                 CollectStatesFromFSM(child.stateMachine, name + ".", hash, displayName + ".");
    274.             }
    275.         }
    276.  
    277.         List<AnimationClip> CollectClips(Motion motion)
    278.         {
    279.             var           clips = new List<AnimationClip>();
    280.             AnimationClip clip  = motion as AnimationClip;
    281.             if (clip != null)
    282.                 clips.Add(clip);
    283.             BlendTree tree = motion as BlendTree;
    284.             if (tree != null)
    285.             {
    286.                 ChildMotion[] children = tree.children;
    287.                 foreach (var child in children)
    288.                     clips.AddRange(CollectClips(child.motion));
    289.             }
    290.  
    291.             return clips;
    292.         }
    293.  
    294.         int AddState(int hash, int parentHash, string displayName)
    295.         {
    296.             if (parentHash != 0)
    297.                 mStateParentLookup[hash] = parentHash;
    298.             mStateIndexLookup[hash] = mStates.Count;
    299.             mStateNames.Add(displayName);
    300.             mStates.Add(hash);
    301.             return hash;
    302.         }
    303.     }
    304.  
    305.     private int GetStateHashIndex(int stateHash)
    306.     {
    307.         if (stateHash == 0)
    308.             return 0;
    309.         if (!mStateIndexLookup.ContainsKey(stateHash))
    310.             return 0;
    311.         return mStateIndexLookup[stateHash];
    312.     }
    313.  
    314.  
    315.     public void DoEditorLayout()
    316.     {
    317.         EditorGUILayout.PropertyField(serializedObject.FindProperty(nameof(StateDrivenEvent._animator)));
    318.         SerializedProperty layerProp      = serializedObject.FindProperty(nameof(StateDrivenEvent._animatorLayer));
    319.         int                currentLayer   = layerProp.intValue;
    320.         int                layerSelection = EditorGUILayout.Popup("Layer", currentLayer, mLayerNames);
    321.         mInstructionList.DoLayoutList();
    322.         if (currentLayer != layerSelection)
    323.             layerProp.intValue = layerSelection;
    324.     }
    325. }
    326.  
    327.  
    328. [CustomEditor(typeof(StateDrivenEvent), editorForChildClasses: true)]
    329. public class StateDrivenEventEditor : Editor, IHasOnEnable
    330. {
    331.     private StateEditorCollector _stateEditorCollector;
    332.  
    333.     public StateDrivenEvent Target { get; set; }
    334.  
    335.     public void OnEnable()
    336.     {
    337.         Target = (StateDrivenEvent) target;
    338.         _stateEditorCollector = new StateEditorCollector(Target, serializedObject);
    339.         _stateEditorCollector.SetupInstructionList();
    340.     }
    341.  
    342.     public override void OnInspectorGUI()
    343.     {
    344.        
    345.  
    346.         // Layer index
    347.         EditorGUI.BeginChangeCheck();
    348.         _stateEditorCollector.UpdateTargetStates();
    349.         _stateEditorCollector.DoEditorLayout();
    350.         if (EditorGUI.EndChangeCheck())
    351.         {
    352.             serializedObject.ApplyModifiedProperties();
    353.         }
    354.     }
    355.  
    356.  
    357. }
    358. #endif
    359.  
    360. public interface IAnimatorLayerSelection
    361. {
    362.     Animator               Animator      { get; }
    363.     int                    AnimatorLayer { get; }
    364.     List<EventInstruction> Instructions  { get; }
    365.  
    366. }
    367.  
    368. [Serializable]
    369. public class EventInstruction
    370. {
    371.     /// <summary>
    372.     /// Are the instruction enabled
    373.     /// </summary>
    374.     public bool Enabled = true;
    375.  
    376.     public int             FullHash;
    377.     public EventState Event = new EventState();
    378.     /// <summary>
    379.     /// Readable name of the state event is attached to
    380.     /// </summary>
    381.     public string ReadableName;
    382.  
    383.    
    384. }
    385.  
    386. /// <summary>
    387. /// A state driven
    388. /// </summary>
    389. public class StateDrivenEvent : SimpleMonoBehaviour, IHasUpdate, IAnimatorLayerSelection
    390. {
    391.     [SerializeField]
    392.     internal Animator _animator; public Animator Animator => _animator;
    393.     [SerializeField] internal int _animatorLayer; public int AnimatorLayer => _animatorLayer;
    394.  
    395.     [SerializeField]
    396.     internal List<EventInstruction> _instructions = new List<EventInstruction>();
    397.     public List<EventInstruction> Instructions => _instructions;
    398.  
    399.  
    400.  
    401.     private List<(int layer, int currentState)> _states = new List<(int layer, int currentState)>();
    402.  
    403.     public void Update()
    404.     {
    405.         var layers = _animator.layerCount;
    406.         for (var layer = 0; layer < layers; layer++)
    407.         {
    408.             var currentState = _animator.GetCurrentAnimatorStateInfo(layer).fullPathHash;
    409.             var nextState = _animator.GetNextAnimatorStateInfo(layer); // do we use this ?
    410.             var optT = _states.SingleOptional(s => s.layer == layer);
    411.             optT
    412.                .IfPresent(t =>
    413.                 {
    414.                     if (t.currentState == currentState) return;//do not trigger is state is already triggered for given layer
    415.                     SetState(layer, currentState);
    416.                     TriggerEvent(currentState);
    417.                 })
    418.                .IfNotPresent(() =>
    419.                 {
    420.                     _states.Add((layer, currentState));
    421.                     TriggerEvent(currentState);
    422.                 });
    423.         }
    424.        
    425.     }
    426.  
    427.     /// <summary>
    428.     /// Set the new state for a layer
    429.     /// </summary>
    430.     /// <param name="layer"></param>
    431.     /// <param name="currentState"></param>
    432.     private void SetState(int layer, int currentState)
    433.     {
    434.         //Tuples are returned by value so this is needed.
    435.         _states.RemoveAll(t => t.layer == layer);
    436.         _states.Add((layer, currentState));
    437.     }
    438.  
    439.     private void TriggerEvent(int stateHash)
    440.     {
    441.        
    442.         foreach (var eventInstruction in Instructions.Where(i => i.FullHash == stateHash))
    443.         {
    444.             IndusLogger.Trace(nameof(StateDrivenEvent), $"trigger {eventInstruction.ReadableName}");
    445.             //Todo add readable name to callback ?
    446.             eventInstruction.Event.Invoke(stateHash);
    447.         }
    448.     }
    449. }
     
  10. thuong_henry

    thuong_henry

    Joined:
    Mar 4, 2020
    Posts:
    6
    protected Animator Animsync;
    protected Animator Animation;

    private void OnEnable()
    {
    Animsync = GameObject.Find("Animsync").GetComponent<Animator>();
    Animation = GetComponent<Animator>();
    Animation.Play(0, -1, Animsync.GetCurrentAnimatorStateInfo(0).normalizedTime);
    }