Esempio n. 1
0
        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;
            }
        }