A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections.Generic;
3using System.IO;
4using UnityEditor;
5using UnityEditor.SceneManagement;
6using UnityEngine;
7using UnityEngine.SceneManagement;
8using UnityObject = UnityEngine.Object;
9
10namespace Unity.VisualScripting
11{
12 internal class DropdownOptions
13 {
14 internal bool interactable = true;
15 internal string label;
16 internal string tooltip;
17 internal Action callback;
18 }
19
20 internal class SplitDropdown
21 {
22 private bool reset;
23 private List<DropdownOptions> listOfOptions;
24
25 internal SplitDropdown(List<DropdownOptions> listOfOptions)
26 {
27 this.listOfOptions = listOfOptions;
28 }
29
30 internal DropdownOptions GetOption(int index)
31 {
32 return listOfOptions[index];
33 }
34
35 internal void Reset()
36 {
37 reset = true;
38 }
39
40 internal bool Draw(Rect area, string label, ref bool toggleState)
41 {
42 GUI.Box(area, string.Empty, EmptyGraphWindow.buttonStyle);
43
44 if (GUI.Button(new Rect(area.x, area.y, area.width - area.height, area.height), label,
45 EmptyGraphWindow.labelStyleButton))
46 {
47 toggleState = false;
48
49 return true;
50 }
51
52 Rect toggleRect = new Rect(area.x + area.width - area.height - 2, area.y - 1, area.height + 3,
53 area.height + 3);
54
55 toggleState = GUI.Toggle(toggleRect, toggleState, "", EmptyGraphWindow.labelStyleButton);
56
57 GUI.Label(toggleRect, EmptyGraphWindow.dropdownIcon, EmptyGraphWindow.titleStyle);
58
59 if (toggleState)
60 {
61 GUI.BeginGroup(new Rect(area.x, area.y + area.height - 1, area.width, area.height * 2),
62 EmptyGraphWindow.buttonStyle);
63 Rect areaChild = new Rect(0, 0, EmptyGraphWindow.buttonWidth,
64 EmptyGraphWindow.buttonHeight);
65
66 GUIContent contentOption;
67
68 foreach (DropdownOptions dropdownOption in listOfOptions)
69 {
70 EditorGUI.BeginDisabledGroup(!dropdownOption.interactable);
71
72 if (dropdownOption.interactable)
73 {
74 contentOption = new GUIContent(dropdownOption.label);
75 }
76 else
77 {
78 contentOption = new GUIContent(dropdownOption.label, dropdownOption.tooltip);
79 }
80
81 if (GUI.Button(areaChild, contentOption, EmptyGraphWindow.labelStyleDropdownOptions))
82 {
83 toggleState = false;
84
85 dropdownOption.callback();
86 }
87
88 EditorGUI.EndDisabledGroup();
89
90 areaChild.y += areaChild.height;
91 }
92
93 GUI.EndGroup();
94 }
95
96 if (reset)
97 {
98 toggleState = false;
99 reset = false;
100 }
101
102 return false;
103 }
104 }
105
106 internal class EmptyGraphWindow : EditorWindow
107 {
108 internal static GUIContent dropdownIcon;
109
110 private bool toggleOnScript = false;
111 private bool toggleOnState = false;
112
113 internal static GUIStyle buttonStyle;
114 internal static GUIStyle labelStyleButton;
115 internal static GUIStyle labelStyleDropdownOptions;
116 internal static GUIStyle titleStyle;
117
118 internal static int titleHeight = 120;
119 internal static int buttonWidth = 200;
120 internal static int buttonHeight = 22;
121
122 private bool shouldCloseWindow;
123 private Vector2 scrollPosition;
124 private Rect scrollArea;
125
126 private SplitDropdown splitDropdownScriptGraph;
127 private SplitDropdown splitDropdownStateGraph;
128
129 const string k_OnSelectedGameObject = "...on selected GameObject";
130 const string k_OnNewGameObject = "...on new GameObject";
131 const string k_SelectedGameObject = "Please, select a GameObject";
132
133 [MenuItem("Window/Visual Scripting/Visual Scripting Graph", false, 3010)]
134 private static void ShowWindow()
135 {
136 EmptyGraphWindow window = GetWindow<EmptyGraphWindow>();
137
138 window.titleContent = new GUIContent("Visual Scripting");
139 }
140
141 private void OnEnable()
142 {
143 string pathRoot = PathUtility.GetPackageRootPath();
144
145 UnityObject icon = EditorGUIUtility.Load(Path.Combine(pathRoot,
146 "Editor/VisualScripting.Shared/EditorAssetResources/SplitButtonArrow.png"));
147
148 dropdownIcon = new GUIContent(icon as Texture2D);
149
150 scrollArea = new Rect(0, 0, 630, 300);
151
152 toggleOnScript = false;
153 toggleOnState = false;
154 shouldCloseWindow = false;
155
156 var listOfOptions = new List<DropdownOptions>
157 {
158 new DropdownOptions
159 {
160 label = k_OnSelectedGameObject,
161 tooltip = k_SelectedGameObject,
162 callback = CreateScriptGraphOnSelectedGameObject
163 },
164 new DropdownOptions
165 {
166 label = k_OnNewGameObject,
167 callback = CreateScriptGraphOnNewGameObject
168 }
169 };
170
171 splitDropdownScriptGraph = new SplitDropdown(listOfOptions);
172
173 listOfOptions = new List<DropdownOptions>
174 {
175 new DropdownOptions
176 {
177 label = k_OnSelectedGameObject,
178 tooltip = k_SelectedGameObject,
179 callback = CreateStateGraphOnSelectedGameObject
180 },
181 new DropdownOptions
182 {
183 label = k_OnNewGameObject,
184 callback = CreateStateGraphOnNewGameObject
185 }
186 };
187
188 splitDropdownStateGraph = new SplitDropdown(listOfOptions);
189 }
190
191 private void OpenGraphAsset(UnityObject unityObject, bool shouldSetSceneAsDirty)
192 {
193 shouldCloseWindow = true;
194
195 ScriptGraphAsset scriptGraphAsset = unityObject as ScriptGraphAsset;
196
197 GraphReference graphReference = null;
198
199 if (scriptGraphAsset != null)
200 {
201 graphReference = GraphReference.New(scriptGraphAsset, true);
202 }
203 else
204 {
205 StateGraphAsset stateGraphAsset = unityObject as StateGraphAsset;
206
207 if (stateGraphAsset != null)
208 {
209 graphReference = GraphReference.New(stateGraphAsset, true);
210 }
211 }
212
213 if (shouldSetSceneAsDirty)
214 {
215 EditorSceneManager.MarkSceneDirty(SceneManager.GetActiveScene());
216 }
217
218 GraphWindow.OpenActive(graphReference);
219 }
220
221 private void OpenGraphFromPath(string path, bool shouldSetSceneAsDirty)
222 {
223 path = path.Replace(Application.dataPath, "Assets");
224
225 UnityObject unityObject = AssetDatabase.LoadAssetAtPath(path, typeof(UnityObject));
226
227 OpenGraphAsset(unityObject, shouldSetSceneAsDirty);
228 }
229
230 private void OpenGraph()
231 {
232 EditorGUIUtility.ShowObjectPicker<MacroScriptableObject>(null, false, String.Empty,
233 EditorGUIUtility.GetControlID(FocusType.Passive));
234 }
235
236 private bool CreateScriptGraphAsset(GameObject gameObject = null, bool updateName = false)
237 {
238 var path = EditorUtility.SaveFilePanelInProject("Save Graph", "New Script Graph", "asset", null);
239
240 if (string.IsNullOrEmpty(path))
241 {
242 return false;
243 }
244
245 VSUsageUtility.isVisualScriptingUsed = true;
246
247 var macro = (IMacro)CreateInstance(typeof(ScriptGraphAsset));
248 var macroObject = (UnityObject)macro;
249 macro.graph = FlowGraph.WithStartUpdate();
250
251 if (gameObject != null)
252 {
253 ScriptMachine flowMachine = gameObject.AddComponent<ScriptMachine>();
254
255 flowMachine.nest.macro = (ScriptGraphAsset)macro;
256 }
257
258 string filename = Path.GetFileNameWithoutExtension(path);
259
260 if (updateName)
261 {
262 gameObject.name = filename;
263 }
264
265 macroObject.name = filename;
266
267 AssetDatabase.CreateAsset(macroObject, path);
268
269 bool shouldSetSceneAsDirty = gameObject != null;
270
271 OpenGraphFromPath(path, shouldSetSceneAsDirty);
272
273 return true;
274 }
275
276 private void CreateScriptGraph()
277 {
278 Selection.activeGameObject = null;
279
280 if (CreateScriptGraphAsset())
281 {
282 shouldCloseWindow = true;
283 }
284 }
285
286 private void CreateScriptGraphOnNewGameObject()
287 {
288 Selection.activeGameObject = null;
289
290 GameObject newGameObject = new GameObject();
291
292 if (!CreateScriptGraphAsset(newGameObject, true))
293 {
294 DestroyImmediate(newGameObject);
295 }
296 }
297
298 private void CreateScriptGraphOnSelectedGameObject()
299 {
300 if (Selection.activeGameObject != null)
301 {
302 if (CreateScriptGraphAsset(Selection.activeGameObject))
303 {
304 shouldCloseWindow = true;
305 }
306 }
307 }
308
309 private bool CreateStateGraphAsset(GameObject gameObject = null, bool updateName = false)
310 {
311 var path = EditorUtility.SaveFilePanelInProject("Save Graph", "New State Graph", "asset", null);
312
313 if (string.IsNullOrEmpty(path))
314 {
315 return false;
316 }
317
318 VSUsageUtility.isVisualScriptingUsed = true;
319
320 var macro = (IMacro)CreateInstance(typeof(StateGraphAsset));
321 var macroObject = (UnityObject)macro;
322 macro.graph = StateGraph.WithStart();
323
324 if (gameObject != null)
325 {
326 StateMachine stateMachine = gameObject.AddComponent<StateMachine>();
327
328 stateMachine.nest.macro = (StateGraphAsset)macro;
329 }
330
331 string filename = Path.GetFileNameWithoutExtension(path);
332
333 if (updateName)
334 {
335 gameObject.name = filename;
336 }
337
338 macroObject.name = filename;
339
340 AssetDatabase.CreateAsset(macroObject, path);
341
342 bool shouldSetSceneAsDirty = gameObject != null;
343
344 OpenGraphFromPath(path, shouldSetSceneAsDirty);
345
346 return true;
347 }
348
349 private void CreateStateGraph()
350 {
351 Selection.activeGameObject = null;
352
353 if (CreateStateGraphAsset())
354 {
355 shouldCloseWindow = true;
356 }
357 }
358
359 private void CreateStateGraphOnNewGameObject()
360 {
361 Selection.activeGameObject = null;
362
363 GameObject newGameObject = new GameObject();
364
365 if (!CreateStateGraphAsset(newGameObject, true))
366 {
367 DestroyImmediate(newGameObject);
368 }
369 }
370
371 private void CreateStateGraphOnSelectedGameObject()
372 {
373 if (Selection.activeGameObject != null)
374 {
375 if (CreateStateGraphAsset(Selection.activeGameObject))
376 {
377 shouldCloseWindow = true;
378 }
379 }
380 }
381
382 private void CreateStyles()
383 {
384 if (buttonStyle == null)
385 {
386 buttonStyle = new GUIStyle("Button");
387 }
388
389 if (labelStyleButton == null)
390 {
391 labelStyleButton = new GUIStyle("Label") { alignment = TextAnchor.MiddleCenter };
392 }
393
394 if (labelStyleDropdownOptions == null)
395 {
396 labelStyleDropdownOptions = new GUIStyle("ToolbarButton")
397 {
398 alignment = TextAnchor.MiddleLeft,
399 padding = new RectOffset(20, 0, 0, 0)
400 };
401 }
402
403 if (titleStyle == null)
404 {
405 titleStyle = new GUIStyle("Label") { alignment = TextAnchor.MiddleCenter, fontSize = 20 };
406 }
407 }
408
409 private void ResetToggle()
410 {
411 if (Event.current.rawType == EventType.MouseUp)
412 {
413 if (toggleOnScript)
414 {
415 splitDropdownScriptGraph.Reset();
416
417 Repaint();
418 }
419
420 if (toggleOnState)
421 {
422 splitDropdownStateGraph.Reset();
423
424 Repaint();
425 }
426 }
427 }
428
429 private void OpenGraphFromPicker()
430 {
431 if (Event.current.commandName == "ObjectSelectorUpdated")
432 {
433 UnityObject selectedObject = EditorGUIUtility.GetObjectPickerObject();
434
435 OpenGraphAsset(selectedObject, false);
436 }
437 }
438
439 private void OnGUI()
440 {
441 CreateStyles();
442
443 ResetToggle();
444
445 DropAreaGUI();
446
447 OpenGraphFromPicker();
448
449 scrollPosition = GUI.BeginScrollView(new Rect(0, 0, position.width, position.height), scrollPosition,
450 scrollArea);
451 GUILayout.BeginVertical(GUILayout.ExpandHeight(true));
452
453 Vector2 groupSize = new Vector2(scrollArea.width, 280);
454
455 GUI.BeginGroup(new Rect((position.width / 2) - (groupSize.x / 2), (position.height / 2) - (groupSize.y / 2),
456 groupSize.x, groupSize.y));
457
458 GUI.Label(new Rect(0, 0, groupSize.x, titleHeight), "Drag and drop a Visual Scripting Graph asset here\nor",
459 titleStyle);
460
461 int buttonX = 10;
462
463 if (GUI.Button(new Rect(buttonX, titleHeight, buttonWidth, buttonHeight), "Browse to open a Graph"))
464 {
465 OpenGraph();
466 }
467
468 buttonX += (buttonWidth + 10);
469
470 Rect area = new Rect(buttonX, titleHeight, buttonWidth, buttonHeight);
471
472 splitDropdownScriptGraph.GetOption(0).interactable = Selection.activeGameObject != null;
473
474 if (splitDropdownScriptGraph.Draw(area, "Create new Script Graph", ref toggleOnScript))
475 {
476 CreateScriptGraph();
477 }
478
479 if (toggleOnScript)
480 {
481 toggleOnState = false;
482 }
483
484 buttonX += (buttonWidth + 10);
485
486 area = new Rect(buttonX, titleHeight, buttonWidth, buttonHeight);
487
488 splitDropdownStateGraph.GetOption(0).interactable = Selection.activeGameObject != null;
489
490 if (splitDropdownStateGraph.Draw(area, "Create new State Graph", ref toggleOnState))
491 {
492 CreateStateGraph();
493 }
494
495 if (toggleOnState)
496 {
497 toggleOnScript = false;
498 }
499
500 GUI.EndGroup();
501 GUILayout.EndVertical();
502 GUI.EndScrollView();
503
504 if (shouldCloseWindow)
505 {
506 Close();
507 }
508 }
509
510 private void DropAreaGUI()
511 {
512 Event currentEvent = Event.current;
513
514 Rect activeArea = GUILayoutUtility.GetRect(0, 0, GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true));
515
516 switch (currentEvent.type)
517 {
518 case EventType.DragUpdated:
519 case EventType.DragPerform:
520 if (!activeArea.Contains(currentEvent.mousePosition))
521 {
522 return;
523 }
524
525 DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
526
527 if (currentEvent.type == EventType.DragPerform)
528 {
529 DragAndDrop.AcceptDrag();
530
531 foreach (UnityObject draggedObject in DragAndDrop.objectReferences)
532 {
533 ScriptGraphAsset scriptGraphAsset = draggedObject as ScriptGraphAsset;
534
535 if (scriptGraphAsset != null)
536 {
537 shouldCloseWindow = true;
538
539 GraphWindow.OpenActive(GraphReference.New(scriptGraphAsset, true));
540 break;
541 }
542 }
543 }
544
545 break;
546 }
547 }
548 }
549}