public static void GenerateImplClass(StateMachineDefinition def, string path) { string className = Path.GetFileNameWithoutExtension(path); string generated = string.Format(implClassTemplate, className, def.name, GetFunctionDeclarations(def, true)); StreamWriter outStream = new StreamWriter(path); outStream.Write(generated); outStream.Close(); AssetDatabase.Refresh(); }
public static string GetTransitionInitializers(StateMachineDefinition def) { string str = ""; foreach (var state in def.states) { for (int i = 0; i < state.transitions.Count; i++) { str += GetTransitionInitializer(state, state.transitions[i], i); } } return(str); }
public override void OnInspectorGUI() { StateMachineDefinition myTarget = (StateMachineDefinition)target; if (GUILayout.Button("Open State Machine Editor")) { StateMachineEditorWindow.def = myTarget; StateMachineEditorWindow.ShowWindow(); } serializedObject.Update(); DrawPropertiesExcluding(serializedObject, "baseClass"); serializedObject.ApplyModifiedProperties(); }
private static string GetStateEnums(StateMachineDefinition def) { string str = ""; for (int i = 0; i < def.states.Count; i++) { str += def.states[i].name; if (i < def.states.Count - 1) { str += ", "; } } return(str); }
public static void GenerateAbstractClass(StateMachineDefinition def) { string generated = string.Format(abstractClassTemplate, def.name, GetStateEnums(def), GetStateInitializers(def), GetTransitionInitializers(def), GetFunctionDeclarations(def), def.baseClass); string defPath = AssetDatabase.GetAssetPath(def); if (defPath.Length > 0) { string newPath = defPath.Substring(0, defPath.LastIndexOf(".")) + ".cs"; StreamWriter outStream = new StreamWriter(newPath); outStream.Write(generated); outStream.Close(); AssetDatabase.Refresh(); } }
public static string GetStateInitializers(StateMachineDefinition def) { string str = " states = new State[" + def.states.Count + "];\n\n"; for (int i = 0; i < def.states.Count; i++) { str += GetStateInitializer(def.states[i], i); } if (def.defaultState.Length > 0) { str += " currentState = state" + def.defaultState + ";\n"; } else { str += " currentState = states[0];\n"; } return(str); }
public static string GetFunctionDeclarations(StateMachineDefinition def, bool over = false) { string vo = over ? "override" : "virtual"; string str = ""; foreach (var state in def.states) { if (state.hasEnter) { str += " protected " + vo + " void StateEnter_" + state.name + "() { }\n"; } if (state.hasDuring) { str += " protected " + vo + " void State_" + state.name + "() { }\n"; } if (state.hasExit) { str += " protected " + vo + " void StateExit_" + state.name + "() { }\n"; } } str += "\n"; foreach (var state in def.states) { foreach (var trans in state.transitions) { str += " protected " + vo + " bool TransitionCond_" + state.name + "_" + trans.to + "() { return false; }\n"; if (trans.hasNotify) { str += " protected " + vo + " void TransitionNotify_" + state.name + "_" + trans.to + "() { }\n"; } } } return(str); }
public Operation(StateMachineDefinition definition, StateMachineDefinition.State state) { this.definition = definition; this.state = state; }
public RenameStateOperation(StateMachineDefinition def, StateMachineDefinition.State state) : base(def, state) { name = state.name; showBaseGUI = false; }
public MakeTransitionOperation(StateMachineDefinition def, StateMachineDefinition.State state) : base(def, state) { showBaseGUI = true; }
public MoveStateOperation(StateMachineDefinition def, StateMachineDefinition.State state) : base(def, state) { showBaseGUI = true; start = state.position; move = Vector2.zero; }
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, selected); } else if (cur.clickCount == 2) { op = new RenameStateOperation(def, 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; op = new RenameStateOperation(def, 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, 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)); } 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); } } } } Handles.EndGUI(); 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(); } } 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; } }