Search Unity

Toggling Gizmos from script

Discussion in 'Scripting' started by GanonM, Jul 29, 2019.

  1. GanonM

    GanonM

    Joined:
    Nov 10, 2018
    Posts:
    10
    Is there any way to toggle specific gizmos from script? The context is I am working on an editor plugin where it would be useful to quick toggle a specific Gizmo on and off without the user having to access the built-in dropdown menu for Gizmos.

    Thanks.
     
  2. calpolican

    calpolican

    Joined:
    Feb 2, 2015
    Posts:
    425
  3. GanonM

    GanonM

    Joined:
    Nov 10, 2018
    Posts:
    10
    Thanks calpolican, I'll give this a try. On first glance it seems like it just handles enabling/disabling all gizmos - I specifically want to toggle a single gizmo, but maybe I can modify it for my own purposes. Thanks again.
     
  4. calpolican

    calpolican

    Joined:
    Feb 2, 2015
    Posts:
    425
    Ok, I haven't really worked with gizmos, and I'm not sure if this would work, but... I guess you're using the OnDrawGizmos() function for your cusotm gizmo, right? couldn't you just put a bool there before the Gizmo.DrawSphere() or whatever you have to draw the gizmo? Anyway, best of luck, and if you find an answer, please post it.


    Code (CSharp):
    1. public class gizmoTest : MonoBehaviour
    2. {
    3.     public float explosionRadius = 5.0f;
    4.  
    5.     void OnDrawGizmos()
    6.     {
    7.         if (on){ //You put a bool here
    8.         // Display the explosion radius
    9.         Gizmos.color = new Color(1, 1, 0, 0.75F);
    10.         Gizmos.DrawSphere(transform.position, explosionRadius);
    11.         }
    12.     }
    13. }
     
  5. GanonM

    GanonM

    Joined:
    Nov 10, 2018
    Posts:
    10
    Thanks for the reply, unfortunately this wouldn't achieve the desired outcome. This would only effectively toggle the gizmos for a component if gizmos were already globablly enabled and the specific component gizmo was also enabled. Ideally, one would be able to just directly call 'OnDrawGizmos', however this throws a runtime exception since Gizmo methods can only be called inside the 'magic' OnDrawGizmos method, which must be invoked by the Unity Editor and only gets called when gizmos are globablly enabled.

    It's not a complete show stopper - one of my custom inspectors relies heavily upon a specific Gizmo being enabled, so for the purposes of improving user friendliness, it would be nice to have a shortcut button that selectively enables one specific Gizmo (if it isn't already enabled).

    Thanks for the help nonetheless. I'll probably come back to this problem at a later date.
     
  6. Grizmu

    Grizmu

    Joined:
    Aug 27, 2013
    Posts:
    131
    I strongly advise against using if directives in OnDrawGizmos. I've worked on projects that had lots of gizmos, and even with them disabled thousands of objects needed to have it called, just to check the if condition. This is causing a snowball effect on editor performance the bigger the scene gets.

    Best idea would be to create an utility class that handles the gizmo toggling for your custom inspectors through the reflection.
     
    Last edited: Aug 7, 2019
  7. INeatFreak

    INeatFreak

    Joined:
    Sep 12, 2018
    Posts:
    46
    Hey! have you found a solution ? i am looking a way to enable/disable CharacterJoint component gizmos without searching through gizmos panel or character rig. Fricking thing enables itself every time i restart the editor.
     
  8. GanonM

    GanonM

    Joined:
    Nov 10, 2018
    Posts:
    10
    Nope, I ended up just relying upon the user remembering to enable gizmos, which is less than ideal...
     
  9. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    I've reported this as a bug. It's ridiculous that Unity still hasn't created an API call for this "feature".

    (It's not a feature: it's a bug. UnityEditor will globally stop executing your OnDrawGizmos methods - which is *still not documented even in 2020* - and there's no API to detect that or fix it. You just waste hours or days debugging "why isn't this running on user X's machine?" only to discover it's that badly designed Gizmos button blocking all the method calls :))
     
  10. electric_jesus

    electric_jesus

    Joined:
    Mar 8, 2015
    Posts:
    36
    Here are two methods you can use to either switch a specific type gizmo in project or switch all of them. Tested on Unity 2019.3. Based on this answer:
    https://answers.unity.com/questions...-view.html?childToView=1651053#answer-1651053

    Code (CSharp):
    1. public static void SwitchAllGizmos(bool value)
    2. {
    3.     var annotation = Type.GetType("UnityEditor.Annotation, UnityEditor");
    4.     var classId = annotation.GetField("classID");
    5.     var scriptClass = annotation.GetField("scriptClass");
    6.  
    7.     var annotationUtility = Type.GetType("UnityEditor.AnnotationUtility, UnityEditor");
    8.     var getAnnotations = annotationUtility.GetMethod("GetAnnotations", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
    9.     var setGizmoEnabled = annotationUtility.GetMethod("SetGizmoEnabled", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
    10.     var setIconEnabled = annotationUtility.GetMethod("SetIconEnabled", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
    11.  
    12.     var annotations = (Array)getAnnotations.Invoke(null, null);
    13.     foreach (var a in annotations)
    14.     {
    15.         var classIdValue = (int)classId.GetValue(a);
    16.         var scriptClassValue = (string)scriptClass.GetValue(a);
    17.  
    18.         setGizmoEnabled.Invoke(null, new object[] { classIdValue, scriptClassValue, value ? 1 : 0, false });
    19.         setIconEnabled.Invoke(null, new object[] { classIdValue, scriptClassValue, value ? 1 : 0 });
    20.     }
    21. }
    22.  
    23. public static void SwitchGizmo<T>(bool value)
    24. {
    25.     var typeName = typeof(T).Name;
    26.     var annotation = Type.GetType("UnityEditor.Annotation, UnityEditor");
    27.     var classId = annotation.GetField("classID");
    28.     var scriptClass = annotation.GetField("scriptClass");
    29.  
    30.     var annotationUtility = Type.GetType("UnityEditor.AnnotationUtility, UnityEditor");
    31.     var getAnnotations = annotationUtility.GetMethod("GetAnnotations", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
    32.     var setGizmoEnabled = annotationUtility.GetMethod("SetGizmoEnabled", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
    33.     var setIconEnabled = annotationUtility.GetMethod("SetIconEnabled", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
    34.  
    35.     var annotations = (Array)getAnnotations.Invoke(null, null);
    36.     foreach (var a in annotations)
    37.     {
    38.         var scriptClassValue = (string)scriptClass.GetValue(a);
    39.         if (scriptClassValue.Equals(typeName) == false) continue;
    40.         var classIdValue = (int)classId.GetValue(a);
    41.  
    42.         setGizmoEnabled.Invoke(null, new object[] { classIdValue, scriptClassValue, value ? 1 : 0, false });
    43.         setIconEnabled.Invoke(null, new object[] { classIdValue, scriptClassValue, value ? 1 : 0 });
    44.         break;
    45.     }
    46. }
     
    Last edited: Mar 28, 2021
  11. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    Isn't that the same as the answer from 2 years ago, which (as already noted) doesn't work except in limited circumstances (and fails in most of the important ones)?
     
  12. John_Leorid

    John_Leorid

    Joined:
    Nov 5, 2012
    Posts:
    650
    Maybe thats helpful?

    Code (CSharp):
    1.     public static void SetSceneViewGizmos(bool gizmosOn)
    2.     {
    3. #if UNITY_EDITOR
    4.         UnityEditor.SceneView sv =
    5.             UnityEditor.EditorWindow.GetWindow<UnityEditor.SceneView>();
    6.         sv.drawGizmos = gizmosOn;
    7. #endif
    8.     }
    9.     public static bool GetSceneViewGizmosEnabled()
    10.     {
    11. #if UNITY_EDITOR
    12.         UnityEditor.SceneView sv =
    13.             UnityEditor.EditorWindow.GetWindow<UnityEditor.SceneView>();
    14.         return sv.drawGizmos;
    15. #else
    16.         return false;
    17. #endif
    18.     }
    I use it in some scripts to enable gizmos, then wait for gizmos to draw (and do other stuff )and disable it after they were drawn, inside the OnDrawGizmo Method, calling the same method.

    Also theres an Update delegate you can subscribe in the static EditorApplication class, and the "SceneView.duringSceneGui" can also be helpful, you can draw handles during this routine and it does not depend on the Gizmos Toggle. (and handles have way more functionality - you can click on them, drag them, draw text, dottet lines, meshes, define Z-Testing for them and so on)
     
  13. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    Interesting approach. How do you know when to 'turn it off' when you have more than one class that's running OnDrawGizmos?

    OR ... is it enough that ANY of them turn it off (even the first one to execute) because the change doesn't take effect until next Editor-frame?
     
  14. adamgolden

    adamgolden

    Joined:
    Jun 17, 2019
    Posts:
    1,555
    Useful tip - thanks! This works - but getting the window causes focus change by default. I found the solution to that (in 2019 LTS at least) is to specify no focus (the false param),
    Code (CSharp):
    1. SceneView sv = EditorWindow.GetWindow<SceneView>(null, false);
     
    MUGIK, funkyCoty and ruudvangaal like this.
  15. MUGIK

    MUGIK

    Joined:
    Jul 2, 2015
    Posts:
    481
    Jost compiled answers from above into one handy utility script. You need to place this script inside Editor folder as any other editor-only script.
    It also has a shortcut for toggling scene gizmos. By default it's Alt+G. You can change this through the code or using "Edit/Shortcuts..." menu.

    It works for me in Unity 2020.3.19f1

    Code (CSharp):
    1. using UnityEditor;
    2.  
    3. public static class SceneViewGizmosUtility
    4. {
    5.     [MenuItem("Tools/Toogle Scene View Gizmos &g")]
    6.     public static void ToggleSceneViewGizmos()
    7.     {
    8.         var currentValue = GetSceneViewGizmosEnabled();
    9.         SetSceneViewGizmos(!currentValue);
    10.     }
    11.  
    12.     public static void SetSceneViewGizmos(bool gizmosOn)
    13.     {
    14. #if UNITY_EDITOR
    15.         UnityEditor.SceneView sv =
    16.             UnityEditor.EditorWindow.GetWindow<UnityEditor.SceneView>(null, false);
    17.         sv.drawGizmos = gizmosOn;
    18. #endif
    19.     }
    20.  
    21.     public static bool GetSceneViewGizmosEnabled()
    22.     {
    23. #if UNITY_EDITOR
    24.         UnityEditor.SceneView sv =
    25.             UnityEditor.EditorWindow.GetWindow<UnityEditor.SceneView>(null, false);
    26.         return sv.drawGizmos;
    27. #else
    28.         return false;
    29. #endif
    30.     }
    31. }
     
    Last edited: Jan 21, 2023
  16. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    Great! You posted another thing that definitely doesn't work (confirmed by Unity QA: they have no intention of fixing this), adding nothing to the thread. If you have an actual solution to the actual problem, post away. "I copy/pasted the existing code that doesn't work" isn't helpful.
     
  17. realitygarage

    realitygarage

    Joined:
    Feb 17, 2017
    Posts:
    11
    It looks like the call to UnityEditor.EditorWindow.GetWindow<UnityEditor.SceneView>(); rellay bogs down the editor, so may only want to call once and store the result
     
  18. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    2,433
    And if the user chooses to reset their layout or close a scene view and open a new one, they're out of luck, I guess.

    While @a436t4ataf 's tone is a bit rough, I am inclined to agree. Hacking the Scene View's deep and undocumented internals is fraught with brittle design flaws and risks of breakage. Gizmos and Handles are drawn at very specific points in time in the editor's processing loop, so that they aren't overdrawn by the other objects in the scene. They have decided not to expose a UnityEditor API to do the toggle. They haven't even exposed the shortcut, so you can't do a generic
    EditorApplication.ExecuteMenuItem("ToggleGizmos");
    and I suspect that's just the way they prefer it.
     
  19. John_Leorid

    John_Leorid

    Joined:
    Nov 5, 2012
    Posts:
    650
    It is public API, I'm not doing any reflection. And instead of GetWindow<> you could use
    SceneView.lastActiveSceneView
    .