/// <summary> /// draws a button showing if a state is on or off, allowing /// the user to toggle states at runtime. will also show /// a text saying if the state is currently disallowed /// </summary> public static void RunTimeStateField(vp_AIComponent component, vp_AIState state, List <vp_AIState> stateList) { EditorGUILayout.BeginHorizontal(); GUI.color = m_ColorTransparentWhite; if (!state.Enabled) { GUILayout.Space(20); GUI.enabled = true; GUILayout.Label((stateList.Count - stateList.IndexOf(state) - 1).ToString() + ":", vp_EditorGUIUtility.RightAlignedPathStyle, GUILayout.MinWidth(20), GUILayout.MaxWidth(20)); GUILayout.BeginHorizontal(); if (GUILayout.Button(state.Name, vp_EditorGUIUtility.CenteredBoxStyle, GUILayout.MinWidth(90), GUILayout.MaxWidth(90))) { vp_AIComponent[] compos = component.gameObject.GetComponentsInChildren <vp_AIComponent>(); foreach (vp_AIComponent c in compos) { c.StateManager.SetState(state.Name, true); c.Refresh(); } } GUILayout.EndHorizontal(); GUI.color = m_ColorTransparentWhite; } else { GUILayout.Space(20); GUILayout.Label((stateList.Count - stateList.IndexOf(state) - 1).ToString() + ":", vp_EditorGUIUtility.RightAlignedPathStyle, GUILayout.MinWidth(20), GUILayout.MaxWidth(20)); if (GUILayout.Button(state.Name, ((!state.Blocked) ? vp_EditorGUIUtility.CenteredBoxStyleBold : vp_EditorGUIUtility.CenteredStyleBold) , GUILayout.MinWidth(90), GUILayout.MaxWidth(90))) { vp_AIComponent[] compos = component.gameObject.GetComponentsInChildren <vp_AIComponent>(); foreach (vp_AIComponent c in compos) { c.StateManager.SetState(state.Name, false); c.Refresh(); } } } if (state.Name != "Default") { if (state.Blocked) { GUILayout.TextField("(Blocked on " + component.GetType().ToString() + "(" + state.BlockCount + "))", vp_EditorGUIUtility.NoteStyle, GUILayout.MinWidth(100)); } if ((state.TextAsset == null) && (!state.Blocked)) { GUILayout.TextField("(No preset)", vp_EditorGUIUtility.NoteStyle, GUILayout.MinWidth(100)); } } EditorGUILayout.EndHorizontal(); }
/// <summary> /// backs up the fields of the component. this should be done /// every time the component is modified in the inspector. /// </summary> public void Persist() { if (!IsActive) { return; } if (Component == null) { Debug.LogError("Error! vp_ComponentPersister: Component is null."); return; } PlayModePersistFields.Clear(); foreach (FieldInfo f in Component.GetType().GetFields()) { PlayModePersistFields.Add(f.GetValue(Component)); //Debug.Log("(" + f.FieldType.Name + ") " + f.Name + " = " + f.GetValue(Component)); } }
private int m_TargetId = 0; // id of the last modified state (for triggering enable / disable callbacks) /// <summary> /// manages a list of states and corresponding presets for a /// component. loads the presets into memory on startup, and /// allows applying them from memory to the component. /// states are enabled in a layered manner, that is: the /// default state is always alive, and any enabled states are /// applied on top of it in the order of which they were enabled. /// /// this class doesn't store any preset data between sessions. /// it is merely used to manipulate the component using a list /// of states that is sent along at startup. it is very silent /// and forgiving; it won't complain if a state isn't found /// (since providing a certain state should not be considered /// mandatory for a component, and states can be set recursively). /// it will also ignore empty presets /// </summary> public vp_AIStateManager(vp_AIComponent component, List <vp_AIState> states) { m_States = states; m_Component = component; // create default state and add it to the list m_Component.RefreshDefaultState(); // refresh the initial state, needed for being able to save // partial presets in the editor #if UNITY_EDITOR m_Component.RefreshInitialState(); #endif // load states and initialize the state id dictionary m_StateIds = new Dictionary <string, int>(System.StringComparer.CurrentCulture); foreach (vp_AIState s in m_States) { s.StateManager = this; // store the name and list index of each state in a dictionary for // fast lookup. IMPORTANT: the state list (m_States) must never be // modified at runtime (i.e. have states reordered, added, renamed // or removed) or the dictionary (m_StateIds) will be out of date if (!m_StateIds.ContainsKey(s.Name)) { m_StateIds.Add(s.Name, m_States.IndexOf(s)); } else { Debug.LogWarning("Warning: " + m_Component.GetType() + " on '" + m_Component.name + "' has more than one state named: '" + s.Name + "'. Only the topmost one will be used."); m_States[m_DefaultId].StatesToBlock.Add(m_States.IndexOf(s)); } // load up the preset of each user assigned state and if (s.Preset == null) { s.Preset = new vp_AIComponentPreset(); } if (s.TextAsset != null) { s.Preset.LoadFromTextAsset(s.TextAsset); } } // the default state of a component is always the last one in the list m_DefaultId = m_States.Count - 1; }
/// <summary> /// /// </summary> static void StateBlockList(vp_AIComponent component, vp_AIState blocker) { GUILayout.BeginHorizontal(); GUILayout.Space(20); GUILayout.BeginVertical(); string componentName = component.GetType().ToString(); if (componentName.Contains("vp_")) { componentName = componentName.Substring(3); } EditorGUILayout.HelpBox("'" + blocker.Name + "' blocks " + ((blocker.StatesToBlock.Count > 0) ? blocker.StatesToBlock.Count.ToString() : "no") + " state" + ((blocker.StatesToBlock.Count == 1) ? "" : "s") + " on this " + componentName + ".", MessageType.None); GUILayout.BeginVertical(); int e = 0; foreach (vp_AIState blockee in component.States) { if (blockee == blocker) { continue; } if (blockee.Name == "Default") { continue; } int i = component.States.IndexOf(blockee); if (component.States[i].StatesToBlock.Contains(component.States.IndexOf(blocker))) { GUI.enabled = false; } if (e % 2 == 0) { GUILayout.BeginHorizontal(); } GUILayout.Space(20); bool before = blocker.StatesToBlock.Contains(i); bool after = before; after = GUILayout.Toggle(after, blockee.Name); if (before != after) { if (!before) { blocker.StatesToBlock.Add(i); } else { blocker.StatesToBlock.Remove(i); } EditorUtility.SetDirty(component); } if (e % 2 == 1) { GUILayout.Space(10); GUILayout.EndHorizontal(); } e++; GUI.enabled = true; } if (e % 2 == 1) { GUILayout.EndHorizontal(); } GUILayout.EndVertical(); GUILayout.BeginHorizontal(); EditorGUILayout.HelpBox("Select states to be disallowed on this " + componentName + " while the '" + blocker.Name + "' state is enabled. A state can not block itself, a state that blocks it or the Default state.", MessageType.Info); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); if (GUILayout.Button("Close", GUILayout.MinWidth(100), GUILayout.MaxWidth(50))) { m_ShowBlockListFor = null; EditorUtility.SetDirty(component); } GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.Space(10); GUILayout.EndHorizontal(); GUILayout.Space(10); }
/// <summary> /// draws a field allowing the user to create, reorganize, /// name, assign presets to and delete states on a component /// </summary> public static bool StateFoldout(bool foldout, vp_AIComponent component, List <vp_AIState> stateList, vp_AIComponentPersister persister = null) { bool before = foldout; foldout = EditorGUILayout.Foldout(foldout, (foldout && !Application.isPlaying) ? "State Preset" : "States" ); if (foldout != before) { m_ShowBlockListFor = null; component.RefreshDefaultState(); } if (foldout) { if (m_ShowBlockListFor != null) { if (!stateList.Contains(m_ShowBlockListFor)) { foldout = false; } } } if (foldout) { for (int v = 0; v < stateList.Count; v++) { vp_AIState s = stateList[v]; if (!Application.isPlaying) { vp_AIPresetEditorGUIUtility.StateField(s, stateList, component); if ((m_ShowBlockListFor != null) && m_ShowBlockListFor == s) { StateBlockList(component, s); } } else { vp_AIPresetEditorGUIUtility.RunTimeStateField(component, s, stateList); } } GUILayout.BeginHorizontal(); if (!Application.isPlaying) { if (GUILayout.Button("Add State", GUILayout.MinWidth(90), GUILayout.MaxWidth(90))) { m_ShowBlockListFor = null; string newStateName = "Untitled"; int n = 1; while (GetStateId(component, newStateName) != -1) { n++; newStateName = newStateName.Substring(0, 8) + (n < 10?"0":"") + n.ToString(); } stateList.Add(new vp_AIState(component.GetType().Name, newStateName, "")); component.RefreshDefaultState(); EditorUtility.SetDirty(component); } } else { GUI.color = Color.clear; GUILayout.Button("", GUILayout.MinWidth(36), GUILayout.MaxWidth(36)); GUI.color = Color.white; } if (!Application.isPlaying) { GUILayout.EndHorizontal(); } if (persister != null) { vp_AIPresetEditorGUIUtility.PersistToggle(persister); } if (Application.isPlaying) { GUILayout.EndHorizontal(); } vp_EditorGUIUtility.Separator(); } return(foldout); }
/// <summary> /// opens a dialog for loading presets /// </summary> static private void ShowLoadDialog(vp_AIComponent component) { string path = Application.dataPath.Replace("\\", "/"); vp_FileDialog.Create(vp_FileDialog.Mode.Open, "Load Preset", path, delegate(string filename) { if (!vp_AIComponentPreset.Load(component, filename)) { vp_FileDialog.Result = "Failed to load preset '" + vp_FileDialog.ExtractFilenameFromPath(filename) + "'.\n\nIs it the correct component type? (" + component.GetType().ToString() + ")"; } else { EditorUtility.SetDirty(component); } }, ".txt"); }