- Extending Unity with Editor Scripting
- Angelo Tadres
- 1916字
- 2025-02-23 16:05:05
Creating gizmos through code
We explored how to add simple gizmos through the Unity editor. This section will cover how to properly implement the OnDrawGizmos
and OnDrawGizmosSelected
methods to achieve a similar but more flexible solution.
The OnDrawGizmos and OnDrawGizmosSelected methods
To get started, in a new project, create a script called GizmoExample.cs
. We will use this as a guinea pig for our first gizmos experiments (don't worry, the script is not going to suffer too much!)
Write the following code in GizmoExample.cs
:
using UnityEngine; public class GizmoExample : MonoBehaviour { private void OnDrawGizmos() { } }
When you implement the OnDrawGizmos
method, you can add gizmos that always drawn in the Scene View and also allows the possibility to be selected with a click.
In this case, the method is empty. However, if you come back to Unity and wait for the compiler to end, you'll find that the class GizmoExample is listed in the Gizmos dropdown on the Scene View:

The Gizmos dropdown allows you to change the way gizmos are displayed in the Scene View. At the top of the dropdown, you can change the scale size that gizmos are drawn at; next to that, you will see a list of the available gizmos. To turn gizmos on or off, simply click on the checkbox to hide or show the gizmo itself.
If you select the column icon associated with GizmoExample, you will get the same kind of results we saw when we clicked on the cube icon in the inspector. Here, again, you can choose between using a label, a built-in icon, or a custom icon. For now, just choose a label.
As soon you add the GizmoExample.cs
script to a new game object in the scene, a gizmo label with the name of the game object will be displayed in the Scene View.
We explored a simple way to make our script visible to Unity, but there are more powerful things to explore. Remove the icon from the Gizmos dropdown and update the GizmoExample.cs
script as follows:
using UnityEngine; public class GizmoExample : MonoBehaviour { private void OnDrawGizmos () { Gizmos.color = Color.white; Gizmos.DrawCube ( transform.position, Vector3.one); } private void OnDrawGizmosSelected () { Gizmos.color = Color.red; Gizmos.DrawWireCube ( transform.position, Vector3.one); } }
We added a new method, OnDrawGizmosSelected
. Implement this to draw gizmos only if the object is selected. Here, gizmos aren't pickable.
Inside the OnDrawGizmos
and OnDrawGizmosSelected
methods, we made use of the Gizmos class to draw gizmos. Now, you should see a solid cube in the position of the game object with the GizmoExample.cs
script attached, and then a solid cube with a color outline when this game object is selected:

At this point, you may have noticed that the Gizmos class is part of the UnityEngine
namespace instead of the UnityEditor
namespace. This means you can use it in your MonoBehaviour
class directly if you have the method OnDrawGizmos
or OnDrawGizmosSelected
implemented.
Adding gizmos using the DrawGizmo attribute
The DrawGizmo
attribute allows you to display gizmos using a separated class from the original MonoBehaviour
class and without using the OnDrawGizmos
or OnDrawGizmosSelected
method explicitly.
Here, we will need two scripts. The first one, called TargetExample.cs
, is the MonoBehaviour
class we want to add the gizmo; and the second one, called DrawGizmoExample.cs
is a script that implements the gizmo.
Let's start creating the script TargetExample.cs
in a new project:
using UnityEngine; public class TargetExample : MonoBehaviour { }
As we can see, no gizmo logic was added here. Because the DrawGizmo
attribute is part of the UnityEditor
namespace, we will create the DrawGizmoExample.cs
script inside an Editor
folder. For this, add the following code:
using UnityEngine; using UnityEditor; public class DrawGizmoExample { // This emulates OnDrawGizmos [DrawGizmo(GizmoType.NotInSelectionHierarchy | GizmoType.InSelectionHierarchy | GizmoType.Selected | GizmoType.Active | GizmoType.Pickable)] private static void MyCustomOnDrawGizmos( TargetExample targetExample, GizmoType gizmoType) { Gizmos.color = Color.white; Gizmos.DrawCube( targetExample.transform.position, Vector3.one); } }
Save everything you've done so far, and in a new scene, add a game object with TargetExample.cs
attached. You will see this:

The TargetExample
class is rendering a gizmos cube similar to the past example, but now the DrawGizmoExample.cs
, and external editor script, is responsible of that.
Any method meant to be used for render gizmos, in this case, MyCustomOnDrawGizmos
, must be static and take two parameters: the object for which the gizmo is being drawn, and a GizmoType
parameter, which indicates the context in which the gizmo is being drawn. Inside this method, you can use the Gizmos class again to add all the gizmos you want.
Then, to make this work, we used the DrawGizmo
attribute on the method MyCustomOnDrawGizmos
and passed as parameters the GizmoType
we want to use. These are flags that specify scenarios in which the gizmos will be rendered and their behavior.
The GizmoType
method offers five properties you can use:
InSelectionHierarchy
: This draws the gizmo if it is selected or it is a child of the selectedNotInSelectionHierarchy
: This draws the gizmo if it is not selected and also no parent is selectedSelected
: This draws the gizmo if it is selectedActive
: This draws the gizmo if it is active (shown in the inspector)Pickable
: The gizmo to be drawn can be picked from the editor
In the case of MyCustomOnDrawGizmos
, to emulate the behavior of the OnDrawGizmos
method, we used all the gizmo types available. You can use different combinations to achieve different results, for example, let's try to emulate the behavior of OnDrawGizmosSelected
creating a method called MyCustomOnDrawGizmosSelected
inside the DrawGizmoExample
class:
[DrawGizmo(GizmoType.InSelectionHierarchy | GizmoType.Active)] private static void MyCustomOnDrawGizmosSelected( TargetExample targetExample, GizmoType gizmoType) { Gizmos.color = Color.red; Gizmos.DrawWireCube( targetExample.transform.position, Vector3.one); }
Save the changes and wait for Unity to compile the scripts, and then you will see the functionality of the original OnDrawGizmosSelected
method achieved by the MyCustomOnDrawGizmosSelected
method:

Here, we presented an alternative to rendering gizmos in Unity, but in most cases, using the OnDrawGizmos
or OnDrawGizmosSelected
method is enough to create visual aids in the Scene View.
In which scenarios do you want to use this approach? Well, one instance is if you really want to separate what is related to your video game and what is related to the editor stuff. However, most of the times, this approach would be helpful when you don't have access to the implementation of the specific MonoBehaviour
class, so using the method OnDrawGizmos
or OnDrawGizmosSelected
is not possible.
The Gizmos class
The Gizmos
class has all the methods to draw gizmos in the Scene View and we will explore these methods in this section. If you want to reproduce the examples that we will work on here, just add the code snippets inside a MonoBehaviour
class.
Note
This section is an extended version of the official documentation about the Gizmos class. If you want to check the original version visit: http://docs.unity3d.com/ScriptReference/Gizmos.html.
This draws a solid box with center and size.
Example:
// Method signature public static void DrawCube(Vector3 center, Vector3 size); public Vector3 center = Vector3.zero; public Vector3 size = Vector3.one; private void OnDrawGizmos() { Gizmos.DrawCube(center, size); } Result:

This draws a wireframe box with center and size.
Example:
// Method signature public static void DrawWireCube(Vector3 center, Vector3 size); public Vector3 center = Vector3.zero; public Vector3 size = Vector3.one; private void OnDrawGizmos() { Gizmos.DrawWireCube(center, size); } Result:

This draw a solid sphere with center and radius.
Example:
// Method signature public static void DrawSphere(Vector3 center, float radius); public Vector3 center = Vector3.zero; public float radius = 1f; private void OnDrawGizmos() { Gizmos.DrawSphere(center, radius); } Result:

This draws a wireframe sphere with center and radius.
Example:
// Method signature public static void DrawWireSphere(Vector3 center, float radius); public Vector3 center = Vector3.zero; public float radius = 1f; private void OnDrawGizmos() { Gizmos.DrawWireSphere(center, radius); } Result:

This draws a ray , a line starting at some position and going in some direction. This is a good way to visualize ray casting algorithms when you are unsure what the length or direction of a ray is.
Example:
// Method signatures public static void DrawRay(Ray r); // public static void DrawRay(Vector3 from, Vector3 direction); public Vector3 from = Vector3.zero; public Vector3 direction = Vector3.up; private void OnDrawGizmos() { Gizmos.DrawRay(from, direction); } Result:

This draws a line.
Example:
// Method signature public static void DrawLine(Vector3 from, Vector3 to); public Vector3 from = new Vector3(1, 0, 0); public Vector3 to = new Vector3(0, 0, 1); private void OnDrawGizmos() { Gizmos.DrawLine(from, to); } Result:

This draws an icon at the world space Vector3
, in Center
at the specified position. The icon should be a regular image file, such as a PNG or JPG image, which is to be placed in the Assets/Gizmos
folder. Whether or not the icon will be scaled and displayed or hidden is determined in the Gizmos dropdown. Using this method instead of the approach we saw at the beginning of this chapter gives you more control over the icons. For example, you can simulate toggle icons to visually represent boolean values in your code.
To make this code work, we previously created a copy of the asset Game/Art/UI/ UI_LivesAvatar.png
inside a folder called Gizmos
. Note that the icon always faces the Scene View camera.
Example:
// Method signature public static void DrawIcon(Vector3 center, string name, bool allowScaling = true); private void OnDrawGizmos() { Gizmos.DrawIcon( transform.position, "icon.png"); } Result:

This draws the Texture
inside the ScreenRect
method on the Scene View using the XY plane (where the Z coordinate is zero). The values of the texture rectangle are given in scene units.
The optional border values specify an inset from each edge within the rectangle in scene units; the texture is drawn inside the inset rectangle and the edge pixels are repeated outward.
In this example, we pass the reference of the texture as a parameter. You will see that the texture is inverted; this is because the origin of the coordinate system is in the top-left corner.
Example:
// Method signature public static void DrawGUITexture(Rect screenRect, Texture texture, Material mat = null); // public static void DrawGUITexture(Rect screenRect, Texture texture, int leftBorder, int rightBorder, int topBorder, int bottomBorder, Material mat = null); public Rect screenRect = new Rect(0, 0, 100, 100); public Texture theTexture; private void OnDrawGizmos() { if(theTexture != null) { Gizmos.DrawGUITexture(screenRect, theTexture); } } Result:

The DrawFrustrum
draws a camera frustum using the currently set Gizmos.matrix
for its location and rotation (don't worry about the meaning of the Gizmos.matrix
variable, we will talk about that soon).
Example:
// Method signature public static void DrawFrustum(Vector3 center, float fov, float maxRange, float minRange, float aspect); public Vector3 center = Vector3.zero; public float fov = 60; public float maxRange = 1; public float minRange = 3; public float aspect = 1.3f; private void OnDrawGizmos() { Gizmos.DrawFrustum( center, fov, maxRange, minRange, aspect); }

Now, you have all the necessary knowledge to work with gizmos, so apply what you learned to move forward with the development of the Level Creator tool.