Search Unity

Resolved Gizmos

Discussion in 'Authoring Dev Blitz Day 2023' started by Baste, Jan 25, 2023.

  1. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    There were some recent performance work on Gizmos, which is great. There's still a lot of things that I'd like to see that would make building editor tooling and visualization a ton easier:

    - 2D Friendly gizmos! We often want circles and boxes and all of that jazz. Boxes can be made from lines, and we can drop down to Handles for the circles, but that's a) annoying and b) requires UnityEditor imports.

    - More gizmo shapes in general! Character controllers are often capsules, and I often want to draw things like where the character's collider was when something happened, both in 2D and in 3D. I added an arrow gizmo helper to our code base years ago and it's the most common thing for me to want to draw.

    - Text! Again, we can drop to Handles and use it's text, but that's awkward, camera facing, ugly text. Some TextCore covered high res text with a fixed facing would make such a huge difference to gizmo drawing. The fallback here is to use a in-game Text component and then deactivate/delete it when post processing the scene, but that's annoying code to write and prevents us from seeing them while the game is running.

    - A way to queue a gizmo for drawing outside of OnDrawGizmos. We very often want to draw gizmos for the result of a raycast or some other code. That requires us to copy a bunch of data from the code that does that to a field, in order to access it in OnDrawGizmos or OnDrawGizmosSelected. That is a major inconvenience, and can at times not be possible (eg. if we're in a static method). Our code-base uses a Gizmos singleton that we pass Actions to draw for one frame or forever, but this might be out of reach for some people, or annoying enough to deal with that it won't be done.


    In short, the Gizmos system could be made a lot better by adding a bunch of new features. Gizmos is at the core of Unity, and it's something we use constantly, so we feel like they're really missing some necessary love.
     
  2. kaarrrllll

    kaarrrllll

    Unity Technologies

    Joined:
    Aug 24, 2017
    Posts:
    552
    More gizmos is pretty low hanging fruit, especially simple things like boxes and circles.

    When you say 2D, is that meaning specific overloads for 2D or just shapes that are useful in 2D?

    That's a good idea. We do have a text rendering path in the gizmo renderer already for the GameObject icons, would exposing that be sufficient?

    Many gizmos are rendered in "immediate mode", and need to be called when the render is at a specific state. If we had restricted `OnDrawGizmos` to the basic drawing commands available in the `Gizmos` class this might be possible to change, as for the most part they are simply adding to batched buffers that are rendered when necessary. However because gizmo drawing is effectively just invoking a delegate to user code for drawing, we cannot assume that only "safe" drawing commands are used. Ex, even within our code-base there are many gizmos that are not rendered through the `Gizmos.Draw` methods.
     
  3. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    Primarily shapes that are useful in 2D. But 2D overloads would be nice for those - for example, drawing a Gizmo Circle can mean two things:
    - A camera-facing circle
    - A circle facing in a specific direction

    When we're in 2D mode with an ortographic camera, we'll always want the second one, and it'll always face -z. For 3D, I'm not sure what the best default is - possibly camera-facing, possibly still -z?

    You can just require us to always pass the facing direction, as far as I can remember
    SceneView.currentDrawingSceneView.camera.transform.forward will always be available in OnDrawGizmos, so we can just use that if that's what we want an -z if that's what we want. Still, I think having default overloads that just use -z if we don't supply a facing will be nice.

    Right. I wasn't suggesting that you should be actually doing the drawing outside of OnDrawGizmos. I was suggesting that you would be able to queue something to happen when the editor is in that drawing mode.

    How that looks in our code-base is this:

    Code (csharp):
    1. var hit = Physics2D.Raycast(from, dir, distance);
    2.  
    3. GizmosHelper.AddGizmoCall(() => {
    4.     if (hit)
    5.         GizmosHelper.DrawArrow(from, hit.point, Color.red);
    6.     else
    7.         GizmosHelper.DrawArrow(from, from + (dir * distance), Color.green);
    8. }, callId: 39393);
    AddGizmoCall generates a singleton with HideFlags.DontSave, which draws all of the gizmos it has been supplied. The (optional) callId is used to replace the previous command with the same ID, we use it to replace earlier gizmos if we want to.

    This makes it a ton easier to quickly get visualizations for what our code is doing, without having to copy a bunch of data to our MonoBehaviour's fields, or have to have a MonoBehaviour around.

    My suggestion is to have something like that natively. Since you're saying that you're already effectively just invoking a delegate, my suggestion kinda boils down to "let's add things to that delegate from wherever", pluss some extra bells and whistles.


    OH and before I forget, a real low hanging fruit from our code-base you can look at is Color. We've got overloads for all of the basic shapes with colors, so instead of doing:
    Code (csharp):
    1. var old = Gizmos.Color;
    2. Gizmos.color = Color.red;
    3. Gizmos.DrawLine(...);
    4. Gizmos.Color = old;
    We do
    GizmosHelper.DrawLine(..., Color.red);
    . But we'd be happier with
    Gizmos.DrawLine(..., Color.red);


    We also have a GizmosColorScope which works as you'd expect it to.
     
  4. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    Probably!

    The only problem we might face is that it's very easy to get white text on white background or black text on black backgrounds. So a good way to draw an opaque and/or semi-transparent background would be necessary. The GameObject names already have some shapes, but the worry from reusing those is that Unity users are used to those and believe that they indicate a clickable handle.

    Other than that, exposing the stuff that's already there for the GameObject names would be a big improvement.
     
  5. Qriva

    Qriva

    Joined:
    Jun 30, 2019
    Posts:
    1,315
    I tried to solve my problem in similar way, but personally I would like to see exactly what Debug.DrawLine does, but for Gizmos (schedule draw and forget).
     
    Last edited: Jan 26, 2023
  6. kaarrrllll

    kaarrrllll

    Unity Technologies

    Joined:
    Aug 24, 2017
    Posts:
    552
    I see, yes that is more feasible. Like how EditorApplication.delayCall works, where the list of callbacks is cleared at the end of each frame?

    Yeah that's a really easy one. We have similar little utility disposable state classes littered over our internal packages, but never bother taking it to trunk. I'll add this to my notes on gizmos to look at when I have some spare time.
     
    Baste likes this.
  7. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    2,445
    I have no evidence, but this is what I have always assumed the Debug.Draw* functions were doing. They're just queueing up the lines until the next opportunity to dump them into the gizmo/ui rendering stage, and then sticking in the queue until the requested duration expires.
     
  8. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    Yup!
     
    kaarrrllll likes this.