Newer
Older
Dreamsturbia-Project-IADE-Unity3D / Assets / Editor / FSMGraphView.cs
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEditor;
  5. using UnityEditor.Experimental.GraphView;
  6. using UnityEngine.UIElements;
  7. using UnityEditor.UIElements;
  8. using System.IO;
  9. public class FSMGraphView : GraphView
  10. {
  11. private Edge selectedCache;
  12. public AIGraphWindow instance;
  13. public string savePath = "Assets/ScriptableObjects/FSM/";
  14. private Dictionary<Edge,Transition> edgeTransitionTupleList = new Dictionary<Edge, Transition>();
  15. private List<StateNode> statesToRemove = new List<StateNode>();
  16. private List<Edge> edgesToRemove = new List<Edge>();
  17. public FSMGraphView()
  18. {
  19. AddGridBackground();
  20. AddManipulators();
  21. graphViewChanged = OnGraphViewChange;
  22. }
  23. private GraphViewChange OnGraphViewChange(GraphViewChange change)
  24. {
  25. if (change.edgesToCreate != null)
  26. {
  27. foreach (Edge edge in change.edgesToCreate)
  28. {
  29. Transition newTransition = ScriptableObject.CreateInstance<Transition>();
  30. StateNode goalStateNode = (StateNode)edge.input.node;
  31. StateNode sourceStateNode = (StateNode)edge.output.node;
  32. newTransition.name = sourceStateNode.titleInputField.value+goalStateNode.titleInputField.value + "Transition";
  33. newTransition.goalState = goalStateNode.state;
  34. edgeTransitionTupleList.Add(edge, newTransition);
  35. }
  36. }
  37. if (change.elementsToRemove != null)
  38. {
  39. foreach (GraphElement element in change.elementsToRemove)
  40. {
  41. if (element is Edge)
  42. {
  43. edgesToRemove.Add((Edge)element);
  44. }
  45. if (element is StateNode)
  46. {
  47. // RemoveStateNode((StateNode)element);
  48. statesToRemove.Add((StateNode)element);
  49. }
  50. }
  51. }
  52. return change;
  53. }
  54. //runs on build
  55. private void RegisterTransitions()
  56. {
  57. foreach (Port p in ports)
  58. {
  59. if (p.direction == Direction.Output)
  60. {
  61. if (p.connected && p.node is StateNode)
  62. {
  63. List<Transition> transitionsToBeAdded = new List<Transition>();
  64. StateNode sn = (StateNode)p.node;
  65. foreach (Edge e in p.connections)
  66. {
  67. Transition targetTransition = null;
  68. if (edgeTransitionTupleList.TryGetValue(e, out targetTransition))
  69. {
  70. transitionsToBeAdded.Add(targetTransition);
  71. }
  72. }
  73. sn.state.transitions = transitionsToBeAdded.ToArray();
  74. }
  75. }
  76. }
  77. }
  78. public void ClearGraph()
  79. {
  80. graphElements.ForEach(grapElement => RemoveElement(grapElement));
  81. edgeTransitionTupleList.Clear();
  82. statesToRemove.Clear();
  83. edgesToRemove.Clear();
  84. }
  85. public void OnGUI()
  86. {
  87. }
  88. public Transition[] OpenTransitions()
  89. {
  90. List<Transition> transitions = new List<Transition>();
  91. foreach (ISelectable selected in selection)
  92. {
  93. if (selected is Edge)
  94. {
  95. Transition transitionFound;
  96. if (edgeTransitionTupleList.TryGetValue((Edge)selected, out transitionFound))
  97. {
  98. transitions.Add(transitionFound);
  99. //TransitionEditorWindow.Init(transitionFound);
  100. selectedCache = (Edge)selected;
  101. }
  102. }
  103. }
  104. return transitions.ToArray();
  105. // TransitionEditorWindow.Init(transitions.ToArray());
  106. }
  107. private void AddGridBackground()
  108. {
  109. GridBackground gridBackground = new GridBackground();
  110. gridBackground.StretchToParentSize();
  111. Insert(0, gridBackground);
  112. }
  113. private void AddManipulators()
  114. {
  115. this.AddManipulator(CreateNodeContextualMenu());
  116. // this.AddManipulator(LoadContextualMenu());
  117. this.AddManipulator(new SelectionDragger());
  118. this.AddManipulator(new ContentDragger());
  119. this.AddManipulator(new ContentZoomer());
  120. }
  121. private IManipulator CreateNodeContextualMenu()
  122. {
  123. ContextualMenuManipulator contextualMenuManipulator = new ContextualMenuManipulator(
  124. menuEvent => menuEvent.menu.AppendAction("Add State", actionEvent => AddElement(CreateStateNode(actionEvent.eventInfo.localMousePosition)))
  125. );
  126. return contextualMenuManipulator;
  127. }
  128. public void Load(FSMSave loadSource)
  129. {
  130. if (loadSource == null) return;
  131. ClearGraph();
  132. if (loadSource.states != null)
  133. {
  134. foreach (FSMSave.SavedElement s in loadSource.states)
  135. {
  136. StateNode st = RecreateStateNode(s.position, s.node);
  137. AddElement(st);
  138. }
  139. }
  140. foreach (Node nodElement in nodes)
  141. {
  142. StateNode st = (StateNode)nodElement;
  143. st.titleInputField.value = st.state.name;
  144. if (st.state.transitions != null)
  145. {
  146. foreach (Transition transition in st.state.transitions)
  147. {
  148. if (transition != null)
  149. {
  150. LoadTrasition(loadSource, st, transition);
  151. }
  152. }
  153. }
  154. }
  155. }
  156. private void LoadTrasition(FSMSave sv,StateNode sourceState,Transition transition)
  157. {
  158. FSMSave.SavedElement st = sv.states.Find((FSMSave.SavedElement savedState) => {
  159. return savedState.node == transition.goalState;
  160. });
  161. foreach (Node n in nodes)
  162. {
  163. StateNode targetStateNode = (StateNode)n;
  164. if (targetStateNode.state == st.node)
  165. {
  166. Edge newEdge = sourceState.outputPort.ConnectTo(targetStateNode.inputPort);
  167. AddElement(newEdge);
  168. edgeTransitionTupleList.Add(newEdge, transition);
  169. }
  170. }
  171. sourceState.RefreshPorts();
  172. sourceState.RefreshExpandedState();
  173. }
  174. public StateNode CreateStateNode(Vector2 pos)
  175. {
  176. StateNode st = new StateNode(ScriptableObject.CreateInstance<State>());
  177. st.Initialize(pos);
  178. st.Draw();
  179. return st;
  180. }
  181. public StateNode RecreateStateNode(Vector2 pos,State state)
  182. {
  183. StateNode st = new StateNode(state);
  184. st.Initialize(pos);
  185. st.Draw();
  186. return st;
  187. }
  188. public override List<Port> GetCompatiblePorts(Port startPort, NodeAdapter nodeAdapter)
  189. {
  190. List<Port> compatiblePorts = new List<Port>();
  191. ports.ForEach((Port port) => {
  192. if (port == startPort)
  193. {
  194. return;
  195. }
  196. if (startPort.node == port.node)
  197. {
  198. return;
  199. }
  200. if (startPort.direction == port.direction)
  201. {
  202. return;
  203. }
  204. compatiblePorts.Add(port);
  205. });
  206. return compatiblePorts;
  207. }
  208. public void Save()
  209. {
  210. RegisterTransitions();
  211. ActualSave(instance.saveFile);
  212. }
  213. private void ActualSave(FSMSave sv)
  214. {
  215. string path = instance.rootDirectory;
  216. foreach (Node currentNode in nodes)
  217. {
  218. if (currentNode is StateNode)
  219. {
  220. StateNode stateNode = (StateNode)currentNode;
  221. stateNode.state.entryAction = (Action)stateNode.entry.value;
  222. Action[] actionArray = new Action[stateNode.actions.Count];
  223. if (stateNode.state != null)
  224. {
  225. EditorUtility.SetDirty(stateNode.state);
  226. }
  227. //ObjectField actionsObjectField in primary.actions.ToArray()
  228. for (int i = 0; i < actionArray.Length; i++)
  229. {
  230. actionArray[i] = (Action)stateNode.actions[i].value;
  231. }
  232. stateNode.state.actions = actionArray;
  233. stateNode.state.exitAction = (Action)stateNode.exit.value;
  234. //AssetDatabase.CreateAsset(primary.state, string.Empty);
  235. if (stateNode.state != null)
  236. {
  237. /* string combinedPath = System.IO.Path.GetRelativePath(Application.dataPath,System.IO.Path.Combine(path,"States", stateNode.titleInputField.value));
  238. combinedPath = System.IO.Path.Combine("Assets", combinedPath);
  239. AssetDatabase.CreateAsset(stateNode.state, combinedPath + ".asset");*/
  240. if (!PathExists(path, "States", stateNode.titleInputField.value + ".asset"))
  241. {
  242. string finalPath = LocatePathOrCreateFolder(path, "States", stateNode.titleInputField.value);
  243. AssetDatabase.CreateAsset(stateNode.state, finalPath);
  244. if (instance.saveFile.states != null)
  245. {
  246. if (!instance.saveFile.states.Exists((FSMSave.SavedElement element) => { return element.node == stateNode.state; }))
  247. {
  248. instance.saveFile.states.Add(new FSMSave.SavedElement(stateNode.state, stateNode.GetPosition().position));
  249. }
  250. }
  251. } else
  252. {
  253. if (instance.saveFile.states != null)
  254. {
  255. if (!instance.saveFile.states.Exists((FSMSave.SavedElement element) => { return element.node == stateNode.state; }))
  256. {
  257. instance.saveFile.states.Add(new FSMSave.SavedElement(stateNode.state, stateNode.GetPosition().position));
  258. }
  259. else
  260. {
  261. int index = instance.saveFile.states.FindIndex((FSMSave.SavedElement element) => { return element.node == stateNode.state; });
  262. instance.saveFile.states[index] = new FSMSave.SavedElement(stateNode.state, stateNode.GetPosition().position);
  263. }
  264. }
  265. }
  266. }
  267. }
  268. }
  269. foreach (Edge removalEdgeGoal in edgesToRemove)
  270. {
  271. Transition transitionToCleanup = null;
  272. if (edgeTransitionTupleList.TryGetValue(removalEdgeGoal, out transitionToCleanup))
  273. {
  274. foreach (StateNode st in nodes)
  275. {
  276. if (st.state.transitions != null)
  277. {
  278. RemoveTransitionFromState(st.state, transitionToCleanup);
  279. }
  280. RemoveTransition(transitionToCleanup);
  281. }
  282. }
  283. }
  284. foreach (StateNode stateNodeToRemove in statesToRemove)
  285. {
  286. State stateToRemove = stateNodeToRemove.state;
  287. if (sv.states.Exists((FSMSave.SavedElement stElement) => { return stElement.node == stateToRemove; }))
  288. {
  289. if (stateToRemove.transitions != null)
  290. {
  291. foreach (Transition removalTransition in stateToRemove.transitions)
  292. {
  293. if (removalTransition != null)
  294. {
  295. RemoveTransition(removalTransition);
  296. }
  297. }
  298. }
  299. edgesToRemove.Clear();
  300. AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(stateToRemove));
  301. sv.states.RemoveAll((FSMSave.SavedElement removePredicate) => {
  302. return removePredicate.node == stateToRemove;
  303. });
  304. }
  305. }
  306. statesToRemove.Clear();
  307. foreach (Edge e in edges)
  308. {
  309. Transition transition = null;
  310. if (edgeTransitionTupleList.TryGetValue(e, out transition))
  311. {
  312. if (!PathExists(path, "Transitions", transition.name + ".asset"))
  313. {
  314. string finalPath = LocatePathOrCreateFolder(path, "Transitions", transition.name);
  315. string assetId = Path.Combine(finalPath, "Transitions");
  316. AssetDatabase.CreateAsset(transition, finalPath);
  317. EditorUtility.SetDirty(transition);
  318. } else
  319. {
  320. EditorUtility.SetDirty(transition);
  321. }
  322. }
  323. }
  324. EditorUtility.SetDirty(instance.saveFile);
  325. AssetDatabase.SaveAssets();
  326. AssetDatabase.Refresh();
  327. EditorUtility.FocusProjectWindow();
  328. }
  329. private void RemoveTransition(Transition transition)
  330. {
  331. Edge edgeToClear = FindEdgeByTransition(transition);
  332. if (edgeToClear != null)
  333. {
  334. edgeTransitionTupleList.Remove(edgeToClear);
  335. }
  336. AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(transition));
  337. }
  338. private void RemoveTransitionFromState(State state, Transition targetTransition)
  339. {
  340. List<Transition> newTransitionList = new List<Transition>(state.transitions);
  341. if (newTransitionList.Contains(targetTransition))
  342. {
  343. newTransitionList.Remove(targetTransition);
  344. }
  345. state.transitions = newTransitionList.ToArray();
  346. }
  347. private Edge FindEdgeByTransition(Transition transition)
  348. {
  349. foreach (KeyValuePair<Edge,Transition> kvp in edgeTransitionTupleList)
  350. {
  351. if (kvp.Value == transition)
  352. {
  353. return kvp.Key;
  354. }
  355. }
  356. return null;
  357. }
  358. private string LocatePathOrCreateFolder(string path,string folder,string fileName)
  359. {
  360. string folderRelativePath = Path.GetRelativePath(Application.dataPath, path);
  361. string transitionFolder = string.Empty;
  362. if (folder != string.Empty)
  363. {
  364. if (!Directory.Exists(Path.Combine(path, folder)))
  365. {
  366. AssetDatabase.CreateFolder(Path.Combine("Assets", folderRelativePath), folder);
  367. transitionFolder = Path.Combine("Assets", folderRelativePath, folder);
  368. }
  369. else
  370. {
  371. transitionFolder = Path.Combine("Assets", folderRelativePath, folder);
  372. }
  373. } else
  374. {
  375. transitionFolder = Path.Combine("Assets", folderRelativePath);
  376. }
  377. return Path.Combine(transitionFolder, fileName + ".asset");
  378. }
  379. private bool PathExists(string path, string folders, string fileName)
  380. {
  381. return File.Exists(Path.Combine(path,folders,fileName));
  382. }
  383. }