public static string GetTransitionInitializers(StateMachineDefinition def) { string str = ""; foreach (var state in def.states) { if (state.transitions != null) { for (int i = 0; i < state.transitions.Count; i++) { var t = state.transitions[i]; var to = def.GetState(t.to); if (to == null) { Debug.LogWarning("Ignoring transition from " + state.name + " to non-existant state " + t.to + "."); } else if (def.IsAncestor(state, to) || def.IsAncestor(to, state)) { Debug.LogWarning("Ignoring transition from " + state.name + " to " + t.to + " because one state is a direct ancestor."); } else { str += GetTransitionInitializer(state, t, i); } } } } return(str); }
public static string GetStateInitializers(StateMachineDefinition def) { string str = " allStates = new State[" + def.states.Count + "];\n\n"; for (int i = 0; i < def.states.Count; i++) { str += GetStateInitializer(def, i); } if (def.defaultState != null && def.defaultState.Length > 0 && def.GetState(def.defaultState) != null) { str += " rootMachine.defaultState = state" + def.defaultState + ";\n"; } else { str += " rootMachine.defaultState = allStates[0];\n"; } for (int i = 0; i < def.states.Count; i++) { str += GetStateParentChildInitializer(def, i); } return(str); }
public static string GetStateParentChildInitializer(StateMachineDefinition def, int index) { string str = ""; var state = def.states[index]; string p = state.parent; if (p != null && p.Length > 0) { var parent = def.GetState(p); if (parent != null && parent.hasChildren) { str += " state" + state.name + ".parent = state" + p + ";\n"; str += " state" + state.name + ".parentMachine = state" + p + ".subMachine;\n"; } else { Debug.LogWarning("State " + state.name + " has non-existant parent " + p + "."); str += " state" + state.name + ".parentMachine = rootMachine;\n"; } } else { str += " state" + state.name + ".parentMachine = rootMachine;\n"; } if (state.hasChildren) { var children = def.GetChildren(state.name); if (children.Count > 0) { StateMachineDefinition.State defState = null; foreach (var child in children) { if (child.name == state.localDefault) { defState = child; break; } } if (defState == null) { defState = children[0]; } str += " state" + state.name + ".subMachine.defaultState = state" + defState.name + ";\n"; } } return(str); }
void OnGUI() { foreach (var obj in Selection.objects) { if (obj is StateMachineDefinition) { def = (StateMachineDefinition)obj; break; } } if (!Application.isPlaying) { ShowSidebar(); } Rect viewportRect = new Rect(0, 0, position.width - sideWidth, position.height); EditorGUI.DrawRect(viewportRect, bgColor); GUI.EndGroup(); Rect clippedArea = new Rect(0, 0, position.width - sideWidth, position.height); clippedArea.size /= zoom; clippedArea.y += editorWindowTabHeight / zoom; GUI.BeginGroup(clippedArea); bool repaint = false; if (def != null) { Event cur = Event.current; Matrix4x4 gm = GUI.matrix; GUI.matrix = Matrix4x4.Scale(new Vector3(zoom, zoom, 1)); UpdateView(ref repaint); if (!Application.isPlaying) { if (op != null) { if (def != op.definition) { op.Cancel(); op = null; } else { op.Update(); if (op.done) { op = null; repaint = true; EditorUtility.SetDirty(def); dirty = true; } } } else { var selected = def.SelectState(ToWorld(cur.mousePosition)); var selectedTr = def.SelectTransition(ToWorld(cur.mousePosition)); if (selectedTr.t1 == null) { if (selected != lastSelectedState) { repaint = true; lastSelectedState = selected; } if (lastSelectedTr != null) { lastSelectedTr = null; repaint = true; } } else { if (selectedTr.t2 != lastSelectedTr) { repaint = true; lastSelectedTr = selectedTr.t2; } if (lastSelectedState != null) { lastSelectedState = null; repaint = true; } } if (cur.type == EventType.MouseDown) { if (cur.button == 0) { if (selectedTr.t1 != null) { editingTransition = selectedTr; editingState = null; repaint = true; } else if (selected != null) { editingState = selected; editingTransition.t1 = null; editingTransition.t2 = null; repaint = true; if (cur.clickCount == 1) { op = new MoveStateOperation(def, this, selected); } else if (cur.clickCount == 2) { op = new RenameStateOperation(def, this, selected); } } else { editingState = null; editingTransition.t1 = null; editingTransition.t2 = null; repaint = true; } } else if (cur.button == 1) { if (selected == null && lastSelectedTr == null) { GenericMenu menu = new GenericMenu(); menu.AddItem(new GUIContent("Create State"), false, () => { var s = def.AddState(); s.position = ToWorld((cur.mousePosition - Vector2.up * editorWindowTabHeight) / zoom); MoveStateOperation.Snap(ref s.position); op = new RenameStateOperation(def, this, s); EditorUtility.SetDirty(def); dirty = true; }); GUI.EndGroup(); menu.ShowAsContext(); GUI.BeginGroup(clippedArea); } else if (selectedTr.t1 != null) { GenericMenu menu = new GenericMenu(); menu.AddItem(new GUIContent("Remove Transition"), false, () => { selectedTr.t1.RemoveTransition(selectedTr.t2); EditorUtility.SetDirty(def); dirty = true; repaint = true; }); GUI.EndGroup(); menu.ShowAsContext(); GUI.BeginGroup(clippedArea); } else if (selected != null) { GenericMenu menu = new GenericMenu(); menu.AddItem(new GUIContent("Delete State"), false, () => { def.RemoveState(selected); EditorUtility.SetDirty(def); dirty = true; repaint = true; }); menu.AddItem(new GUIContent("Rename State"), false, () => { op = new RenameStateOperation(def, this, selected); }); menu.AddItem(new GUIContent("Add Transition"), false, () => { op = new MakeTransitionOperation(def, this, selected); }); StateMachineDefinition.State parent = def.GetState(selected.parent); if (parent == null) { if (selected.name != def.defaultState) { menu.AddItem(new GUIContent("Make Default State"), false, () => { def.defaultState = selected.name; dirty = true; repaint = true; }); } } else { if (selected.name != parent.localDefault) { menu.AddItem(new GUIContent("Make Local Default"), false, () => { parent.localDefault = selected.name; dirty = true; repaint = true; }); } } if (selected.hasChildren) { menu.AddItem(new GUIContent("Remove Sub-Machine"), false, () => { def.RemoveSub(selected, MoveStateOperation.snap * 2); }); menu.AddItem(new GUIContent("Create Child State"), false, () => { var s = def.AddState(); s.position = ToWorld((cur.mousePosition - Vector2.up * editorWindowTabHeight) / zoom); def.SetStateParent(s, selected, MoveStateOperation.snap); MoveStateOperation.Snap(ref s.position); op = new RenameStateOperation(def, this, s); EditorUtility.SetDirty(def); dirty = true; }); } else { menu.AddItem(new GUIContent("Make Sub-Machine"), false, () => { def.CreateSub(selected, MoveStateOperation.snap * 2); }); } GUI.EndGroup(); menu.ShowAsContext(); GUI.BeginGroup(clippedArea); } } } } } if (Event.current.type != EventType.Repaint && (repaint || (op != null && op.repaint))) { Repaint(); } Handles.BeginGUI(); Handles.color = lineColor; Vector2 sz = scroll / zoom; for (float x = -sz.x % MoveStateOperation.snap; x < clippedArea.width; x += MoveStateOperation.snap) { Handles.DrawLine(new Vector3(x, 0), new Vector3(x, clippedArea.height)); } for (float y = -sz.y % MoveStateOperation.snap; y < clippedArea.height; y += MoveStateOperation.snap) { Handles.DrawLine(new Vector3(0, y), new Vector3(clippedArea.width, y)); } if (Application.isPlaying) { if (observing == null || observing.gameObject != Selection.activeGameObject) { if (Selection.activeGameObject) { observing = Selection.activeGameObject.GetComponent <StateMachine>(); } if (observing == null) { observing = (StateMachine)FindObjectOfType(typeof(StateMachine).Assembly.GetType(def.name)); } } } else if (!Application.isPlaying) { observing = null; } Color oldColor = GUI.color; if (def.states != null) { foreach (var state in def.states) { if (op == null || op.state != state || op.showBaseGUI) { string s = state.name; var parent = def.GetState(state.parent); if (parent == null && def.defaultState == s) { s += "\n<default state>"; } else if (parent != null && parent.localDefault == s) { s += "\n<local default>"; } Rect rect = state.rect; rect.position = ToScreen(rect.position); GUI.SetNextControlName("StateButton"); if (observing && cur.type == EventType.Repaint) { if (observing.IsStateActive(state.name)) { GUI.color = new Color(0.5f, 0.7f, 1.0f); } else if (observing.IsStateRemembered(state.name)) { GUI.color = new Color(0.9f, 0.8f, 1.0f); } else { GUI.color = oldColor; } } if (!state.hasChildren) { if (state != editingState) { GUI.Button(rect, s); } else { GUI.Button(rect, ""); var style = new GUIStyle(GUI.skin.label); style.alignment = TextAnchor.MiddleCenter; style.normal.textColor = Color.blue; style.fontStyle = FontStyle.Bold; GUI.Label(rect, s, style); } } else { GUI.Button(rect, ""); var style = new GUIStyle(GUI.skin.label); style.alignment = TextAnchor.UpperCenter; if (state == editingState) { style.normal.textColor = Color.blue; style.fontStyle = FontStyle.Bold; } GUI.Label(rect, s, style); Rect innerRect = rect; innerRect.xMin += 4; innerRect.xMax -= 4; innerRect.yMin += 32; innerRect.yMax -= 4; GUI.color = oldColor; EditorGUI.DrawRect(innerRect, bgColor); Handles.color = lineColor; for (float x = rect.xMin + MoveStateOperation.snap; x < rect.xMax; x += MoveStateOperation.snap) { Handles.DrawLine(new Vector3(x, innerRect.yMin), new Vector3(x, innerRect.yMax)); } for (float y = rect.yMin + MoveStateOperation.snap * 2; y < rect.yMax; y += MoveStateOperation.snap) { Handles.DrawLine(new Vector3(innerRect.xMin, y), new Vector3(innerRect.xMax, y)); } } } if (op != null && op.state == state) { op.OnGUI(); } } foreach (var from in def.states) { if (from.transitions != null) { foreach (var tr in from.transitions) { if (Application.isPlaying) { Handles.color = Color.black; if (observing) { float t = Time.unscaledTime - observing.TransitionLastTime(from.name, tr.to); if (t < 0.5f) { Handles.color = Color.Lerp(Color.black, Color.green, 1.0f - t * 2); } } } else { if (tr != lastSelectedTr && tr != editingTransition.t2) { Handles.color = Color.black; } else { Handles.color = Color.blue; } } if (def.GetState(tr.to) != null) { var line = def.GetTransitionPoints(from, tr); Vector2 src = ToScreen(line.t1); Vector2 dest = ToScreen(line.t2); Vector2 v = (dest - src).normalized; Vector2 ortho = new Vector2(v.y, -v.x); Vector2 arrow = ortho - v; Vector2 mid = (src + dest) / 2; Handles.DrawAAPolyLine(3, src, dest); Handles.DrawAAPolyLine(3, mid + v * 5, mid + arrow * 6); } } } } } Handles.EndGUI(); GUI.matrix = gm; } else if (op != null) { op.Cancel(); op = null; } }
void OnGUI() { ResizeScrollView(); Rect viewportRect = new Rect(0, 0, position.width - sidePanelWidth, position.height); EditorGUI.DrawRect(viewportRect, bgColor); bool repaint = false; foreach (var obj in Selection.objects) { if (obj is StateMachineDefinition) { def = (StateMachineDefinition)obj; break; } } if (def != null) { bool showSide = true; Event cur = Event.current; if (op != null) { if (def != op.definition) { op.Cancel(); op = null; } else { op.Update(); if (op.done) { op = null; repaint = true; EditorUtility.SetDirty(def); dirty = true; } } } else { var selected = def.SelectState(cur.mousePosition + scrollPos); var selectedTr = def.SelectTransition(cur.mousePosition + scrollPos); if (selected == null) { if (selectedTr.t2 != lastSelectedTr) { repaint = true; lastSelectedTr = selectedTr.t2; } if (lastSelectedState != null) { lastSelectedState = null; repaint = true; } } else { if (selected != lastSelectedState) { repaint = true; lastSelectedState = selected; } if (lastSelectedTr != null) { lastSelectedTr = null; repaint = true; } } if (viewportRect.Contains(cur.mousePosition)) { if (cur.type == EventType.MouseDown) { if (cur.button == 0) { if (selected != null) { editingState = selected; editingTransition.t1 = null; editingTransition.t2 = null; repaint = true; showSide = false; if (cur.clickCount == 1) { op = new MoveStateOperation(def, this, selected); } else if (cur.clickCount == 2) { op = new RenameStateOperation(def, this, selected); } } else if (selectedTr.t1 != null) { editingTransition = selectedTr; editingState = null; repaint = true; showSide = false; } else { editingState = null; editingTransition.t1 = null; editingTransition.t2 = null; repaint = true; showSide = false; } } else if (cur.button == 1) { if (selected == null && lastSelectedTr == null) { GenericMenu menu = new GenericMenu(); menu.AddItem(new GUIContent("Create State"), false, () => { var s = def.AddState(); s.position = cur.mousePosition + scrollPos; MoveStateOperation.Snap(ref s.position); op = new RenameStateOperation(def, this, s); EditorUtility.SetDirty(def); dirty = true; }); menu.ShowAsContext(); } else if (selected != null) { GenericMenu menu = new GenericMenu(); menu.AddItem(new GUIContent("Delete State"), false, () => { def.RemoveState(selected); EditorUtility.SetDirty(def); dirty = true; repaint = true; }); menu.AddItem(new GUIContent("Add Transition"), false, () => { op = new MakeTransitionOperation(def, this, selected); }); if (selected.name != def.defaultState) { menu.AddItem(new GUIContent("Make Default State"), false, () => { def.defaultState = selected.name; dirty = true; repaint = true; }); } menu.ShowAsContext(); } else if (selectedTr.t1 != null) { GenericMenu menu = new GenericMenu(); menu.AddItem(new GUIContent("Remove Transition"), false, () => { selectedTr.t1.RemoveTransition(selectedTr.t2); EditorUtility.SetDirty(def); dirty = true; repaint = true; }); menu.ShowAsContext(); } } } else if (cur.type == EventType.MouseDrag && cur.button == 2) { scrollPos -= cur.delta; repaint = true; } else if (cur.type == EventType.KeyDown && cur.keyCode == KeyCode.F) { var state = def.GetState(def.defaultState); if (state == null && def.states.Count > 0) { state = def.states[0]; } if (state != null) { scrollPos = state.position - viewportRect.size / 2; repaint = true; } } } } if (Event.current.type != EventType.Repaint && (repaint || (op != null && op.repaint))) { Repaint(); } Handles.BeginGUI(); Handles.color = lineColor; for (float x = -scrollPos.x % MoveStateOperation.snap; x < viewportRect.width; x += MoveStateOperation.snap) { Handles.DrawLine(new Vector3(x, 0), new Vector3(x, viewportRect.height)); } for (float y = -scrollPos.y % MoveStateOperation.snap; y < viewportRect.height; y += MoveStateOperation.snap) { Handles.DrawLine(new Vector3(0, y), new Vector3(viewportRect.width, y)); } if (def.states != null) { foreach (var from in def.states) { if (from.transitions != null) { foreach (var tr in from.transitions) { if (tr != lastSelectedTr && tr != editingTransition.t2) { Handles.color = Color.black; } else { Handles.color = Color.blue; } if (def.GetState(tr.to) != null) { var line = def.GetTransitionPoints(from, tr); Vector2 src = line.t1 - scrollPos; Vector2 dest = line.t2 - scrollPos; Vector2 v = (dest - src).normalized; Vector2 ortho = new Vector2(v.y, -v.x); Vector2 arrow = ortho - v; Vector2 mid = (src + dest) / 2; Handles.DrawAAPolyLine(3, src, dest); Handles.DrawAAPolyLine(3, mid + v * 5, mid + arrow * 10); } } } } foreach (var state in def.states) { if (op == null || op.state != state || op.showBaseGUI) { string s = state.name; if (def.defaultState == s) { s += "\n<default state>"; } Rect rect = state.rect; rect.position -= scrollPos; if (state != lastSelectedState && state != editingState) { GUI.Button(rect, s); } else { GUI.Button(rect, ""); var centeredStyle = new GUIStyle(GUI.skin.label); centeredStyle.alignment = TextAnchor.MiddleCenter; centeredStyle.normal.textColor = Color.blue; centeredStyle.fontStyle = FontStyle.Bold; GUI.Label(rect, s, centeredStyle); } } if (op != null && op.state == state) { op.OnGUI(); } } } Handles.EndGUI(); if (showSide) { EditorGUI.DrawRect(new Rect(position.width - sidePanelWidth, 0, sidePanelWidth, position.height), panelColor); float padding = 20; GUILayout.BeginArea(new Rect(position.width - sidePanelWidth + padding, padding, sidePanelWidth - padding * 2, position.height - padding * 2)); EditorGUILayout.BeginVertical(); float w = EditorGUIUtility.labelWidth; EditorGUIUtility.labelWidth = 1; if (GUILayout.Button(dirty ? "Save *" : "Save")) { AssetDatabase.SaveAssets(); dirty = false; StateMachineClassGenerator.GenerateAbstractClass(def); } if (GUILayout.Button("New Impl Class...")) { var path = EditorUtility.SaveFilePanelInProject("Save new class", def.name + "Impl.cs", "cs", "Enter a name for the new impl class"); if (path.Length > 0) { StateMachineClassGenerator.GenerateImplClass(def, path); } } if (types == null) { types = typeof(StateMachine).Assembly.GetTypes() .Where(p => !p.IsGenericType && typeof(StateMachine).IsAssignableFrom(p)) .Select(t => t.FullName).ToArray(); } int index = Array.IndexOf(types, def.baseClass); if (index < 0) { index = Array.IndexOf(types, typeof(StateMachine).FullName); } int prev = index; EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Base Class"); index = EditorGUILayout.Popup(index, types); if (prev != index) { dirty = true; } EditorGUILayout.EndHorizontal(); def.baseClass = types[index]; if (editingState != null) { EditorGUILayout.LabelField("State " + editingState.name); EditorGUILayout.LabelField("State Events", EditorStyles.boldLabel); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Enter: "); bool enter = EditorGUILayout.Toggle(editingState.hasEnter); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("During: "); bool during = EditorGUILayout.Toggle(editingState.hasDuring); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Exit: "); bool exit = EditorGUILayout.Toggle(editingState.hasExit); EditorGUILayout.EndHorizontal(); if (enter != editingState.hasEnter || during != editingState.hasDuring || exit != editingState.hasExit) { editingState.hasEnter = enter; editingState.hasDuring = during; editingState.hasExit = exit; dirty = true; EditorUtility.SetDirty(def); } } else if (editingTransition.t1 != null) { EditorGUILayout.LabelField("Transition From " + editingTransition.t1.name + " To " + editingTransition.t2.to); EditorGUILayout.LabelField("Transition Events", EditorStyles.boldLabel); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Notify: "); bool notify = EditorGUILayout.Toggle(editingTransition.t2.hasNotify); EditorGUILayout.EndHorizontal(); if (notify != editingTransition.t2.hasNotify) { editingTransition.t2.hasNotify = notify; dirty = true; EditorUtility.SetDirty(def); } } EditorGUILayout.EndVertical(); EditorGUIUtility.labelWidth = w; GUILayout.EndArea(); } } else if (op != null) { op.Cancel(); op = null; } }