For some unknown reason, Unity lacks a built-in tool to measure distances in scenes and prefabs. Godot does have one, but Unity doesn’t, even though it’s extremely useful both for building your scenes and for evaluating functionality tests. That’s why the Unity Asset Store is full of assets offering this functionality… for a price. Recently, I needed to measure distances in a Unity project I’m working on and considered buying one of these assets, but then I thought, “It can’t be that hard to implement myself.” I gave it a try, and it turned out to be very simple. In this article, I’ll explain how. First, what we want to achieve, and then we’ll move on to the implementation details.
A measuring rule is conceptually simple. It’s based on two independent variables, position A and position B, whose difference gives us the dependent variable we want to calculate: the distance. That’s the minimum. From there, we can add visual enhancements or manipulation features. For this article’s example, I wanted a visual tool that could be moved around the scene, with two grab points to adjust the positions to be measured using the mouse.
![]() |
| The rule to implement |
I also wanted to be able to have multiple rules simultaneously, to deploy them in different locations in the scene, and have them visible at all times without needing to be selected. The visual appearance of the rules had to be configurable so they could stand out appropriately in different scenes. So I needed to configure the line color, thickness, and the dimensions of the rule’s ends. Additionally, the rule had to provide the global positions of its ends to allow precise placement. With all these conditions, I wanted the rule to have an inspector like the one shown in the following figure.
![]() |
| Tool Inspector |
Considering all the above, we can move on to the implementation details. By the way, all the code we’ll see is available in the GitHub repository of the tool. There, it’s explained how to install the tool easily using Unity’s Package Manager. In another article, I’ll explain how I packaged the tool so it can be installed from the repository via the Package Manager.
Using the repository as a reference, the first file to explain is Assets/Runtime/Scripts/MeasuringTape.cs. This file is the MonoBehaviour component that will be mounted on a Transform to be instantiated in the scene, and it contains the tool’s data model. It couldn’t be simpler:
![]() |
| Assets/Runtime/Scripts/MeasuringTape.cs |
The fields in lines 8 and 9 are the two independent variables we mentioned earlier, the positions of points A and B. It’s important to note that these are relative positions to the GameObject on which this component is mounted. I wanted it this way to be able to move the rule around the scene all at once, without having to move one end and then the other. It’s also important to give both positions a default value different from zero. Otherwise, their visual handles will coincide with the GameObject’s Transform, and we won’t be able to manipulate them.
The rest of the fields refer to the tool’s visual configuration:
- color: The color of the rule’s lines.
- thickness: The thickness of those lines.
- endWidth: The length of the transverse lines at the rule’s ends.
- endAlignment: A value between -1 and +1 to shift the transverse lines at the ends to one side or the other of the rule. This is an interesting option if we want to place multiple rules in the scene like dimension lines.
- textSize: Font size used to display the measured distance.
- textDistance: Distance from the rule to the text showing the measured distance. It can take negative values, in which case the text appears on the other side of the rule.
Besides the above, MeasuringTape.cs offers two properties with the global positions of the rule’s ends:
![]() |
| Assets/Runtime/Scripts/MeasuringTape.cs |
To have two visual handles to set the values of localPositionA and localPositionB by dragging the mouse, we’ll use Unity’s Handles. Handles are visual controls that respond to user interaction, such as clicks, drags, and rotations. Every time you move an object in the scene, you’re manipulating the Handle representing the object’s position.
To show custom Handles, you need to create a script in the Editor folder with a class that inherits from UnityEditor.Editor. This class must be decorated with the [CustomEditor] attribute to indicate which MonoBehaviour the Handles are associated with. In our example, this script is in Assets/Editor/DrawMeasuringTape.cs.
It’s very important to highlight that any script like this, which uses classes from the UnityEditor namespace, must be placed in a folder named Editor. If you put the script directly in the Scripts folder alongside the MonoBehaviours, you won’t be able to compile the game. Some people place the Editor folder inside Scripts. I prefer to keep them well separated.
![]() |
| Assets/Editor/DrawMeasuringTape.cs |
Look at line 6. The [CustomEditor] attribute must be passed the type of MonoBehaviour it’s associated with. It’s a way of telling the editor: “Every time you encounter a MonoBehaviour of this type, represent it in the inspector and scene as defined here.”
It’s important to note that I left this script in the default namespace. I’m not sure why, but when I tried to use a custom namespace, the drawing of Gizmos in the DrawTapeGizmos() method failed, which we’ll see later.
Handle configuration is done in the OnSceneGUI() method, which belongs to the UnityEditor.Editor class.
![]() |
| Assets/Editor/DrawMeasuringTape.cs |
Handle management within OnSceneGUI() always follows the same structure. In fact, it wouldn’t be a bad idea to save a template, because you always end up doing the same thing.
You start by retrieving a reference to the MonoBehaviour associated with the Handles. In OnSceneGUI(), it’s common to use the target field of UnityEditor.Editor. That field has the reference we’re looking for, although you need to cast it (line 160) to get the exact type. This reference will be very useful for reading and setting the properties and fields of the original MonoBehaviour.
Then comes the segment where you display your Handles and retrieve their values. This segment starts with a call to EditorGUI.BeginChangeCheck() (line 162). This call detects whether the user has modified any editor control between this call and its corresponding EditorGUI.EndChangeCheck() (line 172). If the latter detects any change in the editor controls, the values are retrieved and saved in the original MonoBehaviour (lines 176 and 177). These changes are recorded in the Undo/Redo system with a call to Undo.RecordObject() (line 175). This method records all changes made to the object passed as a parameter from the moment of the call. The second parameter’s text identifies the change in the history.
Between lines 165 and 170 is where the Handles are created. There are many types, each with its own appearance and manipulation method. The ones I used here are the simplest. PositionHandles simply present a coordinate axis that we can move around the scene. If moved, the Handle returns the new position, which in our case is stored in the variables positionAHandle (line 165) and positionBHandle (line 168). These variables are used to update the fields of the original MonoBehaviour (lines 176 and 177).
In simple cases, it’s quite common to see implementations that include the object’s Gizmo visual representation in the same OnSceneGUI() method, usually after the EditorGUI.EndChangeCheck() block. I’ve done this often, but it has a couple of drawbacks. The first is that you mix the visual control management and Gizmo representation logic in the same method, which lengthens and complicates the implementation. The second problem is that all visual representation included in that method will only be shown when the source object is selected. This last point was a blocker for me, as I wanted the rule’s representation to remain visible even when another object was selected.
The alternative I discovered is to decorate a static method with the [DrawGizmo] attribute. This attribute marks a method as responsible for drawing an object’s Gizmos. On one hand, it allows you to concentrate all visual representation logic in it, leaving OnSceneGUI() for Handle management; and on the other hand, depending on the parameters passed to the attribute, you can define when the Gizmos should be shown.
![]() |
| Assets/Editor/DrawMeasuringTape.cs |
In the example, as seen in line 108, I passed the necessary flags to the attribute so that the Gizmos are shown both when selected and when not.
To draw the Gizmos, I used the drawing functions from the Handles library. There’s some overlap with what the Gizmos library offers, which can also be used for drawing. The advantage of Handles is that it lets you set the line thickness, while Gizmos only allows drawing with a thickness of 1 pixel. Based on that, the rest of the method’s lines are very similar to when we draw with Gizmos.
In line 112, I set the color of the lines to be drawn, while in line 113 I draw the line between the two ends of the measuring tool. Note that the third parameter passed to the DrawLine() method is precisely the thickness of the line to be drawn.
To highlight the ends and their visual handles, in lines 116 and 121 I draw a circle around the ends. To finish these, I drew perpendicular lines to the main one. If I wanted my rule to only measure in 2D game scenes, I could have calculated the perpendicular to the main line, but since I want to use it in 3D environments, I need the lines crossing the ends to also be perpendicular to the camera’s viewing direction. Otherwise, there could be moments when the camera moves and the end lines disappear because they’re aligned with the viewing direction. To calculate the perpendicular to two vectors, we use the cross product, which I calculate in line 133. With that done, the perpendicular vector helps calculate the half-length of the ends (line 146) and to draw them, incorporating the endAlignment shift (lines 147 and 150).
The perpendicular vector is also very useful for separating the text from the main line, starting from its center. That center is calculated in line 138, and from it the separation is calculated in line 142. Once we have the location, I draw the text in line 143. The “F2” in the ToString() call ensures the distance is shown with two decimal places.
At this point, I already have a measuring rule whose ends I can manipulate and that is drawn as shown in the image at the beginning of the article. What remains to be clarified is how I made the inspector show the global position in read-only fields. Although the MeasuringTape MonoBehaviour has two properties that provide the global position of the ends, Unity cannot display properties in the inspector (which is a shame because Godot can). To display these properties as read-only, you need to use a custom inspector. To do this, you must create a class that inherits from UnityEditor.Editor, just like the one we already used for Handles and visual representation, but to represent custom inspectors you must use the CreateInspectorGUI() method.
This method is based on reading the serialized values of the original object’s fields to be represented in the inspector. These values are passed to the class through a field called serializedObject. What I usually do is use the OnEnable() method to get references to each of the original object’s fields.
![]() |
| Assets/Editor/DrawMeasuringTape.cs |
To get each reference, you must call the serializedObject.FindProperty() method and pass it a string with the name of the original object’s field you’re interested in. I don’t like using strings because it’s easy to make mistakes, but that’s how it works.
Once we have the references to the original object’s fields, we can use them in CreateInspectorGUI().
![]() |
| Assets/Editor/DrawMeasuringTape.cs |
In line 38, the panel where all the inspector fields will be represented is created. I didn’t want to make a big deal about the field order, so I just organized them in the classic vertical layout.
Remember when in OnSceneGUI() I got a reference to the original MonoBehaviour using the target field? Well, you might be tempted to do the same in this method, but Unity’s documentation advises against it and requires that in this method you use serializedObject.targetObject, as seen in line 47. I don’t quite understand the reason for this peculiarity, but I preferred to follow the documentation’s recommendation rather than go against it and run into strange problems later.
Since I’m fine with the default visualization of the first two fields (the local positions), I simply created two default fields and populated them with the original object’s values (lines 50 and 52). Then, I added the fields to the panel (lines 51 and 53).
In my particular case, I usually use custom inspectors when I want to show or hide fields depending on the value of a previous one (e.g., a boolean). In those situations, I like to place the variable fields (those that can be shown or hidden) on a separate panel from the main one. That separate panel is created in line 56 and added to the main one in line 57. Then, in line 60, we pass that panel to the UpdateGlobalPositionFields() method to create the read-only fields with the global positions. In a moment, we’ll see how UpdateGlobalPositionFields() works internally, but to not lose the thread of CreateInspectorGUI(), let’s finish reviewing how it works.
Once the fields where the global positions will be shown are added, I want them to stay updated. To do this, I linked UpdatePositionFields() to the local position fields so it runs every time the local position values change (lines 64 to 67). This will update the global positions.
Since I'm fine with the default visualization for the remaining fields, I simply add their respective PropertyField to the main panel (lines 70 to 75).
Note that CreateInspectorGUI() must return the main panel so the editor can display it in the inspector. That’s what I do in line 77.
As promised, let’s now look at what happens inside UpdateGlobalPositionFields().
![]() |
| Assets/Editor/DrawMeasuringTape.cs |
Remember I just mentioned that this method receives, as its first parameter, the subpanel where the fields we want to manually update are drawn—either to insert values or to show/hide them.
The first thing I do with that subpanel, in line 91, is clear its contents to start with a blank canvas.
In line 93, I define that I want to stack the elements I add to the subpanel in a descending column.
Finally, I add the fields with the global positions (lines 96 and 101). In both cases, I do the same: I create a field specialized in displaying positional values (Vector3Field). Then, I assign values to those positional fields using the MeasuringTape properties that return the respective global positions (lines 97 and 102). Since I want the fields to be read-only, and therefore not manually editable, I disable them with SetEnabled(false) (lines 98 and 103). Lastly, I add the newly created and configured fields to the subpanel (lines 99 and 104).
With this, we now have a fully functional distance measuring rule… although rather inconvenient. To use it, we’d have to create an empty GameObject in the scene and then add the MeasuringTape component to it. Certainly tedious. We could simplify things by creating a prefab that only includes the MeasuringTape component. That way, we’d just drag the prefab into the hierarchy whenever we want a rule in the scene. However, that would require us to search for the prefab in our folders every time we need to measure something. That’s why I created an entry in Unity’s main editor menu to instantiate the prefab whenever needed. Let’s see how I did it.
To add entries to Unity’s main menu, you simply decorate a static method with the [MenuItem] attribute. This attribute receives the path, within the main menu, of our entry. What happens then is that every time that menu entry is clicked, the static method is called.
In my case, I implemented the static method in the file Assets/Editor/InstanceMeasuringTape.cs. Note that the [MenuItem] attribute is in the UnityEditor namespace, so any script using it must be placed in the Editor folder.
![]() |
| Assets/Editor/InstanceMeasuringTape.cs |
The first thing the method does, in line 15, is call LoadAssetAtPath() to load the prefab with MeasuringTape we mentioned earlier. The path to search will depend on how you run the tool. If you copy-paste all the code directly into your project, you’ll need to specify the path where you place the prefab (with Assets as the root of the path). In my case, I configured everything to run as a package downloaded from GitHub using Unity’s Package Manager (I’ll explain how to do this in another article). So the path must be that of the folder where the package is installed. Packages are installed in the Packages folder, so my path starts from that root, as shown in line 9.
Once the prefab is loaded as a GameObject, it can be instantiated in the scene using the method PrefabUtility.InstantiatePrefab() (line 23). The result of this call is that the prefab instance appears in the scene, hanging from the root of the hierarchy.
Finally, since it’s logical that you’ll want to operate on the newly created rule, line 32 selects that rule in the hierarchy so you don’t have to search for it to click on it.
And that’s it. I hope you found it interesting and that it gave you some ideas for creating your own tools within Unity. As I mentioned earlier in this article, I hope to find time soon to explain how to publish your tools on GitHub so they can be loaded and kept updated using Unity’s Package Manager.












