Exemple #1
0
        /// <summary>
        /// Draws the interface for an AnimatorItemSetData.
        /// </summary>
        private static void DrawAnimatorSet(MonoBehaviour target, Dictionary <string, ReorderableList> reordableListMap, List <ReorderableList> reordableLists,
                                            ReorderableList.SelectCallbackDelegate selectCallback, ReorderableList.AddCallbackDelegate addCallback,
                                            ReorderableList.RemoveCallbackDelegate removeCallback, SerializedProperty itemStateOrderDataProperty, bool abilitySet)
        {
            var itemGroupDataProperty = itemStateOrderDataProperty.FindPropertyRelative("m_Groups");
            var groupCount            = itemGroupDataProperty.arraySize;
            // Add an extra element for the "Add New Group" element. If there are no states then add an extra element to be able to display "No States".
            var groupIndexes = new string[groupCount + 1 + (groupCount == 0 ? 1 : 0)];

            if (groupCount > 0)
            {
                for (int i = 0; i < groupCount; ++i)
                {
                    groupIndexes[i] = i.ToString();
                }
            }
            else
            {
                groupIndexes[0] = "(No Groups)";
            }
            groupIndexes[groupIndexes.Length - 1] = "Add New Group...";
            EditorGUI.BeginChangeCheck();
            EditorGUILayout.BeginHorizontal();
            var indexPath = EditorPrefsKey + target + "." + itemGroupDataProperty.propertyPath;
            var index     = EditorGUILayout.Popup("Group Index", EditorPrefs.GetInt(indexPath, 0), groupIndexes);

            GUI.enabled = itemGroupDataProperty.arraySize > 1;
            if (GUILayout.Button("X", GUILayout.Width(20)))
            {
                itemGroupDataProperty.DeleteArrayElementAtIndex(index);
                itemGroupDataProperty.serializedObject.ApplyModifiedProperties();
                index = Mathf.Max(index - 1, 0);
            }
            GUI.enabled = true;
            EditorGUILayout.EndHorizontal();
            if (EditorGUI.EndChangeCheck())
            {
                // "Add New" was clicked.
                if (index == groupIndexes.Length - 1)
                {
                    itemGroupDataProperty.InsertArrayElementAtIndex(itemGroupDataProperty.arraySize);
                    index = itemGroupDataProperty.arraySize - 1;
                }
                itemGroupDataProperty.serializedObject.ApplyModifiedProperties();
                EditorPrefs.SetInt(indexPath, index);
            }
            if (itemGroupDataProperty.arraySize > 0)
            {
                if (itemGroupDataProperty.arraySize > 1)
                {
                    EditorGUILayout.PropertyField(itemStateOrderDataProperty.FindPropertyRelative("m_GroupOrder"), false);
                }
                GUILayout.Space(7);
                index = Mathf.Clamp(index, 0, itemGroupDataProperty.arraySize - 1);
                DrawGroup(target, reordableListMap, reordableLists, selectCallback, addCallback, removeCallback, itemGroupDataProperty.GetArrayElementAtIndex(index), abilitySet);
            }
        }
Exemple #2
0
        /// <summary>
        /// Draws the item group. Will show a ReordableList along with state details if an element is selected.
        /// </summary>
        private static void DrawGroup(MonoBehaviour target, Dictionary <string, ReorderableList> reordableListMap, List <ReorderableList> reordableLists,
                                      ReorderableList.SelectCallbackDelegate selectCallback, ReorderableList.AddCallbackDelegate addCallback,
                                      ReorderableList.RemoveCallbackDelegate removeCallback, SerializedProperty itemStateDataProperty, bool abilitySet)
        {
            EditorGUI.BeginChangeCheck();
            var stateOrderProperty = itemStateDataProperty.FindPropertyRelative("m_StateOrder");

            EditorGUILayout.PropertyField(stateOrderProperty);
            if (stateOrderProperty.enumValueIndex == (int)AnimatorItemGroupData.Order.Combo)
            {
                EditorGUILayout.PropertyField(itemStateDataProperty.FindPropertyRelative("m_ComboTimeout"));
            }
            var list = GetReorderableList(reordableListMap, reordableLists, selectCallback, addCallback, removeCallback, itemStateDataProperty.FindPropertyRelative("m_States"));

            // Indent the list so it lines up with the rest of the content.
            var rect = GUILayoutUtility.GetRect(0, list.GetHeight());

            rect.x    += EditorGUI.indentLevel * 15;
            rect.xMax -= EditorGUI.indentLevel * 15;
            list.DoList(rect);

            // Show the selected state details.
            if (list.index != -1 && list.count > 0)
            {
                var elementProperty = itemStateDataProperty.FindPropertyRelative("m_States").GetArrayElementAtIndex(list.index);
                EditorGUILayout.PropertyField(elementProperty.FindPropertyRelative("m_Name"), false);
                if (abilitySet)
                {
                    EditorGUILayout.HelpBox("Leave the name empty to use the state name determined by the ability.", MessageType.Info);
                }
                EditorGUILayout.PropertyField(elementProperty.FindPropertyRelative("m_TransitionDuration"), false);
                EditorGUILayout.PropertyField(elementProperty.FindPropertyRelative("m_SpeedMultiplier"), false);
                EditorGUILayout.PropertyField(elementProperty.FindPropertyRelative("m_CanReplay"), false);
                EditorGUILayout.PropertyField(elementProperty.FindPropertyRelative("m_ItemNamePrefix"), false);
                var layerProperty = elementProperty.FindPropertyRelative("m_Layer");
                layerProperty.intValue = EditorGUILayout.MaskField(new GUIContent("Layer", layerProperty.tooltip), layerProperty.intValue, layerProperty.enumDisplayNames);
                EditorGUILayout.PropertyField(elementProperty.FindPropertyRelative("m_IgnoreLowerPriority"), false);
                var controller = target.GetComponentInParent <RigidbodyCharacterController>();
                if (controller != null && controller.UseRootMotion == false)
                {
                    EditorGUILayout.PropertyField(elementProperty.FindPropertyRelative("m_ForceRootMotion"), false);
                }
                else
                {
                    elementProperty.FindPropertyRelative("m_ForceRootMotion").boolValue = false;
                }
            }
            if (EditorGUI.EndChangeCheck())
            {
                itemStateDataProperty.serializedObject.ApplyModifiedProperties();
            }
        }
Exemple #3
0
        /// <summary>
        /// Retrieve a ReorderableList for the specified property.
        /// </summary>
        private static ReorderableList GetReorderableList(Dictionary <string, ReorderableList> reordableListMap, List <ReorderableList> reordableLists,
                                                          ReorderableList.SelectCallbackDelegate selectCallback, ReorderableList.AddCallbackDelegate addCallback,
                                                          ReorderableList.RemoveCallbackDelegate removeCallback, SerializedProperty property)
        {
            ReorderableList list;

            if (reordableListMap.TryGetValue(property.propertyPath, out list))
            {
                return(list);
            }
            list = new ReorderableList(property.serializedObject, property, true, false, true, true);
            list.onSelectCallback += selectCallback;
            list.onAddCallback    += addCallback;
            list.onRemoveCallback += removeCallback;
            reordableListMap.Add(property.propertyPath, list);
            reordableLists.Add(list);
            return(list);
        }
Exemple #4
0
        /// <summary>
        /// Draws the itnerface for an AnimatorItemCollectionData.
        /// </summary>
        public static void DrawAnimatorStateSet(MonoBehaviour target, Dictionary <string, ReorderableList> reordableListMap, List <ReorderableList> reordableLists,
                                                ReorderableList.SelectCallbackDelegate selectCallback, ReorderableList.AddCallbackDelegate addCallback,
                                                ReorderableList.RemoveCallbackDelegate removeCallback, SerializedProperty itemGroupDataProperty)
        {
            if (EditorGUILayout.PropertyField(itemGroupDataProperty, false))
            {
                EditorGUI.indentLevel++;
                var itemStateOrderDataProperty = itemGroupDataProperty.FindPropertyRelative("m_Idle");
                var indexPath = EditorPrefsKey + target + "." + itemStateOrderDataProperty.propertyPath;
                var foldout   = false;
                if ((foldout = EditorGUILayout.Foldout(EditorPrefs.GetBool(indexPath, true), "Idle")))
                {
                    EditorGUI.indentLevel++;
                    DrawAnimatorSet(target, reordableListMap, reordableLists, selectCallback, addCallback, removeCallback, itemStateOrderDataProperty, false);
                    EditorGUI.indentLevel--;
                }
                EditorPrefs.SetBool(indexPath, foldout);

                itemStateOrderDataProperty = itemGroupDataProperty.FindPropertyRelative("m_Movement");
                indexPath = EditorPrefsKey + target + "." + itemStateOrderDataProperty.propertyPath;
                if ((foldout = EditorGUILayout.Foldout(EditorPrefs.GetBool(indexPath, true), "Movement")))
                {
                    EditorGUI.indentLevel++;
                    DrawAnimatorSet(target, reordableListMap, reordableLists, selectCallback, addCallback, removeCallback, itemStateOrderDataProperty, false);
                    EditorGUI.indentLevel--;
                }
                EditorPrefs.SetBool(indexPath, foldout);

                itemStateOrderDataProperty = itemGroupDataProperty.FindPropertyRelative("m_Abilities");
                indexPath = EditorPrefsKey + target + "." + itemStateOrderDataProperty.propertyPath;
                if ((foldout = EditorGUILayout.Foldout(EditorPrefs.GetBool(indexPath, true), "Abilities")))
                {
                    EditorGUI.indentLevel++;
                    DrawAnimatorAbilitySet(target, reordableListMap, reordableLists, selectCallback, addCallback, removeCallback, itemStateOrderDataProperty);
                    EditorGUI.indentLevel--;
                }
                EditorPrefs.SetBool(indexPath, foldout);
                EditorGUI.indentLevel--;
            }
        }
Exemple #5
0
        public static CachedReorderableList GetListDrawer(SerializedProperty property, ReorderableList.HeaderCallbackDelegate drawHeaderCallback, ReorderableList.ElementHeightCallbackDelegate getElementHeightCallback, ReorderableList.ElementCallbackDelegate drawElementCallback,
                                                          ReorderableList.FooterCallbackDelegate drawFooterCallback         = null,
                                                          ReorderableList.AddCallbackDelegate onAddCallback                 = null, ReorderableList.RemoveCallbackDelegate onRemoveCallback   = null, ReorderableList.SelectCallbackDelegate onSelectCallback = null,
                                                          ReorderableList.ChangedCallbackDelegate onChangedCallback         = null, ReorderableList.ReorderCallbackDelegate onReorderCallback = null, ReorderableList.CanRemoveCallbackDelegate onCanRemoveCallback = null,
                                                          ReorderableList.AddDropdownCallbackDelegate onAddDropdownCallback = null)
        {
            if (property == null)
            {
                throw new System.ArgumentNullException("property");
            }
            if (!property.isArray)
            {
                throw new System.ArgumentException("SerializedProperty must be a property for an Array or List", "property");
            }

            int hash = GetPropertyHash(property);
            CachedReorderableList lst;

            if (_lstCache.TryGetValue(hash, out lst))
            {
                lst.ReInit(property.serializedObject, property);
            }
            else
            {
                lst             = new CachedReorderableList(property.serializedObject, property);
                _lstCache[hash] = lst;
            }
            lst.drawHeaderCallback    = drawHeaderCallback;
            lst.elementHeightCallback = getElementHeightCallback;
            lst.drawElementCallback   = drawElementCallback;
            lst.drawFooterCallback    = drawFooterCallback;
            lst.onAddCallback         = onAddCallback;
            lst.onRemoveCallback      = onRemoveCallback;
            lst.onSelectCallback      = onSelectCallback;
            lst.onChangedCallback     = onChangedCallback;
            lst.onReorderCallback     = onReorderCallback;
            lst.onCanRemoveCallback   = onCanRemoveCallback;
            lst.onAddDropdownCallback = onAddDropdownCallback;

            return(lst);
        }
Exemple #6
0
        /// <summary>
        /// Draws the AnimatorAudioStateSet.
        /// </summary>
        public static void DrawAnimatorAudioStateSet(UnityEngine.Object target, AnimatorAudioStateSet animatorAudioStateSet, string animatorAudioStateSetFieldName, bool randomDefaultSelector,
                                                     ref ReorderableList reorderableList, ReorderableList.ElementCallbackDelegate drawCallback, ReorderableList.SelectCallbackDelegate selectCallback,
                                                     ReorderableList.AddCallbackDelegate addCallback, ReorderableList.RemoveCallbackDelegate removeCallback, string preferencesKey,
                                                     ref ReorderableList reorderableAudioList, ReorderableList.ElementCallbackDelegate drawAudioElementCallback,
                                                     ReorderableList.AddCallbackDelegate addAudioCallback, ReorderableList.RemoveCallbackDelegate removeAudioCallback,
                                                     ref ReorderableList reorderableStateList, ReorderableList.ElementCallbackDelegate stateDrawElementCallback,
                                                     ReorderableList.AddCallbackDelegate stateAddCallback, ReorderableList.ReorderCallbackDelegate stateReorderCallback,
                                                     ReorderableList.RemoveCallbackDelegate stateRemoveCallback, string statePreferencesKey)
        {
            PopulateAnimatorAudioStateSelectorTypes();
            if (s_SelectorTypeNameCache != null)
            {
                var selected    = 0;
                var forceUpdate = true;
                if (animatorAudioStateSet.AnimatorAudioStateSelectorData != null && !string.IsNullOrEmpty(animatorAudioStateSet.AnimatorAudioStateSelectorData.ObjectType))
                {
                    for (int i = 0; i < s_SelectorTypeCache.Count; ++i)
                    {
                        if (s_SelectorTypeCache[i].FullName == animatorAudioStateSet.AnimatorAudioStateSelectorData.ObjectType)
                        {
                            selected    = i;
                            forceUpdate = false;
                            break;
                        }
                    }
                }
                var newSelected = EditorGUILayout.Popup("Selector", selected, s_SelectorTypeNameCache.ToArray());
                if (newSelected != selected || forceUpdate)
                {
                    // Use the Sequence selector as the default (or recoil in the case of a melee weapon).
                    if (forceUpdate)
                    {
                        for (int i = 0; i < s_SelectorTypeCache.Count; ++i)
                        {
                            if ((randomDefaultSelector && s_SelectorTypeCache[i].FullName == "Opsive.UltimateCharacterController.Items.AnimatorAudioStates.Sequence") ||
                                (!randomDefaultSelector && s_SelectorTypeCache[i].FullName == "Opsive.UltimateCharacterController.Items.AnimatorAudioStates.ConstantRecoil"))
                            {
                                newSelected = i;
                                break;
                            }
                        }
                    }
                    var animatorAudioOutputSelector = Activator.CreateInstance(s_SelectorTypeCache[newSelected]) as AnimatorAudioStateSelector;
                    animatorAudioStateSet.AnimatorAudioStateSelectorData = Serialization.Serialize(animatorAudioOutputSelector);
                    InspectorUtility.SetDirty(target);
                }
            }

            if (animatorAudioStateSet.AnimatorAudioStateSelector != null)
            {
                EditorGUI.indentLevel++;
                InspectorUtility.DrawObject(animatorAudioStateSet.AnimatorAudioStateSelector, false, true, target, false, () => {
                    animatorAudioStateSet.AnimatorAudioStateSelectorData = Serialization.Serialize(animatorAudioStateSet.AnimatorAudioStateSelector);
                    InspectorUtility.SetDirty(target);
                });
                EditorGUI.indentLevel--;
            }

            if (animatorAudioStateSet.States == null || animatorAudioStateSet.States.Length == 0)
            {
                animatorAudioStateSet.States = new AnimatorAudioStateSet.AnimatorAudioState[] { new AnimatorAudioStateSet.AnimatorAudioState() };
            }

            var serializedObject   = new SerializedObject(target);
            var serializedProperty = serializedObject.FindProperty(animatorAudioStateSetFieldName).FindPropertyRelative("m_States");

            if (reorderableList == null)
            {
                reorderableList = new ReorderableList(animatorAudioStateSet.States, typeof(AnimatorAudioStateSet.AnimatorAudioState), false, true, true, animatorAudioStateSet.States.Length > 1);
                reorderableList.drawHeaderCallback  = OnAnimatorAudioStateListHeaderDraw;
                reorderableList.drawElementCallback = drawCallback;
                reorderableList.onSelectCallback    = selectCallback;
                reorderableList.onAddCallback       = addCallback;
                reorderableList.onRemoveCallback    = removeCallback;
                reorderableList.serializedProperty  = serializedProperty;
                if (EditorPrefs.GetInt(preferencesKey, -1) != -1)
                {
                    reorderableList.index = EditorPrefs.GetInt(preferencesKey, -1);
                }
            }

            // ReorderableLists do not like indentation.
            var indentLevel = EditorGUI.indentLevel;

            while (EditorGUI.indentLevel > 0)
            {
                EditorGUI.indentLevel--;
            }

            var listRect = GUILayoutUtility.GetRect(0, reorderableList.GetHeight());

            // Indent the list so it lines up with the rest of the content.
            listRect.x    += InspectorUtility.IndentWidth * indentLevel;
            listRect.xMax -= InspectorUtility.IndentWidth * indentLevel;
            EditorGUI.BeginChangeCheck();
            var prevPref = EditorPrefs.GetInt(preferencesKey, 0);

            reorderableList.DoList(listRect);
            while (EditorGUI.indentLevel < indentLevel)
            {
                EditorGUI.indentLevel++;
            }
            if (EditorGUI.EndChangeCheck() || prevPref != EditorPrefs.GetInt(preferencesKey, 0))
            {
                reorderableList      = null;
                reorderableAudioList = null;
                reorderableStateList = null;
                return;
            }

            if (EditorPrefs.GetInt(preferencesKey, 0) >= animatorAudioStateSet.States.Length)
            {
                EditorPrefs.SetInt(preferencesKey, 0);
            }

            serializedProperty = serializedProperty.GetArrayElementAtIndex(EditorPrefs.GetInt(preferencesKey, 0));
            EditorGUI.BeginChangeCheck();
            EditorGUILayout.PropertyField(serializedProperty.FindPropertyRelative("m_AllowDuringMovement"));
            EditorGUILayout.PropertyField(serializedProperty.FindPropertyRelative("m_RequireGrounded"));
            EditorGUILayout.PropertyField(serializedProperty.FindPropertyRelative("m_StateName"));
            EditorGUILayout.PropertyField(serializedProperty.FindPropertyRelative("m_ItemSubstateIndex"));
            if (EditorGUI.EndChangeCheck())
            {
                serializedObject.ApplyModifiedProperties();
            }

            var animatorAudioState = animatorAudioStateSet.States[EditorPrefs.GetInt(preferencesKey, 0)];

            AudioClipSetInspector.DrawAudioClipSet(animatorAudioState.AudioClipSet, serializedProperty.FindPropertyRelative("m_AudioClipSet"), ref reorderableAudioList, drawAudioElementCallback, addAudioCallback, removeAudioCallback);
            if (InspectorUtility.Foldout(animatorAudioState, new GUIContent("States"), false))
            {
                EditorGUI.indentLevel--;
                // The MovementType class derives from system.object at the base level and reorderable lists can only operate on Unity objects. To get around this restriction
                // create a dummy array within a Unity object that corresponds to the number of elements within the ability's state list. When the reorderable list is drawn
                // the ability object will be used so it's like the dummy object never existed.
                var gameObject       = new GameObject();
                var stateIndexHelper = gameObject.AddComponent <StateInspectorHelper>();
                stateIndexHelper.StateIndexData = new int[animatorAudioState.States.Length];
                for (int i = 0; i < stateIndexHelper.StateIndexData.Length; ++i)
                {
                    stateIndexHelper.StateIndexData[i] = i;
                }
                var stateIndexSerializedObject = new SerializedObject(stateIndexHelper);
                reorderableStateList = StateInspector.DrawStates(reorderableStateList, new SerializedObject(target), stateIndexSerializedObject.FindProperty("m_StateIndexData"),
                                                                 statePreferencesKey, stateDrawElementCallback, stateAddCallback,
                                                                 stateReorderCallback, stateRemoveCallback);
                GameObject.DestroyImmediate(gameObject);
                EditorGUI.indentLevel++;
            }
            GUILayout.Space(5);
        }
Exemple #7
0
        /// <summary>
        /// Draws the interface for an AnimatorItemAbilitySetData.
        /// </summary>
        private static void DrawAnimatorAbilitySet(MonoBehaviour target, Dictionary <string, ReorderableList> reordableListMap, List <ReorderableList> reordableLists,
                                                   ReorderableList.SelectCallbackDelegate selectCallback, ReorderableList.AddCallbackDelegate addCallback,
                                                   ReorderableList.RemoveCallbackDelegate removeCallback, SerializedProperty abilityStatesProperty)
        {
            // The item must be attached to a RigidbodyCharacterController.
            RigidbodyCharacterController controller = null;
            var parent = target.transform.parent;

            while (parent != null)
            {
                if ((controller = parent.GetComponent <RigidbodyCharacterController>()) != null)
                {
                    break;
                }
                parent = parent.parent;
            }
            if (controller == null)
            {
                // Remove all of the ability states as they are pointing to another character.
                if (abilityStatesProperty.arraySize > 0)
                {
                    abilityStatesProperty.ClearArray();
                    abilityStatesProperty.serializedObject.ApplyModifiedProperties();
                }
                EditorGUILayout.LabelField("Please assign the item to a character.");
                return;
            }

            // Ensure the ability states are pointing to the current character. They may not be pointing to the current character if the item was switched from one
            // GameObject to another.
            if (abilityStatesProperty.arraySize > 0)
            {
                var ability = abilityStatesProperty.GetArrayElementAtIndex(0).FindPropertyRelative("m_Ability").objectReferenceValue as Ability;
                if (ability == null || ability.gameObject != controller.gameObject)
                {
                    abilityStatesProperty.ClearArray();
                    abilityStatesProperty.serializedObject.ApplyModifiedProperties();
                }
            }

            var abilitySet = new HashSet <Ability>();
            var duplicateAbilityNameSet = new HashSet <string>();
            var abilityStates           = new List <string>();
            var newAbilityStates        = new List <Ability>();

            if (abilityStatesProperty.arraySize == 0)
            {
                abilityStates.Add("(No Ability Groups)");
            }
            else
            {
                for (int i = 0; i < abilityStatesProperty.arraySize; ++i)
                {
                    var ability = abilityStatesProperty.GetArrayElementAtIndex(i).FindPropertyRelative("m_Ability").objectReferenceValue;
                    if (ability == null)
                    {
                        abilityStatesProperty.DeleteArrayElementAtIndex(i);
                        break;
                    }
                    // Add the priority index if the list already contains a name with the same ability name.
                    var name = ability.GetType().Name;
                    if (duplicateAbilityNameSet.Contains(name))
                    {
                        name += string.Format("(Priority {0})", (ability as Ability).Index);
                    }
                    else
                    {
                        duplicateAbilityNameSet.Add(name);
                    }
                    abilityStates.Add(name);
                    abilitySet.Add(ability as Ability);
                }
            }

            // List any abilities that exist on the controller but have not been added to the ability list yet.
            duplicateAbilityNameSet.Clear();
            var newAbilityStartIndex = abilityStates.Count;

            for (int i = 0; i < controller.Abilities.Length; ++i)
            {
                var ability = controller.Abilities[i];
                if (abilitySet.Contains(ability))
                {
                    continue;
                }
                // Add the priority index if the list already contains a name with the same ability name.
                var name = ability.GetType().Name;
                if (duplicateAbilityNameSet.Contains(name))
                {
                    name += string.Format("(Priority {0})", (ability as Ability).Index);
                }
                else
                {
                    duplicateAbilityNameSet.Add(name);
                }
                abilityStates.Add("Add Ability Group/" + name);
                newAbilityStates.Add(ability);
            }

            // Save the selected index out to EditorPrefs to ensure the selected item stays selected across serialization reloads.
            var foldoutPath   = EditorPrefsKey + controller.gameObject + "." + abilityStatesProperty.propertyPath;
            var selectedIndex = EditorPrefs.GetInt(foldoutPath, 0);

            // Show the popup.
            EditorGUILayout.BeginHorizontal();
            selectedIndex = EditorGUILayout.Popup("Ability", selectedIndex, abilityStates.ToArray());

            // Only allow the delete button if the array contains at least one state.
            GUI.enabled = abilityStatesProperty.arraySize > 0;
            if (GUILayout.Button("X", GUILayout.Width(20)))
            {
                abilityStatesProperty.DeleteArrayElementAtIndex(selectedIndex);
                abilityStatesProperty.serializedObject.ApplyModifiedProperties();
                selectedIndex = 0;
            }
            GUI.enabled = true;
            EditorGUILayout.EndHorizontal();

            // Add a new ability state if the selected index is greater than the number of existing abilities.
            if (selectedIndex >= newAbilityStartIndex)
            {
                abilityStatesProperty.InsertArrayElementAtIndex(abilityStatesProperty.arraySize);
                var abilitySetProperty = abilityStatesProperty.GetArrayElementAtIndex(abilityStatesProperty.arraySize - 1);
                abilitySetProperty.FindPropertyRelative("m_Ability").objectReferenceValue = newAbilityStates[selectedIndex - newAbilityStartIndex];
                var groups = abilitySetProperty.FindPropertyRelative("m_Groups");
                // Add the default group and group state.
                if (groups.arraySize == 0)
                {
                    abilitySetProperty.FindPropertyRelative("m_Groups").InsertArrayElementAtIndex(0);
                    var group = abilitySetProperty.FindPropertyRelative("m_Groups").GetArrayElementAtIndex(0);
                    AddGroupState(group.FindPropertyRelative("m_States"));
                }
                abilityStatesProperty.serializedObject.ApplyModifiedProperties();
                selectedIndex = abilityStatesProperty.arraySize - 1;
            }
            else if (abilityStatesProperty.arraySize > 0)
            {
                // The ability isn't being added or removed. Show the selected ability set.
                DrawAnimatorSet(target, reordableListMap, reordableLists, selectCallback, addCallback, removeCallback, abilityStatesProperty.GetArrayElementAtIndex(selectedIndex), true);
            }
            EditorPrefs.SetInt(foldoutPath, selectedIndex);
        }
        /// <summary>
        /// Draws the ReorderableList.
        /// </summary>
        public static void DrawReorderableList(ref ReorderableList reorderableList, InspectorBase inspector, Array drawnObject, string serializedData,
                                               ReorderableList.HeaderCallbackDelegate drawHeaderCallback, ReorderableList.ElementCallbackDelegate drawElementCallback,
                                               ReorderableList.ReorderCallbackDelegate reorderCallback, ReorderableList.AddCallbackDelegate addCallback,
                                               ReorderableList.RemoveCallbackDelegate removeCallback, ReorderableList.SelectCallbackDelegate selectCallback,
                                               Action <int> drawSelectedElementCallback, string key, bool requireOne, bool indentList)
        {
            // Initialize the reorder list on first run.
            if (reorderableList == null)
            {
                var data = inspector.PropertyFromName(inspector.serializedObject, serializedData);
                reorderableList = new ReorderableList(inspector.serializedObject, data, (reorderCallback != null), true, !Application.isPlaying,
                                                      !Application.isPlaying && (!requireOne || (drawnObject != null && drawnObject.Length > 1)));
                reorderableList.drawHeaderCallback = (Rect rect) =>
                {
                    EditorGUI.LabelField(rect, "Name");
                };
                if (drawHeaderCallback != null)
                {
                    reorderableList.drawHeaderCallback = drawHeaderCallback;
                }
                reorderableList.drawElementCallback = drawElementCallback;
                if (reorderCallback != null)
                {
                    reorderableList.onReorderCallback = reorderCallback;
                }
                reorderableList.onAddCallback    = addCallback;
                reorderableList.onRemoveCallback = removeCallback;
                reorderableList.onSelectCallback = selectCallback;
                if (EditorPrefs.GetInt(key, -1) != -1)
                {
                    reorderableList.index = EditorPrefs.GetInt(key, -1);
                }
            }

            var indentLevel = EditorGUI.indentLevel;

            if (indentList)
            {
                // ReorderableLists do not like indentation.
                while (EditorGUI.indentLevel > 0)
                {
                    EditorGUI.indentLevel--;
                }
            }

            var listRect = GUILayoutUtility.GetRect(0, reorderableList.GetHeight());

            // Indent the list so it lines up with the rest of the content.
            if (indentList)
            {
                listRect.x    += Shared.Editor.Inspectors.Utility.InspectorUtility.IndentWidth * indentLevel;
                listRect.xMax -= Shared.Editor.Inspectors.Utility.InspectorUtility.IndentWidth * indentLevel;
            }
            reorderableList.DoList(listRect);
            while (EditorGUI.indentLevel < indentLevel)
            {
                EditorGUI.indentLevel++;
            }
            if (reorderableList != null && reorderableList.index != -1)
            {
                if (drawnObject != null && reorderableList.index < drawnObject.Length)
                {
                    drawSelectedElementCallback(reorderableList.index);
                }
            }
        }
        /// <summary>
        /// Creates a cached ReorderableList that can be used on a IList. The serializedProperty passed is used for look-up and is not used in the ReorderableList itself.
        /// </summary>
        /// <param name="memberList"></param>
        /// <param name="tokenProperty"></param>
        /// <param name="drawHeaderCallback"></param>
        /// <param name="drawElementCallback"></param>
        /// <param name="onAddCallback"></param>
        /// <param name="onRemoveCallback"></param>
        /// <param name="onSelectCallback"></param>
        /// <param name="onChangedCallback"></param>
        /// <param name="onReorderCallback"></param>
        /// <param name="onCanRemoveCallback"></param>
        /// <param name="onAddDropdownCallback"></param>
        /// <returns></returns>
        public static CachedReorderableList GetListDrawer(System.Collections.IList memberList, SerializedProperty tokenProperty, ReorderableList.HeaderCallbackDelegate drawHeaderCallback, ReorderableList.ElementCallbackDelegate drawElementCallback,
                                                          ReorderableList.AddCallbackDelegate onAddCallback                 = null, ReorderableList.RemoveCallbackDelegate onRemoveCallback   = null, ReorderableList.SelectCallbackDelegate onSelectCallback = null,
                                                          ReorderableList.ChangedCallbackDelegate onChangedCallback         = null, ReorderableList.ReorderCallbackDelegate onReorderCallback = null, ReorderableList.CanRemoveCallbackDelegate onCanRemoveCallback = null,
                                                          ReorderableList.AddDropdownCallbackDelegate onAddDropdownCallback = null)
        {
            if (memberList == null)
            {
                throw new System.ArgumentNullException("memberList");
            }
            if (tokenProperty == null)
            {
                throw new System.ArgumentNullException("property");
            }

            int hash = PropertyHandlerCache.GetIndexRespectingPropertyHash(tokenProperty);
            CachedReorderableList lst;

            if (_lstCache.TryGetValue(hash, out lst))
            {
                lst.ReInit(memberList);
            }
            else
            {
                lst             = new CachedReorderableList(memberList);
                _lstCache[hash] = lst;
            }

            lst.drawHeaderCallback    = drawHeaderCallback;
            lst.drawElementCallback   = drawElementCallback;
            lst.onAddCallback         = onAddCallback;
            lst.onRemoveCallback      = onRemoveCallback;
            lst.onSelectCallback      = onSelectCallback;
            lst.onChangedCallback     = onChangedCallback;
            lst.onReorderCallback     = onReorderCallback;
            lst.onCanRemoveCallback   = onCanRemoveCallback;
            lst.onAddDropdownCallback = onAddDropdownCallback;

            return(lst);
        }