Gizmo - Visual Debug Library

Background

At the end of 2020, I found myself writing a ledge climbing system similar to Assassin’s Creed. The system relies heavily on raycasting and vector math which needs to be tuned correctly if you want good results. Unfortunately, I wasn’t seeing good results and needed a way to visualize exactly what was going on.

Were I working in Unity I would have turned to their Gizmos library (or DrawDebug in Unreal). Alas, Roblox does not offer such a library and it wasn’t the first time I’ve wanted something similar so I implemented my own.

Gizmo

Gizmo allows you to render 3D debug information on the screen known as ‘gizmos’. It’s really easy to use and will make debugging complicated issues much easier.

Adding it to your project is easy with the studio plugin! Just hit the install button and the latest version of gizmo will be inserted into your game.

You can also view the source on GitHub.

Usage

Viewing Gizmos

By default, gizmos won’t render unless you manually enable them or have them turned on with the studio plugin. This means players won’t see them by accident and members of your team will only see them if they themselves have them enabled.

If you want to manually enable them, this can be done with a single line of code from any script:

workspace:SetAttribute("GizmosEnabled", true)

Otherwise, it’s best just to use the studio plugin and this will automatically switch them on for you.

Drawing Gizmos

Gizmos are only rendered for a single frame, this makes them easy to manage as they’ll be cleaned up for you automatically. On the flip-side this means you need to call your draw method at least once per frame to make sure the gizmo is rendered continuously.

RunService:BindToRenderStep("DrawGizmos", 200, function ()
	gizmo.setColor("Bright red")
	gizmo.drawRay(ray.Origin, ray.Direction)
	gizmo.reset() -- resets the style
end)

The following draw methods are supported:

  • drawBox
  • drawWireBox
  • drawSphere
  • drawWireSphere
  • drawPoint
  • drawLine
  • drawArrow
  • drawRay
  • drawText

You can also stylize your gizmos by calling the style methods before drawing them. This will make it easy to tell them apart:

  • setColor
  • setColor3
  • setTransparency
  • setOrigin
  • setLayer
  • setScale
  • reset

Documentation

You can find the full documentation on GitHub.

Future Updates

  • Persistent gizmos (gizmos that last longer than a single frame).
45 Likes

Wow man, this is great!
One thing I would change is that maybe you should make a custom visual for a ray that way there easy to single out.
Also, these should be in the community resources.

1 Like

Finally, I thought I was going to have to make one myself, it saved me quite a while!

This is great, but I’ve the following complaints;

  • The source is… Ugly. There is simply no other way to go about it. There’s a lot of insert, CFrame.Angles and style spam, making this code unbearable to go through. I am honestly a bit worried on the performance side of things.

  • As you stated, everything is only rendered once per frame. For me, there are a lot more conns than pros to this, as you’d have to setup a whole system to insert / remove renders from, when all you want to do is quickly debug what is up with your ray.


I made a minimalist version which only supports rays, arrows, cones and lines, but the source is much cleaner and the drawings last for more than a frame.
Here is the source for those interested: paste.sh · encrypted pastebin

2 Likes

To your first point, the module is designed to be performant.

  • Instances are cached in memory to avoid creating them at runtime unless necessary
  • If gizmos are disabled then each call is the same as an empty function call.
  • Calls to style and insert are necessary, but also pretty cheap.
  • CFraming adornments is unavoidable.

To your second point, this is down to personal preference. Typically, the information you want to display will change with time so gizmo automatically cleans them up to avoid stale artifacts. Eventually, I plan to expand the library to support long-lived adornments since I realize there is still a valid use-case here.

1 Like

When I was talking about the source, I was referring to the general infrastructure - I’m aware that CFrame’ing is unavoidable - my point was that the source could look much cleaner while achieving the same purpose. It is hard to understand what’s going on currently