Пример #1
0
        /// <summary>
        /// Inserts a new state element in the state array.
        /// </summary>
        private static Opsive.Shared.StateSystem.State[] InsertStateElement(Opsive.Shared.StateSystem.State[] states, ReorderableList reorderableList, string selectedIndexKey, string name, PersistablePreset preset)
        {
            // The name has to be unique to prevent it from interferring with other state names.
            if (!IsUniqueName(states, name))
            {
                var postfixIndex = 1;
                while (!IsUniqueName(states, name + " " + postfixIndex))
                {
                    postfixIndex++;
                }
                name += " " + postfixIndex;
            }

            // Create the element.
            var state = new Opsive.Shared.StateSystem.State(name, false);

            state.Preset = preset;
            var stateList = new List <Opsive.Shared.StateSystem.State>(states);

            stateList.Insert(0, state);
            reorderableList.displayRemove = stateList.Count > 1;

            // Select the new element.
            reorderableList.index = stateList.Count - 1;
            EditorPrefs.SetInt(selectedIndexKey, reorderableList.index);
            return(stateList.ToArray());
        }
Пример #2
0
        /// <summary>
        /// Draws all of the added states.
        /// </summary>
        public static void OnStateListDraw(object obj, Opsive.Shared.StateSystem.State[] states, SerializedProperty statesProperty, Rect rect, int index)
        {
            if (rect.width < 0)
            {
                return;
            }

            // States cannot be edited at runtime, nor can the Default state ever be edited.
            GUI.enabled = !Application.isPlaying && index != states.Length - 1;
            rect.x     -= EditorGUI.indentLevel * Utility.InspectorUtility.IndentWidth;

            // Ensure the default state doesn't get changed.
            var state         = states[index];
            var stateProperty = (statesProperty != null ? statesProperty.GetArrayElementAtIndex(index) : null);

            if (!Application.isPlaying && index == states.Length - 1)
            {
                if (statesProperty != null && stateProperty == null)
                {
                    statesProperty.InsertArrayElementAtIndex(index);
                    stateProperty = statesProperty.GetArrayElementAtIndex(index);
                    stateProperty.FindPropertyRelative("m_Name").stringValue  = "Default";
                    stateProperty.FindPropertyRelative("m_Default").boolValue = true;
                }
                else if (statesProperty == null && state == null)
                {
                    states[index] = state = new Opsive.Shared.StateSystem.State("Default", true);
                    GUI.changed   = true;
                }
                if (state.Name != "Default")
                {
                    if (stateProperty != null)
                    {
                        stateProperty.FindPropertyRelative("m_Name").stringValue = "Default";
                    }
                    else
                    {
                        state.Name = "Default";
                    }
                    GUI.changed = true;
                }
                if (!state.Default)
                {
                    if (stateProperty != null)
                    {
                        stateProperty.FindPropertyRelative("m_Default").boolValue = true;
                    }
                    else
                    {
                        state.Default = true;
                    }
                    GUI.changed = true;
                }
            }

            // Setup the field sizings.
            var fieldWidth     = rect.width / 5;
            var blockedByWidth = Mathf.Max(c_MinBlockedByWidth, Mathf.Min(c_MaxBlockedByWidth, fieldWidth)) + EditorGUI.indentLevel * Utility.InspectorUtility.IndentWidth;

            fieldWidth = rect.width / 7;
            var persistWidth  = Mathf.Max(c_MinPersistWidth, Mathf.Min(c_MaxPersistWidth, fieldWidth));
            var activateWidth = Mathf.Max(c_MinActivateWidth, Mathf.Min(c_MaxActivateWidth, fieldWidth));

            fieldWidth = (rect.width - blockedByWidth - persistWidth - activateWidth) / 2 - (c_WidthBuffer * 3);
            var presetWidth = Mathf.Min(c_MaxPresetWidth, fieldWidth) + EditorGUI.indentLevel * 30;
            var nameWidth   = Mathf.Max(0, rect.width - presetWidth - blockedByWidth - persistWidth - activateWidth - (c_WidthBuffer * 6)) + EditorGUI.indentLevel * 45;
            var startRectX  = rect.x;

            // The state name has to be unique.
            var active      = state.Active && !state.IsBlocked();
            var desiredName = EditorGUI.TextField(new Rect(startRectX, rect.y + 1, nameWidth, EditorGUIUtility.singleLineHeight), state.Name +
                                                  (active ? " (Active)" : string.Empty),
                                                  (active ? Utility.InspectorStyles.BoldTextField : EditorStyles.textField));

            if (!Application.isPlaying && desiredName != state.Name && IsUniqueName(states, desiredName))
            {
                // The name of the state that is blocking the current state should be updated.
                for (int i = 0; i < states.Length; ++i)
                {
                    if (states[i] == state)
                    {
                        continue;
                    }

                    if (states[i].BlockList != null)
                    {
                        for (int j = 0; j < states[i].BlockList.Length; ++j)
                        {
                            if (states[i].BlockList[j] == state.Name)
                            {
                                if (stateProperty != null)
                                {
                                    statesProperty.GetArrayElementAtIndex(i).FindPropertyRelative("m_BlockList").GetArrayElementAtIndex(j).stringValue = desiredName;
                                }
                                else
                                {
                                    states[i].BlockList[j] = desiredName;
                                }
                            }
                        }
                    }
                }

                if (stateProperty != null)
                {
                    stateProperty.FindPropertyRelative("m_Name").stringValue = desiredName;
                }
                else
                {
                    state.Name = desiredName;
                }
            }
            startRectX += nameWidth + c_WidthBuffer - EditorGUI.indentLevel * Utility.InspectorUtility.IndentWidth;

            // The preset cannot be null.
            var desiredPreset = EditorGUI.ObjectField(new Rect(startRectX, rect.y + 1, presetWidth,
                                                               EditorGUIUtility.singleLineHeight), string.Empty, state.Preset, typeof(PersistablePreset), false) as PersistablePreset;

            if (desiredPreset != null)
            {
                var objType = TypeUtility.GetType(desiredPreset.Data.ObjectType);
                if (objType != null && objType.IsInstanceOfType(obj))
                {
                    if (stateProperty != null)
                    {
                        stateProperty.FindPropertyRelative("m_Preset").objectReferenceValue = desiredPreset;
                    }
                    else
                    {
                        state.Preset = desiredPreset;
                    }
                }
                else
                {
                    Debug.LogError($"Error: Unable to add preset. {desiredPreset.name} ({desiredPreset.Data.ObjectType}) doesn't use the same object type ({obj.GetType().FullName}).");
                }
            }
            startRectX += presetWidth + c_WidthBuffer - EditorGUI.indentLevel * Utility.InspectorUtility.IndentWidth;

            // Create a popup of the states that can block the current state. There are several conditions which would prevent a state from being able to block
            // another state so this popup has to first be filtered.
            var stateName = state.Name;
            var blockList = state.BlockList;
            var allStates = new List <string>();
            var selected  = 0;

            for (int i = 0; i < states.Length; ++i)
            {
                var currentState = states[i];
                if (currentState == null)
                {
                    states[i] = currentState = new Opsive.Shared.StateSystem.State();
                }
                // The current state cannot block the default state.
                if (currentState.Default)
                {
                    continue;
                }
                string name;
                // The current state cannot block itself.
                if ((name = currentState.Name) == stateName)
                {
                    continue;
                }
                // The selected state cannot block the current state if the current state blocks the selected state.
                var currentStateBlockList = currentState.BlockList;
                var canAdd = true;
                if (currentStateBlockList != null)
                {
                    for (int j = 0; j < currentStateBlockList.Length; ++j)
                    {
                        if (stateName == currentStateBlockList[j])
                        {
                            canAdd = false;
                            break;
                        }
                    }
                }

                // canAdd will be false if the current state is blocking the selected state.
                if (!canAdd)
                {
                    continue;
                }

                // The current state can block the selected state. Add the name to the popup and determine if the state is selected. A mask is used
                // to allow multiple selected states.
                allStates.Add(name);
                if (blockList != null)
                {
                    for (int j = 0; j < blockList.Length; ++j)
                    {
                        if (allStates[allStates.Count - 1] == blockList[j])
                        {
                            selected |= 1 << (allStates.Count - 1);
                            break;
                        }
                    }
                }
            }
            // At least one value needs to exist.
            if (allStates.Count == 0)
            {
                allStates.Add("Nothing");
            }

            // Draw the actual popup.
            var blockMask = EditorGUI.MaskField(new Rect(startRectX, rect.y + 1, blockedByWidth, EditorGUIUtility.singleLineHeight), string.Empty, selected, allStates.ToArray());

            if (blockMask != selected)
            {
                var stateNames        = new List <string>();
                var blockListProperty = (stateProperty != null ? stateProperty.FindPropertyRelative("m_BlockList") : null);
                if (blockListProperty != null)
                {
                    blockListProperty.ClearArray();
                }
                for (int i = 0; i < allStates.Count; ++i)
                {
                    // If the state index is within the block mask then that state should be added to the list. A blockMask of -1 indicates Everything.
                    if (((1 << i) & blockMask) != 0 || blockMask == -1)
                    {
                        if (blockListProperty != null)
                        {
                            blockListProperty.InsertArrayElementAtIndex(blockListProperty.arraySize);
                            blockListProperty.GetArrayElementAtIndex(blockListProperty.arraySize - 1).stringValue = allStates[i];
                        }
                        else
                        {
                            stateNames.Add(allStates[i]);
                        }
                    }
                }
                if (blockListProperty == null)
                {
                    state.BlockList = stateNames.ToArray();
                }
            }
            startRectX += blockedByWidth + c_WidthBuffer;

            GUI.enabled = index < states.Length - 1;

            if (GUI.Button(new Rect(startRectX + persistWidth / 2, rect.y + 1, 18, EditorGUIUtility.singleLineHeight), Utility.InspectorStyles.PersistIcon, Utility.InspectorStyles.NoPaddingButtonStyle))
            {
                // Populate the position map so ObjectInspector.DrawProperties to know which properties already exist.
                var valuePositionMap = new Dictionary <int, int>(desiredPreset.Data.ValueHashes.Length);
                for (int i = 0; i < desiredPreset.Data.ValueHashes.Length; ++i)
                {
                    valuePositionMap.Add(desiredPreset.Data.ValueHashes[i], i);
                }

                // Loop through all of the properties on the object.
                var properties  = Serialization.GetSerializedProperties(obj.GetType(), MemberVisibility.Public);
                var bitwiseHash = new System.Version(desiredPreset.Data.Version).CompareTo(new System.Version("3.1")) >= 0;
                // Remove and add the properties that are being serialized.
                for (int i = 0; i < properties.Length; ++i)
                {
                    var hash = Serialization.StringHash(properties[i].PropertyType.FullName) + Serialization.StringHash(properties[i].Name);
                    // The property is currently being serialized.
                    if (valuePositionMap.ContainsKey(hash))
                    {
                        // Add the new property to the serialization.
                        object value    = null;
                        var    property = properties[i];
                        if (!typeof(Object).IsAssignableFrom(property.PropertyType))
                        {
                            var unityObjectIndexes = new List <int>();
                            Serialization.GetUnityObjectIndexes(ref unityObjectIndexes, property.PropertyType, property.Name, 0, valuePositionMap, desiredPreset.Data.ValueHashes, desiredPreset.Data.ValuePositions,
                                                                desiredPreset.Data.Values, false, MemberVisibility.Public, bitwiseHash);

                            Serialization.RemoveProperty(i, unityObjectIndexes, desiredPreset.Data, MemberVisibility.Public, bitwiseHash);

                            // Get the current value of the active object.
                            var getMethod = property.GetGetMethod();
                            if (getMethod != null)
                            {
                                value = getMethod.Invoke(obj, null);
                            }
                            // Add the property back with the updated value.
                            Serialization.AddProperty(property, value, unityObjectIndexes, desiredPreset.Data, MemberVisibility.Public);
                        }
                    }
                }
            }
            startRectX += persistWidth + c_WidthBuffer;

            GUI.enabled = Application.isPlaying && index < states.Length - 1;
            if (GUI.Button(new Rect(startRectX + activateWidth / 2, rect.y + 1, 18, EditorGUIUtility.singleLineHeight), Utility.InspectorStyles.ActivateIcon, Utility.InspectorStyles.NoPaddingButtonStyle))
            {
                StateManager.ActivateState(states[index], !states[index].Active, states);
            }

            GUI.enabled = true;
        }