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}