using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; using UnityEditor.Experimental.GraphView; using UnityEngine.UIElements; using UnityEditor.UIElements; using System.IO; public class FSMGraphView : GraphView { private Edge selectedCache; public AIGraphWindow instance; public string savePath = "Assets/ScriptableObjects/FSM/"; private Dictionary<Edge,Transition> edgeTransitionTupleList = new Dictionary<Edge, Transition>(); private List<StateNode> statesToRemove = new List<StateNode>(); private List<Edge> edgesToRemove = new List<Edge>(); public FSMGraphView() { AddGridBackground(); AddManipulators(); graphViewChanged = OnGraphViewChange; } private GraphViewChange OnGraphViewChange(GraphViewChange change) { if (change.edgesToCreate != null) { foreach (Edge edge in change.edgesToCreate) { Transition newTransition = ScriptableObject.CreateInstance<Transition>(); StateNode goalStateNode = (StateNode)edge.input.node; StateNode sourceStateNode = (StateNode)edge.output.node; newTransition.name = sourceStateNode.titleInputField.value+goalStateNode.titleInputField.value + "Transition"; newTransition.goalState = goalStateNode.state; edgeTransitionTupleList.Add(edge, newTransition); } } if (change.elementsToRemove != null) { foreach (GraphElement element in change.elementsToRemove) { if (element is Edge) { edgesToRemove.Add((Edge)element); } if (element is StateNode) { // RemoveStateNode((StateNode)element); statesToRemove.Add((StateNode)element); } } } return change; } //runs on build private void RegisterTransitions() { foreach (Port p in ports) { if (p.direction == Direction.Output) { if (p.connected && p.node is StateNode) { List<Transition> transitionsToBeAdded = new List<Transition>(); StateNode sn = (StateNode)p.node; foreach (Edge e in p.connections) { Transition targetTransition = null; if (edgeTransitionTupleList.TryGetValue(e, out targetTransition)) { transitionsToBeAdded.Add(targetTransition); } } sn.state.transitions = transitionsToBeAdded.ToArray(); } } } } public void ClearGraph() { graphElements.ForEach(grapElement => RemoveElement(grapElement)); edgeTransitionTupleList.Clear(); statesToRemove.Clear(); edgesToRemove.Clear(); } public void OnGUI() { } public Transition[] OpenTransitions() { List<Transition> transitions = new List<Transition>(); foreach (ISelectable selected in selection) { if (selected is Edge) { Transition transitionFound; if (edgeTransitionTupleList.TryGetValue((Edge)selected, out transitionFound)) { transitions.Add(transitionFound); //TransitionEditorWindow.Init(transitionFound); selectedCache = (Edge)selected; } } } return transitions.ToArray(); // TransitionEditorWindow.Init(transitions.ToArray()); } private void AddGridBackground() { GridBackground gridBackground = new GridBackground(); gridBackground.StretchToParentSize(); Insert(0, gridBackground); } private void AddManipulators() { this.AddManipulator(CreateNodeContextualMenu()); // this.AddManipulator(LoadContextualMenu()); this.AddManipulator(new SelectionDragger()); this.AddManipulator(new ContentDragger()); this.AddManipulator(new ContentZoomer()); } private IManipulator CreateNodeContextualMenu() { ContextualMenuManipulator contextualMenuManipulator = new ContextualMenuManipulator( menuEvent => menuEvent.menu.AppendAction("Add State", actionEvent => AddElement(CreateStateNode(actionEvent.eventInfo.localMousePosition))) ); return contextualMenuManipulator; } public void Load(FSMSave loadSource) { if (loadSource == null) return; ClearGraph(); if (loadSource.states != null) { foreach (FSMSave.SavedElement s in loadSource.states) { StateNode st = RecreateStateNode(s.position, s.node); AddElement(st); } } foreach (Node nodElement in nodes) { StateNode st = (StateNode)nodElement; st.titleInputField.value = st.state.name; if (st.state.transitions != null) { foreach (Transition transition in st.state.transitions) { if (transition != null) { LoadTrasition(loadSource, st, transition); } } } } } private void LoadTrasition(FSMSave sv,StateNode sourceState,Transition transition) { FSMSave.SavedElement st = sv.states.Find((FSMSave.SavedElement savedState) => { return savedState.node == transition.goalState; }); foreach (Node n in nodes) { StateNode targetStateNode = (StateNode)n; if (targetStateNode.state == st.node) { Edge newEdge = sourceState.outputPort.ConnectTo(targetStateNode.inputPort); AddElement(newEdge); edgeTransitionTupleList.Add(newEdge, transition); } } sourceState.RefreshPorts(); sourceState.RefreshExpandedState(); } public StateNode CreateStateNode(Vector2 pos) { StateNode st = new StateNode(ScriptableObject.CreateInstance<State>()); st.Initialize(pos); st.Draw(); return st; } public StateNode RecreateStateNode(Vector2 pos,State state) { StateNode st = new StateNode(state); st.Initialize(pos); st.Draw(); return st; } public override List<Port> GetCompatiblePorts(Port startPort, NodeAdapter nodeAdapter) { List<Port> compatiblePorts = new List<Port>(); ports.ForEach((Port port) => { if (port == startPort) { return; } if (startPort.node == port.node) { return; } if (startPort.direction == port.direction) { return; } compatiblePorts.Add(port); }); return compatiblePorts; } public void Save() { RegisterTransitions(); ActualSave(instance.saveFile); } private void ActualSave(FSMSave sv) { string path = instance.rootDirectory; foreach (Node currentNode in nodes) { if (currentNode is StateNode) { StateNode stateNode = (StateNode)currentNode; stateNode.state.entryAction = (Action)stateNode.entry.value; Action[] actionArray = new Action[stateNode.actions.Count]; if (stateNode.state != null) { EditorUtility.SetDirty(stateNode.state); } //ObjectField actionsObjectField in primary.actions.ToArray() for (int i = 0; i < actionArray.Length; i++) { actionArray[i] = (Action)stateNode.actions[i].value; } stateNode.state.actions = actionArray; stateNode.state.exitAction = (Action)stateNode.exit.value; //AssetDatabase.CreateAsset(primary.state, string.Empty); if (stateNode.state != null) { /* string combinedPath = System.IO.Path.GetRelativePath(Application.dataPath,System.IO.Path.Combine(path,"States", stateNode.titleInputField.value)); combinedPath = System.IO.Path.Combine("Assets", combinedPath); AssetDatabase.CreateAsset(stateNode.state, combinedPath + ".asset");*/ if (!PathExists(path, "States", stateNode.titleInputField.value + ".asset")) { string finalPath = LocatePathOrCreateFolder(path, "States", stateNode.titleInputField.value); AssetDatabase.CreateAsset(stateNode.state, finalPath); if (instance.saveFile.states != null) { if (!instance.saveFile.states.Exists((FSMSave.SavedElement element) => { return element.node == stateNode.state; })) { instance.saveFile.states.Add(new FSMSave.SavedElement(stateNode.state, stateNode.GetPosition().position)); } } } else { if (instance.saveFile.states != null) { if (!instance.saveFile.states.Exists((FSMSave.SavedElement element) => { return element.node == stateNode.state; })) { instance.saveFile.states.Add(new FSMSave.SavedElement(stateNode.state, stateNode.GetPosition().position)); } else { int index = instance.saveFile.states.FindIndex((FSMSave.SavedElement element) => { return element.node == stateNode.state; }); instance.saveFile.states[index] = new FSMSave.SavedElement(stateNode.state, stateNode.GetPosition().position); } } } } } } foreach (Edge removalEdgeGoal in edgesToRemove) { Transition transitionToCleanup = null; if (edgeTransitionTupleList.TryGetValue(removalEdgeGoal, out transitionToCleanup)) { foreach (StateNode st in nodes) { if (st.state.transitions != null) { RemoveTransitionFromState(st.state, transitionToCleanup); } RemoveTransition(transitionToCleanup); } } } foreach (StateNode stateNodeToRemove in statesToRemove) { State stateToRemove = stateNodeToRemove.state; if (sv.states.Exists((FSMSave.SavedElement stElement) => { return stElement.node == stateToRemove; })) { if (stateToRemove.transitions != null) { foreach (Transition removalTransition in stateToRemove.transitions) { if (removalTransition != null) { RemoveTransition(removalTransition); } } } edgesToRemove.Clear(); AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(stateToRemove)); sv.states.RemoveAll((FSMSave.SavedElement removePredicate) => { return removePredicate.node == stateToRemove; }); } } statesToRemove.Clear(); foreach (Edge e in edges) { Transition transition = null; if (edgeTransitionTupleList.TryGetValue(e, out transition)) { if (!PathExists(path, "Transitions", transition.name + ".asset")) { string finalPath = LocatePathOrCreateFolder(path, "Transitions", transition.name); string assetId = Path.Combine(finalPath, "Transitions"); AssetDatabase.CreateAsset(transition, finalPath); EditorUtility.SetDirty(transition); } else { EditorUtility.SetDirty(transition); } } } EditorUtility.SetDirty(instance.saveFile); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); EditorUtility.FocusProjectWindow(); } private void RemoveTransition(Transition transition) { Edge edgeToClear = FindEdgeByTransition(transition); if (edgeToClear != null) { edgeTransitionTupleList.Remove(edgeToClear); } AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(transition)); } private void RemoveTransitionFromState(State state, Transition targetTransition) { List<Transition> newTransitionList = new List<Transition>(state.transitions); if (newTransitionList.Contains(targetTransition)) { newTransitionList.Remove(targetTransition); } state.transitions = newTransitionList.ToArray(); } private Edge FindEdgeByTransition(Transition transition) { foreach (KeyValuePair<Edge,Transition> kvp in edgeTransitionTupleList) { if (kvp.Value == transition) { return kvp.Key; } } return null; } private string LocatePathOrCreateFolder(string path,string folder,string fileName) { string folderRelativePath = Path.GetRelativePath(Application.dataPath, path); string transitionFolder = string.Empty; if (folder != string.Empty) { if (!Directory.Exists(Path.Combine(path, folder))) { AssetDatabase.CreateFolder(Path.Combine("Assets", folderRelativePath), folder); transitionFolder = Path.Combine("Assets", folderRelativePath, folder); } else { transitionFolder = Path.Combine("Assets", folderRelativePath, folder); } } else { transitionFolder = Path.Combine("Assets", folderRelativePath); } return Path.Combine(transitionFolder, fileName + ".asset"); } private bool PathExists(string path, string folders, string fileName) { return File.Exists(Path.Combine(path,folders,fileName)); } }