private void RenderSettings() { EditorGUILayout.Space(); EditorGUILayout.PropertyField(active); if (Application.isPlaying) { if (!active.boolValue) { // Add a notice in the inspector to let the user know that they have disabled internal updates, i.e. state values will not update InspectorUIUtility.DrawNotice("Internal updates to this Interactive Element have been disabled."); } if (previousActiveStatus != active.boolValue) { // Only reset all states when Active is set to false if (!active.boolValue) { ResetAllStates(); } } previousActiveStatus = active.boolValue; } }
public virtual void RenderCustomInspector() { serializedObject.Update(); Rect position; bool isPlayMode = EditorApplication.isPlaying || EditorApplication.isPaused; #region General Settings EditorGUILayout.BeginHorizontal(); InspectorUIUtility.DrawTitle("General"); InspectorUIUtility.RenderDocLinkButton(Interactable_URL); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginVertical(EditorStyles.helpBox); // States SerializedProperty states = serializedObject.FindProperty("States"); // If states value is not provided, try to use Default states type if (states.objectReferenceValue == null) { states.objectReferenceValue = ThemeInspector.GetDefaultInteractableStates(); } GUI.enabled = !isPlayMode; EditorGUILayout.PropertyField(states, new GUIContent("States", "The States this Interactable is based on")); GUI.enabled = true; if (states.objectReferenceValue == null) { InspectorUIUtility.DrawError("Please assign a States object!"); EditorGUILayout.EndVertical(); serializedObject.ApplyModifiedProperties(); return; } //standard Interactable Object UI SerializedProperty enabled = serializedObject.FindProperty("Enabled"); EditorGUILayout.PropertyField(enabled, new GUIContent("Enabled", "Is this Interactable Enabled?")); SerializedProperty actionId = serializedObject.FindProperty("InputActionId"); if (actionOptions == null) { GUI.enabled = false; EditorGUILayout.Popup("Input Actions", 0, new string[] { "Missing Mixed Reality Toolkit" }); GUI.enabled = true; } else { position = EditorGUILayout.GetControlRect(); DrawDropDownProperty(position, actionId, actionOptions, new GUIContent("Input Actions", "The input action filter")); } using (new EditorGUI.IndentLevelScope()) { SerializedProperty isGlobal = serializedObject.FindProperty("IsGlobal"); EditorGUILayout.PropertyField(isGlobal, new GUIContent("Is Global", "Like a modal, does not require focus")); } SerializedProperty voiceCommands = serializedObject.FindProperty("VoiceCommand"); // check speech commands profile for a list of commands if (speechKeywords == null) { GUI.enabled = false; EditorGUILayout.Popup("Speech Command", 0, new string[] { "Missing Speech Commands" }); InspectorUIUtility.DrawNotice("Create speech commands in the MRTK/Input/Speech Commands Profile"); GUI.enabled = true; } else { //look for items in the sppech commands list that match the voiceCommands string // this string should be empty if we are not listening to speech commands // will return zero if empty, to match the inserted off value. int currentIndex = SpeechKeywordLookup(voiceCommands.stringValue, speechKeywords); GUI.enabled = !isPlayMode; position = EditorGUILayout.GetControlRect(); GUIContent label = new GUIContent("Speech Command", "Speech Commands to use with Interactable, pulled from MRTK/Input/Speech Commands Profile"); EditorGUI.BeginProperty(position, label, voiceCommands); { currentIndex = EditorGUI.Popup(position, label, currentIndex, speechKeywords); if (currentIndex > 0) { voiceCommands.stringValue = speechKeywords[currentIndex].text; } else { voiceCommands.stringValue = ""; } } EditorGUI.EndProperty(); GUI.enabled = true; } // show requires gaze because voice command has a value if (!string.IsNullOrEmpty(voiceCommands.stringValue)) { using (new EditorGUI.IndentLevelScope()) { SerializedProperty requireGaze = serializedObject.FindProperty("RequiresFocus"); EditorGUILayout.PropertyField(requireGaze, new GUIContent("Requires Focus", "Does the voice command require gazing at this interactable?")); } } SerializedProperty dimensions = serializedObject.FindProperty("Dimensions"); // should be 1 or more dimensions.intValue = Mathf.Clamp(dimensions.intValue, 1, 9); string[] selectionModeNames = Enum.GetNames(typeof(SelectionModes)); // clamp to values in the enum int selectionModeIndex = Mathf.Clamp(dimensions.intValue, 1, selectionModeNames.Length) - 1; // user-friendly dimension settings SelectionModes selectionMode = SelectionModes.Button; position = EditorGUILayout.GetControlRect(); GUI.enabled = !isPlayMode; EditorGUI.BeginProperty(position, selectionModeLabel, dimensions); { selectionMode = (SelectionModes)EditorGUI.EnumPopup(position, selectionModeLabel, (SelectionModes)(selectionModeIndex)); switch (selectionMode) { case SelectionModes.Button: dimensions.intValue = 1; break; case SelectionModes.Toggle: dimensions.intValue = 2; break; case SelectionModes.MultiDimension: // multi dimension mode - set min value to 3 dimensions.intValue = Mathf.Max(3, dimensions.intValue); position = EditorGUILayout.GetControlRect(); dimensions.intValue = EditorGUI.IntField(position, dimensionsLabel, dimensions.intValue); break; default: break; } } EditorGUI.EndProperty(); if (dimensions.intValue > 1) { // toggle or multi dimensional button using (new EditorGUI.IndentLevelScope()) { SerializedProperty canSelect = serializedObject.FindProperty("CanSelect"); SerializedProperty canDeselect = serializedObject.FindProperty("CanDeselect"); SerializedProperty startDimensionIndex = serializedObject.FindProperty("StartDimensionIndex"); EditorGUILayout.PropertyField(canSelect, new GUIContent("Can Select", "The user can toggle this button")); EditorGUILayout.PropertyField(canDeselect, new GUIContent("Can Deselect", "The user can untoggle this button, set false for a radial interaction.")); position = EditorGUILayout.GetControlRect(); EditorGUI.BeginProperty(position, startDimensionLabel, startDimensionIndex); { if (dimensions.intValue >= selectionModeNames.Length) { // multi dimensions if (!isPlayMode) { startDimensionIndex.intValue = EditorGUI.IntField(position, startDimensionLabel, startDimensionIndex.intValue); } else { SerializedProperty dimensionIndex = serializedObject.FindProperty("dimensionIndex"); EditorGUI.IntField(position, CurrentDimensionLabel, dimensionIndex.intValue); } } else if (dimensions.intValue == (int)SelectionModes.Toggle + 1) { // toggle if (!isPlayMode) { bool isToggled = EditorGUI.Toggle(position, isToggledLabel, startDimensionIndex.intValue > 0); startDimensionIndex.intValue = isToggled ? 1 : 0; } else { SerializedProperty dimensionIndex = serializedObject.FindProperty("dimensionIndex"); bool isToggled = EditorGUI.Toggle(position, isToggledLabel, dimensionIndex.intValue > 0); } } startDimensionIndex.intValue = Mathf.Clamp(startDimensionIndex.intValue, 0, dimensions.intValue - 1); } EditorGUI.EndProperty(); } GUI.enabled = true; } EditorGUILayout.EndVertical(); #endregion EditorGUILayout.Space(); if (!ProfilesSetup && !showProfiles) { InspectorUIUtility.DrawWarning("Profiles (Optional) have not been set up or has errors."); } #region Profiles bool isProfilesOpen = InspectorUIUtility.DrawSectionFoldout("Profiles", showProfiles, FontStyle.Bold, InspectorUIUtility.TitleFontSize); if (showProfiles != isProfilesOpen) { showProfiles = isProfilesOpen; EditorPrefs.SetBool(ShowProfilesPrefKey, showProfiles); } if (profileList.arraySize < 1) { AddProfile(0); } int validProfileCnt = 0; int themeCnt = 0; if (showProfiles) { for (int i = 0; i < profileList.arraySize; i++) { EditorGUILayout.BeginVertical(EditorStyles.helpBox); // get profiles SerializedProperty sItem = profileList.GetArrayElementAtIndex(i); SerializedProperty gameObject = sItem.FindPropertyRelative("Target"); string targetName = "Profile " + (i + 1); if (gameObject.objectReferenceValue != null) { targetName = gameObject.objectReferenceValue.name; validProfileCnt++; } EditorGUILayout.BeginHorizontal(); EditorGUILayout.PropertyField(gameObject, new GUIContent("Target", "Target gameObject for this theme properties to manipulate")); bool triggered = InspectorUIUtility.SmallButton(new GUIContent(InspectorUIUtility.Minus, "Remove Profile"), i, RemoveProfile); if (triggered) { continue; } EditorGUILayout.EndHorizontal(); // get themes SerializedProperty themes = sItem.FindPropertyRelative("Themes"); // make sure there are enough themes as dimensions if (themes.arraySize > dimensions.intValue) { // make sure there are not more themes than dimensions int cnt = themes.arraySize - 1; for (int j = cnt; j > dimensions.intValue - 1; j--) { themes.DeleteArrayElementAtIndex(j); } } // add themes when increasing dimensions if (themes.arraySize < dimensions.intValue) { int cnt = themes.arraySize; for (int j = cnt; j < dimensions.intValue; j++) { themes.InsertArrayElementAtIndex(themes.arraySize); SerializedProperty theme = themes.GetArrayElementAtIndex(themes.arraySize - 1); string[] themeLocations = AssetDatabase.FindAssets("DefaultTheme"); if (themeLocations.Length > 0) { for (int k = 0; k < themeLocations.Length; k++) { string path = AssetDatabase.GUIDToAssetPath(themeLocations[k]); Theme defaultTheme = (Theme)AssetDatabase.LoadAssetAtPath(path, typeof(Theme)); if (defaultTheme != null) { theme.objectReferenceValue = defaultTheme; break; } } } } } // Render all themes for current target for (int t = 0; t < themes.arraySize; t++) { SerializedProperty themeItem = themes.GetArrayElementAtIndex(t); string themeLabel = BuildThemeTitle(selectionMode, t); EditorGUILayout.PropertyField(themeItem, new GUIContent(themeLabel, "Theme properties for interaction feedback")); if (themeItem.objectReferenceValue != null && gameObject.objectReferenceValue) { if (themeItem.objectReferenceValue.name == "DefaultTheme") { EditorGUILayout.BeginHorizontal(); InspectorUIUtility.DrawWarning("DefaultTheme should not be edited. "); bool newTheme = InspectorUIUtility.FlexButton(new GUIContent("Create Theme", "Create a new theme"), new int[] { i, t, 0 }, CreateTheme); if (newTheme) { continue; } EditorGUILayout.EndHorizontal(); } SerializedProperty hadDefault = sItem.FindPropertyRelative("HadDefaultTheme"); hadDefault.boolValue = true; string prefKey = themeItem.objectReferenceValue.name + "Profiles" + i + "_Theme" + t + "_Edit"; bool hasPref = EditorPrefs.HasKey(prefKey); bool showSettings = EditorPrefs.GetBool(prefKey); if (!hasPref) { showSettings = true; } InspectorUIUtility.ListSettings settings = listSettings[i]; bool show = InspectorUIUtility.DrawSectionFoldout(themeItem.objectReferenceValue.name + " (Click to edit)", showSettings, FontStyle.Normal); if (show != showSettings) { EditorPrefs.SetBool(prefKey, show); settings.Show = show; } if (show) { SerializedObject themeObj = new SerializedObject(themeItem.objectReferenceValue); SerializedProperty themeObjSettings = themeObj.FindProperty("Settings"); themeObj.Update(); GUILayout.Space(5); if (themeObjSettings.arraySize < 1) { AddThemeProperty(new int[] { i, t, 0 }); } int[] location = new int[] { i, t, 0 }; State[] iStates = GetStates(); ThemeInspector.RenderThemeSettings(themeObjSettings, themeObj, themeOptions, gameObject, location, iStates, ThemePropertiesBoxMargin); InspectorUIUtility.FlexButton(new GUIContent("+", "Add Theme Property"), location, AddThemeProperty); ThemeInspector.RenderThemeStates(themeObjSettings, iStates, ThemePropertiesBoxMargin); themeObj.ApplyModifiedProperties(); } listSettings[i] = settings; validProfileCnt++; } else { // show message about profile setup string themeMsg = "Assign a "; if (gameObject.objectReferenceValue == null) { themeMsg += "Target "; } if (themeItem.objectReferenceValue == null) { if (gameObject.objectReferenceValue == null) { themeMsg += "and "; } themeMsg += "Theme "; } themeMsg += "above to add visual effects"; SerializedProperty hadDefault = sItem.FindPropertyRelative("HadDefaultTheme"); if (!hadDefault.boolValue && t == 0) { string[] themeLocations = AssetDatabase.FindAssets("DefaultTheme"); if (themeLocations.Length > 0) { for (int j = 0; j < themeLocations.Length; j++) { string path = AssetDatabase.GUIDToAssetPath(themeLocations[0]); Theme defaultTheme = (Theme)AssetDatabase.LoadAssetAtPath(path, typeof(Theme)); if (defaultTheme != null) { themeItem.objectReferenceValue = defaultTheme; break; } } if (themeItem.objectReferenceValue != null) { hadDefault.boolValue = true; } } else { InspectorUIUtility.DrawError("DefaultTheme missing from project!"); } } InspectorUIUtility.DrawError(themeMsg); } themeCnt += themes.arraySize; } EditorGUILayout.EndVertical(); }// profile for loop if (GUILayout.Button(new GUIContent("Add Profile"))) { AddProfile(profileList.arraySize); } } else { // make sure profiles are setup if closed by default for (int i = 0; i < profileList.arraySize; i++) { SerializedProperty sItem = profileList.GetArrayElementAtIndex(i); SerializedProperty gameObject = sItem.FindPropertyRelative("Target"); SerializedProperty themes = sItem.FindPropertyRelative("Themes"); if (gameObject.objectReferenceValue != null) { validProfileCnt++; } for (int t = 0; t < themes.arraySize; t++) { SerializedProperty themeItem = themes.GetArrayElementAtIndex(themes.arraySize - 1); if (themeItem.objectReferenceValue != null && gameObject.objectReferenceValue) { validProfileCnt++; SerializedProperty hadDefault = sItem.FindPropertyRelative("HadDefaultTheme"); hadDefault.boolValue = true; } } themeCnt += themes.arraySize; } } ProfilesSetup = validProfileCnt == profileList.arraySize + themeCnt; #endregion EditorGUILayout.Space(); #region Events settings bool isEventsOpen = InspectorUIUtility.DrawSectionFoldout("Events", showEvents, FontStyle.Bold, InspectorUIUtility.TitleFontSize); if (showEvents != isEventsOpen) { showEvents = isEventsOpen; EditorPrefs.SetBool(ShowEventsPrefKey, showEvents); } EditorGUILayout.Space(); if (showEvents) { SerializedProperty onClick = serializedObject.FindProperty("OnClick"); EditorGUILayout.PropertyField(onClick, new GUIContent("OnClick")); SerializedProperty events = serializedObject.FindProperty("Events"); GUI.enabled = !isPlayMode; for (int i = 0; i < events.arraySize; i++) { SerializedProperty eventItem = events.GetArrayElementAtIndex(i); InteractableReceiverListInspector.RenderEventSettings(eventItem, i, eventOptions, ChangeEvent, RemoveEvent); } GUI.enabled = true; if (eventOptions.ClassNames.Length > 1) { if (GUILayout.Button(new GUIContent("Add Event"))) { AddEvent(events.arraySize); } } } #endregion serializedObject.ApplyModifiedProperties(); }
public override void OnInspectorGUI() { //base.OnInspectorGUI(); serializedObject.Update(); InspectorUIUtility.DrawTitle("States"); InspectorUIUtility.DrawNotice("Manage state configurations to drive Interactables or Transitions"); // get the list of options and InteractableStates stateOptions = instance.StateOptions; SerializedProperty stateLogicName = serializedObject.FindProperty("StateLogicName"); SerializedProperty assemblyQualifiedName = serializedObject.FindProperty("AssemblyQualifiedName"); int option = States.ReverseLookup(stateLogicName.stringValue, stateOptions.ClassNames); int newLogic = EditorGUILayout.Popup("State Model", option, stateOptions.ClassNames); if (option != newLogic) { stateLogicName.stringValue = stateOptions.ClassNames[newLogic]; assemblyQualifiedName.stringValue = stateOptions.AssemblyQualifiedNames[newLogic]; } int bitCount = 0; for (int i = 0; i < stateList.arraySize; i++) { if (i == 0) { bitCount += 1; } else { bitCount += bitCount; } EditorGUILayout.BeginVertical("Box"); SerializedProperty stateItem = stateList.GetArrayElementAtIndex(i); SerializedProperty name = stateItem.FindPropertyRelative("Name"); SerializedProperty index = stateItem.FindPropertyRelative("ActiveIndex"); SerializedProperty bit = stateItem.FindPropertyRelative("Bit"); index.intValue = i; EditorGUILayout.BeginHorizontal(); string[] stateEnums = GetStateOptions(); int enumIndex = States.ReverseLookup(name.stringValue, stateEnums); int newEnumIndex = EditorGUILayout.Popup(name.stringValue + " (" + bitCount + ")", enumIndex, stateEnums); if (enumIndex != newEnumIndex) { name.stringValue = stateEnums[newEnumIndex]; } InspectorUIUtility.SmallButton(new GUIContent(InspectorUIUtility.Minus, "Remove State"), i, RemoveState); EditorGUILayout.EndHorizontal(); // assign the bitcount based on location in the list bit.intValue = bitCount; EditorGUILayout.EndVertical(); } InspectorUIUtility.FlexButton(new GUIContent("+", "Add Theme Property"), 0, AddState); serializedObject.ApplyModifiedProperties(); }
public virtual void RenderCustomInspector() { serializedObject.Update(); Rect position; EditorGUILayout.Space(); InspectorUIUtility.DrawTitle("Interactable"); EditorGUILayout.BeginVertical("Box"); bool isPlayMode = EditorApplication.isPlaying || EditorApplication.isPaused; // States bool showStates = false; SerializedProperty states = serializedObject.FindProperty("States"); bool drawerStarted = false; string statesPrefKey = "Settings_States"; bool prefsShowStates = EditorPrefs.GetBool(statesPrefKey); if (states.objectReferenceValue != null) { EditorGUI.indentLevel = indentOnSectionStart + 1; showStates = InspectorUIUtility.DrawSectionStart(states.objectReferenceValue.name + " (Click to edit)", indentOnSectionStart + 2, prefsShowStates, FontStyle.Normal, false); drawerStarted = true; if (showStates != prefsShowStates) { EditorPrefs.SetBool(statesPrefKey, showStates); } } else { AssetDatabase.Refresh(); string[] stateLocations = AssetDatabase.FindAssets("DefaultInteractableStates"); if (stateLocations.Length > 0) { for (int i = 0; i < stateLocations.Length; i++) { string path = AssetDatabase.GUIDToAssetPath(stateLocations[i]); States defaultStates = (States)AssetDatabase.LoadAssetAtPath(path, typeof(States)); if (defaultStates != null) { states.objectReferenceValue = defaultStates; break; } } EditorGUI.indentLevel = indentOnSectionStart + 1; showStates = InspectorUIUtility.DrawSectionStart(states.objectReferenceValue.name + " (Click to edit)", indentOnSectionStart + 2, prefsShowStates, FontStyle.Normal, false); drawerStarted = true; } else { showStates = true; } } if (showStates) { GUI.enabled = !isPlayMode; EditorGUILayout.PropertyField(states, new GUIContent("States", "The States this Interactable is based on")); GUI.enabled = true; } if (drawerStarted) { InspectorUIUtility.DrawSectionEnd(indentOnSectionStart); } if (states.objectReferenceValue == null) { InspectorUIUtility.DrawError("Please assign a States object!"); EditorGUILayout.EndVertical(); serializedObject.ApplyModifiedProperties(); return; } //standard Interactable Object UI SerializedProperty enabled = serializedObject.FindProperty("Enabled"); EditorGUILayout.PropertyField(enabled, new GUIContent("Enabled", "Is this Interactable Enabled?")); SerializedProperty actionId = serializedObject.FindProperty("InputActionId"); if (actionOptions == null) { GUI.enabled = false; EditorGUILayout.Popup("Input Actions", 0, new string[] { "Missing Mixed Reality Toolkit" }); GUI.enabled = true; } else { position = EditorGUILayout.GetControlRect(); DrawDropDownProperty(position, actionId, actionOptions, new GUIContent("Input Actions", "The input action filter")); } EditorGUI.indentLevel = indentOnSectionStart + 1; SerializedProperty isGlobal = serializedObject.FindProperty("IsGlobal"); EditorGUILayout.PropertyField(isGlobal, new GUIContent("Is Global", "Like a modal, does not require focus")); EditorGUI.indentLevel = indentOnSectionStart; SerializedProperty voiceCommands = serializedObject.FindProperty("VoiceCommand"); // check speech commands profile for a list of commands if (speechKeywords == null) { GUI.enabled = false; EditorGUILayout.Popup("Speech Command", 0, new string[] { "Missing Speech Commands" }); InspectorUIUtility.DrawNotice("Create speech commands in the MRTK/Input/Speech Commands Profile"); GUI.enabled = true; } else { //look for items in the sppech commands list that match the voiceCommands string // this string should be empty if we are not listening to speech commands // will return zero if empty, to match the inserted off value. int currentIndex = SpeechKeywordLookup(voiceCommands.stringValue, speechKeywords); GUI.enabled = !isPlayMode; position = EditorGUILayout.GetControlRect(); GUIContent label = new GUIContent("Speech Command", "Speech Commands to use with Interactable, pulled from MRTK/Input/Speech Commands Profile"); EditorGUI.BeginProperty(position, label, voiceCommands); { currentIndex = EditorGUI.Popup(position, label, currentIndex, speechKeywords); if (currentIndex > 0) { voiceCommands.stringValue = speechKeywords[currentIndex].text; } else { voiceCommands.stringValue = ""; } } EditorGUI.EndProperty(); GUI.enabled = true; } // show requires gaze because voice command has a value if (!string.IsNullOrEmpty(voiceCommands.stringValue)) { EditorGUI.indentLevel = indentOnSectionStart + 1; SerializedProperty requireGaze = serializedObject.FindProperty("RequiresFocus"); EditorGUILayout.PropertyField(requireGaze, new GUIContent("Requires Focus", "Does the voice command require gazing at this interactable?")); EditorGUI.indentLevel = indentOnSectionStart; } SerializedProperty dimensions = serializedObject.FindProperty("Dimensions"); GUI.enabled = !isPlayMode; EditorGUILayout.PropertyField(dimensions, new GUIContent("Dimensions", "Toggle or sequence button levels")); GUI.enabled = true; if (dimensions.intValue > 1) { EditorGUI.indentLevel = indentOnSectionStart + 1; SerializedProperty canSelect = serializedObject.FindProperty("CanSelect"); SerializedProperty canDeselect = serializedObject.FindProperty("CanDeselect"); EditorGUILayout.PropertyField(canSelect, new GUIContent("Can Select", "The user can toggle this button")); EditorGUILayout.PropertyField(canDeselect, new GUIContent("Can Deselect", "The user can untoggle this button, set false for a radial interaction.")); EditorGUI.indentLevel = indentOnSectionStart; } EditorGUILayout.EndVertical(); EditorGUILayout.Space(); InspectorUIUtility.DrawDivider(); if (!ProfilesSetup && !showProfiles) { InspectorUIUtility.DrawWarning("Profiles (Optional) have not been set up or has errors."); } // profiles section string profileTitle = "Profiles"; bool isOPen = InspectorUIUtility.DrawSectionStart(profileTitle, indentOnSectionStart + 1, showProfiles, InspectorUIUtility.LableStyle(InspectorUIUtility.TitleFontSize, InspectorUIUtility.ColorTint50).fontStyle, false, InspectorUIUtility.TitleFontSize); if (showProfiles != isOPen) { showProfiles = isOPen; EditorPrefs.SetBool(prefKey, showProfiles); } if (profileList.arraySize < 1) { AddProfile(0); } int validProfileCnt = 0; int themeCnt = 0; if (showProfiles) { for (int i = 0; i < profileList.arraySize; i++) { EditorGUILayout.BeginVertical("Box"); // get profiles SerializedProperty sItem = profileList.GetArrayElementAtIndex(i); EditorGUI.indentLevel = indentOnSectionStart; SerializedProperty gameObject = sItem.FindPropertyRelative("Target"); string targetName = "Profile " + (i + 1); if (gameObject.objectReferenceValue != null) { targetName = gameObject.objectReferenceValue.name; validProfileCnt++; } EditorGUILayout.BeginHorizontal(); InspectorUIUtility.DrawLabel(targetName, 12, InspectorUIUtility.ColorTint100); bool triggered = InspectorUIUtility.SmallButton(new GUIContent(InspectorUIUtility.Minus, "Remove Profile"), i, RemoveProfile); if (triggered) { continue; } EditorGUILayout.EndHorizontal(); EditorGUI.indentLevel = indentOnSectionStart + 1; EditorGUILayout.PropertyField(gameObject, new GUIContent("Target", "Target gameObject for this theme properties to manipulate")); // get themes SerializedProperty themes = sItem.FindPropertyRelative("Themes"); // make sure there are enough themes as dimensions if (themes.arraySize > dimensions.intValue) { // make sure there are not more themes than dimensions int cnt = themes.arraySize - 1; for (int j = cnt; j > dimensions.intValue - 1; j--) { themes.DeleteArrayElementAtIndex(j); } } // add themes when increasing dimensions if (themes.arraySize < dimensions.intValue) { int cnt = themes.arraySize; for (int j = cnt; j < dimensions.intValue; j++) { themes.InsertArrayElementAtIndex(themes.arraySize); SerializedProperty theme = themes.GetArrayElementAtIndex(themes.arraySize - 1); string[] themeLocations = AssetDatabase.FindAssets("DefaultTheme"); if (themeLocations.Length > 0) { for (int k = 0; k < themeLocations.Length; k++) { string path = AssetDatabase.GUIDToAssetPath(themeLocations[k]); Theme defaultTheme = (Theme)AssetDatabase.LoadAssetAtPath(path, typeof(Theme)); if (defaultTheme != null) { theme.objectReferenceValue = defaultTheme; break; } } } } } for (int t = 0; t < themes.arraySize; t++) { SerializedProperty themeItem = themes.GetArrayElementAtIndex(t); EditorGUI.indentLevel = indentOnSectionStart + 2; EditorGUILayout.PropertyField(themeItem, new GUIContent("Theme", "Theme properties for interaction feedback")); if (themeItem.objectReferenceValue != null && gameObject.objectReferenceValue) { if (themeItem.objectReferenceValue.name == "DefaultTheme") { EditorGUILayout.BeginHorizontal(); InspectorUIUtility.DrawWarning("DefaultTheme should not be edited. "); bool newTheme = InspectorUIUtility.FlexButton(new GUIContent("Create Theme", "Create a new theme"), new int[] { i, t, 0 }, CreateTheme); if (newTheme) { continue; } EditorGUILayout.EndHorizontal(); } SerializedProperty hadDefault = sItem.FindPropertyRelative("HadDefaultTheme"); hadDefault.boolValue = true; EditorGUI.indentLevel = indentOnSectionStart + 3; string prefKey = themeItem.objectReferenceValue.name + "Profiles" + i + "_Theme" + t + "_Edit"; bool hasPref = EditorPrefs.HasKey(prefKey); bool showSettings = EditorPrefs.GetBool(prefKey); if (!hasPref) { showSettings = true; } InspectorUIUtility.ListSettings settings = listSettings[i]; bool show = InspectorUIUtility.DrawSectionStart(themeItem.objectReferenceValue.name + " (Click to edit)", indentOnSectionStart + 3, showSettings, FontStyle.Normal, false); if (show != showSettings) { EditorPrefs.SetBool(prefKey, show); settings.Show = show; } if (show) { SerializedObject themeObj = new SerializedObject(themeItem.objectReferenceValue); SerializedProperty themeObjSettings = themeObj.FindProperty("Settings"); themeObj.Update(); GUILayout.Space(5); if (themeObjSettings.arraySize < 1) { AddThemeProperty(new int[] { i, t, 0 }); } int[] location = new int[] { i, t, 0 }; State[] iStates = GetStates(); ThemeInspector.RenderThemeSettings(themeObjSettings, themeObj, themeOptions, gameObject, location, iStates); InspectorUIUtility.FlexButton(new GUIContent("+", "Add Theme Property"), location, AddThemeProperty); ThemeInspector.RenderThemeStates(themeObjSettings, iStates, 30); themeObj.ApplyModifiedProperties(); } InspectorUIUtility.DrawSectionEnd(indentOnSectionStart + 2); listSettings[i] = settings; validProfileCnt++; } else { // show message about profile setup string themeMsg = "Assign a "; if (gameObject.objectReferenceValue == null) { themeMsg += "Target "; } if (themeItem.objectReferenceValue == null) { if (gameObject.objectReferenceValue == null) { themeMsg += "and "; } themeMsg += "Theme "; } themeMsg += "above to add visual effects"; SerializedProperty hadDefault = sItem.FindPropertyRelative("HadDefaultTheme"); if (!hadDefault.boolValue && t == 0) { string[] themeLocations = AssetDatabase.FindAssets("DefaultTheme"); if (themeLocations.Length > 0) { for (int j = 0; j < themeLocations.Length; j++) { string path = AssetDatabase.GUIDToAssetPath(themeLocations[0]); Theme defaultTheme = (Theme)AssetDatabase.LoadAssetAtPath(path, typeof(Theme)); if (defaultTheme != null) { themeItem.objectReferenceValue = defaultTheme; break; } } if (themeItem.objectReferenceValue != null) { hadDefault.boolValue = true; } } else { InspectorUIUtility.DrawError("DefaultTheme missing from project!"); } } InspectorUIUtility.DrawError(themeMsg); } } EditorGUI.indentLevel = indentOnSectionStart; EditorGUILayout.EndVertical(); themeCnt += themes.arraySize; } if (GUILayout.Button(new GUIContent("Add Profile"))) { AddProfile(profileList.arraySize); } } else { // make sure profiles are setup if closed by default for (int i = 0; i < profileList.arraySize; i++) { SerializedProperty sItem = profileList.GetArrayElementAtIndex(i); SerializedProperty gameObject = sItem.FindPropertyRelative("Target"); SerializedProperty themes = sItem.FindPropertyRelative("Themes"); if (gameObject.objectReferenceValue != null) { validProfileCnt++; } for (int t = 0; t < themes.arraySize; t++) { SerializedProperty themeItem = themes.GetArrayElementAtIndex(themes.arraySize - 1); if (themeItem.objectReferenceValue != null && gameObject.objectReferenceValue) { validProfileCnt++; SerializedProperty hadDefault = sItem.FindPropertyRelative("HadDefaultTheme"); hadDefault.boolValue = true; } } themeCnt += themes.arraySize; } } ProfilesSetup = validProfileCnt == profileList.arraySize + themeCnt; InspectorUIUtility.DrawSectionEnd(indentOnSectionStart); EditorGUILayout.Space(); InspectorUIUtility.DrawDivider(); // Events section InspectorUIUtility.DrawTitle("Events"); SerializedProperty onClick = serializedObject.FindProperty("OnClick"); EditorGUILayout.PropertyField(onClick, new GUIContent("OnClick")); SerializedProperty events = serializedObject.FindProperty("Events"); GUI.enabled = !isPlayMode; for (int i = 0; i < events.arraySize; i++) { SerializedProperty eventItem = events.GetArrayElementAtIndex(i); InteractableReceiverListInspector.RenderEventSettings(eventItem, i, eventOptions, ChangeEvent, RemoveEvent); } GUI.enabled = true; if (eventOptions.ClassNames.Length > 1) { if (GUILayout.Button(new GUIContent("Add Event"))) { AddEvent(events.arraySize); } } serializedObject.ApplyModifiedProperties(); }
public override void OnInspectorGUI() { serializedObject.Update(); InspectorUIUtility.DrawTitle("States"); InspectorUIUtility.DrawNotice("Manage state configurations to drive Interactables or Transitions"); SerializedProperty stateModelClassName = serializedObject.FindProperty("StateModelClassName"); SerializedProperty assemblyQualifiedName = serializedObject.FindProperty("AssemblyQualifiedName"); var stateModelTypes = TypeCacheUtility.GetSubClasses <BaseStateModel>(); var stateModelClassNames = stateModelTypes.Select(t => t?.Name).ToArray(); int id = Array.IndexOf(stateModelClassNames, stateModelClassName.stringValue); int newId = EditorGUILayout.Popup("State Model", id, stateModelClassNames); if (id != newId) { Type newType = stateModelTypes[newId]; stateModelClassName.stringValue = newType.Name; assemblyQualifiedName.stringValue = newType.AssemblyQualifiedName; } for (int i = 0; i < stateList.arraySize; i++) { using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox)) { SerializedProperty stateItem = stateList.GetArrayElementAtIndex(i); SerializedProperty name = stateItem.FindPropertyRelative("Name"); SerializedProperty activeIndex = stateItem.FindPropertyRelative("ActiveIndex"); SerializedProperty bit = stateItem.FindPropertyRelative("Bit"); SerializedProperty index = stateItem.FindPropertyRelative("Index"); // assign the bitcount based on location in the list as power of 2 bit.intValue = 1 << i; activeIndex.intValue = i; using (new EditorGUILayout.HorizontalScope()) { string[] stateEnums = Enum.GetNames(typeof(InteractableStates.InteractableStateEnum)); int enumIndex = Array.IndexOf(stateEnums, name.stringValue); int newEnumIndex = EditorGUILayout.Popup(name.stringValue + " (" + bit.intValue + ")", enumIndex, stateEnums); if (newEnumIndex == -1) { newEnumIndex = 0; } name.stringValue = stateEnums[newEnumIndex]; index.intValue = newEnumIndex; if (InspectorUIUtility.SmallButton(RemoveStateLabel)) { stateList.DeleteArrayElementAtIndex(i); break; } } } } if (InspectorUIUtility.FlexButton(AddStateLabel)) { stateList.InsertArrayElementAtIndex(stateList.arraySize); } serializedObject.ApplyModifiedProperties(); }