Showing posts with label Unity. Show all posts
Showing posts with label Unity. Show all posts

26 April 2026

Analysis of the book “Unity 6 Editor Tools Essentials – Vol. 1” by Harrison Ferrone

Book cover

The publisher Jettelly is very young, but it is releasing some very interesting titles. They started with books about shading in Unity and Godot, and little by little they have been expanding the range of topics. The wait until they finally released this book felt long. Although they offer the option to buy it in advance at a discount while the chapters are being completed, I don’t have enough patience to read books in fragments. That’s why, even if I lose the discount, I prefer to wait until they are finished. In this case, I was really looking forward to it because in my projects I have found myself countless times needing to develop custom inspectors to make editing my components easier. Over time, I’ve learned how to do this, but Unity’s documentation is not the clearest in the world on the subject, and the technological shift to UI Toolkit has not helped to clarify things. Even so, I still had many doubts about whether I was doing things the right way and, in any case, about the reasons why they had to be done that way.

Fortunately, this book has cleared up many of those doubts. It follows an incremental structure, starting with the simplest editor customizations—Property Drawers and Decorator Drawers—and then gradually increasing in complexity while building on the previous concepts. The process is so gradual and clear that it’s easy to understand the new concepts being introduced. It’s true that I already had some prior knowledge from my earlier tinkering with UI Toolkit, but that actually helped me see that the author hasn’t taken shortcuts; instead, he explains everything from the beginning without skipping anything.

In the end, the book covers the following topics:

  • Property Drawers and Decorator Drawers
  • Attribute Drawers
  • Custom inspectors
  • Custom editors
  • Interface design with UI Builder
  • Creating interfaces to edit ScriptableObjects

The examples presented are very simple, with very limited functionality, but they are more than sufficient to focus on learning how to use Unity’s UI Toolkit without getting distracted by other matters. The code used is also clear and well structured.

It’s also worth appreciating that the book’s layout is pleasant to look at and makes the text easy to read. I don’t know what the printed version is like, because I read it digitally, but at least in this version the illustrations are in color, which is very helpful for distinguishing the details of the interfaces being explained.

In the end, this book has been so enlightening that I want to use it to revisit the custom editors I had already created. I’m sure I’ll be able to simplify them with what I’ve learned here. On the other hand, it has helped me overcome my fear of creating custom interfaces—a task I used to undertake only when necessity left me no alternative, but one that I will undoubtedly approach with much greater peace of mind in the future.

The publisher promises that a second volume of the book will be released. I hope that’s the case, and soon.

26 February 2026

My Impressions of Zenva Courses

Article cover

As many of you know, on my X profile I usually share packs of assets, courses, and books that I find online at good prices. Some time ago, a pack of Zenva courses appeared on Humble Bundle. Based on what I had read in Reddit forums, I warned in the post that the quality of the courses might not be what people expected. To my surprise, Pablo Farias (@pablofariasnew), founder of Zenva, asked me to give his courses a chance because he didn’t believe they deserved that criticism. So I decided to buy the Humble Bundle pack and take several of the courses to see firsthand whether the impression I had formed from Reddit was accurate or not.

The pack I bought contained a lot of Godot courses. It also had some Unity courses I had purchased a long time ago but hadn’t been able to take. In the end, out of all of them, I completed four courses:

  • Tower Defender Game in Godot – Unit 1 – Base Game
  • Tower Defender Game in Godot – Unit 2 – Enhanced Towers
  • Intro to Visual Shaders in Godot 4
  • The Complete Procedural Terrain Generation Unity Course

The first two are just over an hour and a half long, the shader one is barely an hour, while the procedural terrain generation course in Unity spans more than three and a half hours. It’s a sample very similar to what Zenva courses usually are—or at least the ones I’ve obtained in my bundles—generally ranging from one to four or five hours.

To my surprise, I have to say I liked them a lot. All the courses are well structured and cover the scope they promise. They don’t go deep, but what they explain is enough for you to understand what’s going on and practice it confidently in the editor yourself. Even the theoretical concepts, although explained quickly, are conveyed clearly, leaving you with the right idea—perfectly valid for going deeper on your own afterwards.

Of course, they are all in English, but they include subtitles in multiple languages, including Spanish. From what I’ve seen, the Spanish subtitle translation is very good. Even so, I preferred to watch them with English subtitles. The instructors have very clean pronunciation and are easy to understand, so I only used subtitles to check specific words when I got distracted. With subtitles, you’ll have no trouble following the course, no matter your native language. Although it would be great if this platform—and the others that publish courses—started doing what YouTube does and also translated the audio using AI. I don’t know what the production cost is for that, but when you come across one of those, with translated audio, they’re delightful, because the voice even sounds natural.

I tried checking the price of individual courses, but apparently you only have the option to subscribe annually, which gives you access to the entire course catalog. That subscription seems to cost about $59.70 per year. As for whether the price is worth it, I couldn’t really say, because it depends on the value they bring you based on where you are in your learning journey. In my specific case, I’ve already moved past the phase of general introductory courses. I typically go for intermediate courses that cover specific topics. That makes me more selective, so I tend to avoid subscriptions like Zenva’s. But if you’re getting started, it may be a good option to pay that price and have all the courses concentrated and readily available, instead of bouncing around the internet looking for tutorials. You can also do what I did and keep an eye out for the bundles Zenva releases fairly often. That would let you access their content without paying for a subscription you might not get your money’s worth from if you don’t have time to watch courses regularly. If you follow me on X, I’ll let you know when I hear about a new bundle ;-)

To sum up: I liked the Zenva courses. They are good quality. The comments I had read on Reddit are not justified, at least in my opinion. Personally, I’m sure I’ll buy more bundles as they come out. I’m glad I was able to correct my mistake—there aren’t that many quality options to be discarding one incorrectly.

11 February 2026

Creating Packages for Unity

Cover


Reusability is key in software development. Unity is no exception. After you’ve spent some time developing games, sooner or later you’ll come across problems you solved previously and you’ll want to reuse earlier implementations. It may also happen that a colleague needs a component you developed that would help them solve an implementation problem. In both cases, you’ll need to export a set of assets from a previous project so they can be easily imported into another. In this article, we’ll look at some ways to do that.

Direct Copy

The most obvious way to share components is to copy them onto a USB stick or into a ZIP package, and then copy them into the folder tree of the target project.

This approach has the advantage of simplicity. It’s immediate. But it has the disadvantage that it requires you to manually import each component, placing them in the correct folders. If the imported components have dependencies among themselves, it's easy for those dependencies to break if the components are imported into folders different from the originals.

Additionally, you must consider what exactly you want to copy. Many times the original files aren’t enough. When you include a file in a Unity project, the editor generates another file with a .meta extension where it stores parameters used in the process. Including .meta files in the copy is very important if you want to ensure that other developers use the exported assets in the same way you do.

Considering the above, the direct copy method is only advisable for personal copies—things you used in other projects, that you know well and know how they work, and therefore know how to “transplant” into another project. In all other cases, I recommend any of the other methods in this article—they will be cleaner and less error-prone.

Unity Package

The Unity editor allows creating a container package with the assets we choose and their dependencies. We can give this package to another colleague so they can import it from their editor.

Suppose we want to export a set of scripts we use for automated testing.

The scripts we want to export
The scripts we want to export

To create the package, simply right-click on any folder in the Project tab and select “Export as Asset Package...”.

A pop-up window will appear showing the entire folder tree of our project selected. In that window, you must uncheck everything except what you want to include in the exported package.

Export window
Export window

Once done, click Export, choose a name for the generated package (I named mine TestsCommonTools) and where to save it. The resulting file is what we will share with those we want to give our components to.

Generated Unity package
Generated Unity package

Now, let’s consider the perspective of the person who wants to import our components. They just need to drag the package into their Project tab for the import window to open. This window allows them to select which components to import, in case they don’t want them all.

Import window
Import window

When importing, we must keep in mind that the package’s folder structure will be added inside our Assets folder. This may not be a problem if we share the same folder standard used when the package was created—or it may be a problem if we don’t like that folder structure or if we already have folders with the same name but don’t want them “contaminated” with package content.

Folder structure before import
Folder structure before import

Folder structure after import
Folder structure after import

The good thing about Unity Packages is that the editor handles including meta files and various dependencies needed during import. The bad thing, as we’ve seen, is that our folder structure gets “polluted” by the standard used by the package creator. Therefore, this resource is only recommended occasionally or when sharing assets with team members who share your folder structure.

UPM Package

The evolution of Unity Packages is the packages for the Unity Package Manager (UPM). This is the modern and recommended system by Unity for managing packages, libraries, tools, shaders, samples, and functionalities in your projects.

It has many advantages over the previous system:

  • Packages can be installed and uninstalled from the Package Manager, simplifying the process and ensuring that uninstalling leaves no traces.
  • Installation occurs outside the Assets folder, keeping the project clean.
  • The Package Manager handles versions, making updates easy.
  • If a package depends on others, the Package Manager installs them as well.
  • Allows using official repositories (Unity), third-party ones (OpenUPM), or personal ones (GitHub).

To create a UPM package, open the Package Manager via Window > Package Management > Package Manager. In the pop-up, click the “+” icon in the top left and choose “Create package”.

Creating a UPM package via the Package Manager
Creating a UPM package via the Package Manager

A small window appears where you set the package name. In my example, I named it CommonTestTools.

It will then appear in the project’s package list.

Initial appearance of the package
Initial appearance of the package

However, everything shown at this point is provisional—you now need to configure it and add content.

When you click Locate, Unity will take you to the new package folder inside Packages.

Folder structure for our package
Folder structure for our package

As you can see in the previous screenshot, the Package Manager has already created a folder structure for our package. Each of these folders has a specific purpose, but depending on the purpose of our package, you may not need some of them. I’ll explain the function of each one:

  • Documentation: This is where you must place the help documentation for your package, in Markdown or HTML format.
  • Editor: This is where scripts containing editor extensions go, if your package includes any.
  • Runtime: This contains the scripts intended to be used by the game when it is running.
  • Samples: Usage examples of the package.
  • Tests: This folder should contain all tests (both unit tests and integration tests) to verify the correct functionality of your package. This folder, in turn, contains two subfolders: one for Editor and one for Runtime. The Editor one is used for testing utilities that use the Editor API. Aside from that case, the usual thing is to use the Tests/Runtime folder.

Of all the folders mentioned, the contents of the Runtime folder are the only ones that may end up in the final game if they are referenced from compiled code. The rest only make sense for editor use, so they do not contaminate the final game.

My example is somewhat particular, because these are tools meant to be used in the unit tests of a game—so, in principle, it doesn’t make sense for them to go into either the Runtime folder or the Editor folder at the root of the package. However, I found that I couldn’t get visibility of the tool’s classes from my PlayTests unless I placed them in the Runtime folder. So that’s what I ended up doing.

It is very important that we copy files using the Project tab of the Unity editor. This way, the editor will take care of copying not only the files but also their corresponding .meta files, which are essential for the package to function correctly.

Files placed in the package
Files placed in the package

With the package now filled with content, it’s time to define its metadata. This is done by editing the package.json file, located in the root folder of the package. The default contents of the package, as created by the Package Manager, look something like the following:

Default package.json content
Default package.json content

The purpose of the fields is very intuitive:

  • name: This is the full name of the package. It uses the reverse-domain-name format to avoid name collisions with other packages. In reality, the domain you use doesn’t matter as long as the full name doesn’t end up being the same as another package.
  • displayName: This is the short name that will be shown in the Package Manager and by which others will refer to your package.
  • version: The version of your package. It’s advisable to update it as you release new versions of the package, so that the Package Manager detects the change and suggests updates to the user.
  • unity: Version used to generate the package. It serves as an indicator of the minimum version compatible with the package.
  • unityRelease: The minor-version of the previous tag. Only necessary if, for whatever reason, your package only works starting from a specific patch.
  • description: A description of what your package does.
  • dependencies: All the packages that ours depends on to work. The Package Manager will install all those packages before installing ours.
  • author: Our information as the author of the package.
  • changelogUrl: The URL of our changelog. Typically, you would point this to the changelog of our repository.
  • documentationUrl: URL to the package documentation. It’s a good idea to point this to the README or wiki of the package’s repository.
  • licensesUrl: If you host your package on GitHub and have followed good practices, you’ve likely created a license file in your repository. This URL should point to it.

You don’t need to use all the fields. In fact, there are many more. These are the ones that the Package Manager introduces by default when creating the package. But you can use far fewer. For example, a package I created to have a measuring tape rule in the editor has the following package.json:

Contents of the package.json of my unity-measuring-tape package
Contents of the package.json of my unity-measuring-tape package

In the case of the package we are dealing with in our example, the following should be enough for now:

package.json for our example
package.json for our example

If we later upload the source code of our package to GitHub, we can always update the package.json by adding the corresponding fields that point to the repository.

The easiest way is to use the IDE to edit the package.json file, but Unity also lets you configure it from the editor. If you locate the package in the Package Manager, you’ll see that it offers you three buttons: Locate, Export, and Manage. We've already used the Export button. Now it’s time to click the Manage button, which will open a dropdown where you must choose the “Edit Manifest” option. This will open a window like this one in the inspector:

Editing the package.json from the Inspector
Editing the package.json from the Inspector

Once the changes are made, you may need to restart the editor for them to be reflected in the Package Manager.

We’re almost done. We still need to pay some attention to the .asmdef files. The Package Manager creates them in the directories where code may exist and links them to each other. In theory, you shouldn’t need to touch them because the default configuration is usually enough for simple cases. The problem is that the Package Manager names them using our Unity user ID, and it’s usually quite ugly. Even if we later change the package name in the package.json, the Unity editor does not regenerate the .asmdef files. So you have to search for those files in the different folders to rename them—both the file itself in the Project panel and the Name field of the .asmdef file in the inspector. What will happen when you do this is that links pointing to the renamed file from other .asmdef files will break. Therefore, once you’ve renamed all the .asmdef files, you’ll need to do a second pass to rebuild the links in the inspector of each file. It’s not complicated. You’ll see the broken link because the inspector highlights it, so you just need to add a new element to the list of references, choose the file with its new name, and remove the broken link entry. For example, the .asmdef file in the folder where I left the classes of my package ended up like this:

The .asmdef file of my package
The .asmdef file of my package

Generally speaking, keeping the “Auto Referenced” field checked can help avoid having to manually reference the asmdef from the asmdefs of the code that wants to use the tool’s classes. However, there may be cases in which, even with that, you still need to reference the package’s asmdef to use it from your code.

Another important thing to keep in mind is that the “Root Namespace” parameter of the asmdef must match the namespace used by the classes in our package. Otherwise, you might find that the user of the package cannot see its classes from their code.

With the above in place, the code of the project where we created the package will already be able to see its classes by importing through the namespace we defined in the asmdef.

Importing the classes of our package
Importing the classes of our package

Sharing a UPM package

With the above, we’ll now have a functional package, but it will only be usable in the project where we created it. The logical thing is that we want to export it so we can use it in other projects.

The most immediate way to export a UPM package is as a .tgz file. We just need to right-click the package folder inside Packages and choose “Export as UPM package…”. This will create a .tgz file that includes our package. Whoever receives the file will only need to import it from the Package Manager by clicking the “+” icon in the upper-left corner and choosing “Install package from tarball…”, which allows them to select the tgz file to load it.

The problem with the above option is that we’d be forced to send new tgz files to our users with each new version. A more versatile alternative is to upload the code of our package to GitHub. It’s enough for the root of the repository to be at the same level as the package.json file. By doing this, our users will be able to install the package using the “Install package from git URL…” option and providing the repository URL. The advantage of this approach is that if we change the version recorded in the package.json, the Package Manager will detect the change and notify the user that they can update.

Finally, you can also share your package by uploading it to a public repository like OpenUPM, to maximize its visibility. However, OpenUPM is used outside of the Package Manager, so it may deserve a separate article on its own.

Conclusion

In this article we have reviewed all the ways to reuse your Unity assets, either between your own projects or by sharing them with other developers. This should prevent you from being forced to “reinvent the wheel” by reimplementing components already present in other projects or manually copying their code, components, and dependencies between projects.

26 January 2026

Cost Assignment to 2D Navigation Using Tilemaps in Unity

Article Cover

 Recently, we saw how it could be done in Godot to assign costs to the tiles of a tilemap so that those costs could be used in navigation algorithms. Unity has its own particular way of doing this, but, as in the case of Godot, it will depend on whether we want to use those costs with a custom navigation algorithm or with the engine’s navmesh navigation. Let’s see how it’s done in both cases.

In this article, I will assume you have read my previous article on how to assign data to tiles in Unity. Here, we will take advantage of some of the things I explained there.

Costs for Custom Navigation Algorithms

When using custom algorithms, such as our own implementations of Dijkstra or A*, we will want to access the transit cost of a given cell. We could choose to assign a game object to the tile, as we saw in the mentioned article, and have that game object include a script with the cost data. But that would be excessively complex, since we would need to assign a collider to that game object to detect it with a volume sensor when applying the sensor to the cell’s position. No, it is definitely much better in this case to use the other approach we saw in that article: creating a class that inherits from Tile, adding a cost field, and using it to draw cells that have a higher transit cost. It would be a class like the following:

Implementation of Our Custom Tile
Implementation of Our Custom Tile

The class simply has a field to input the tile’s cost through the inspector (line 13).

To be able to query the tile’s cost from our code, line 18 includes a read-only property.

Essentially, a Tile is a kind of Scriptable Object, so it is extended in a very similar way. As with Scriptable Objects, we can decorate them with the [CreateAssetMenu] attribute (line 6) so that the editor shows a menu entry to create instances of this tile. In this case, I created it so that it appears under the path “Scriptable Objects > CoutyardTile.”

Creating a tile of this type would be similar to creating a Scriptable Object. We would right-click in the folder where we want to save the tile and choose the option configured in the previous paragraph.

Creating an Instance of the Custom Tile
Creating an Instance of the Custom Tile

Once the tile instance is created, we can give it the appearance we want and assign a value to the cost field. My example is visually very simple. I gave a water tile a cost of 80.

Water Tile Configuration
Water Tile Configuration

In my example, I created four types of walkable tiles:

  • Ground: Gray. It has no transit difficulties, so its cost is 1.
  • Grass: Green. Cost is 2.
  • Sand: Orange. Cost is 4.
  • Water: Blue. Cost is 80.

Also, remember to assign a sprite. Otherwise, no matter what color value you assign, the tile will not be visible. I simply used a white sprite so that the color applies over it.

Once the custom tiles are created, you just need to switch the Tile Palette tab to edit mode and drag the tiles onto it from their folder.

My Tile Palette
My Tile Palette

With that, we can now draw the “slow” areas of the scene. Mine is bordered by impassable walls and has internal black walls.

My Example Scene
My Example Scene

Once the costs are associated with their tiles, how would we retrieve them? We just need a method like the one in the following screenshot.

Method to Retrieve the Cost Associated with Each Tile
Method to Retrieve the Cost Associated with Each Tile

The method assumes there is a global variable walkableTilemap with a reference to the tilemap where all the walkable tiles of the scene are drawn. With that reference, we use its WorldToCell() method (line 319) to convert the global position of the scene we want to analyze into the tilemap coordinate corresponding to that position. Then, we use the GetTile() method (line 320) to retrieve the tile associated with those coordinates. Once we have the tile, we can access its Cost property to return its value (line 321).

Mesh Navigation with Slow Zones

The previous method is useful if we want our agents to move through the tilemap using our own navigation algorithms. However, for performance or simplicity, we might prefer to use the mesh navigation system that comes with the engine. The problem in Unity is that its mesh navigation system is designed for 3D and does not work as-is for 2D scenes like the example. Fortunately, there is a free module called NavMeshPlus that solves this problem for 2D and is used almost the same way as Unity’s native 3D mesh navigation. I explained its installation and use in a previous article where I covered 2D navigation in Unity. Based on what was said in that article, here we will explain how to implement slow zones that are considered by the pathfinding algorithm.

So, assuming you have read that article and followed its instructions to generate a NavMesh, now you need to create a type of walkable area for each type of tile we have. To do this, go to Window > AI > Navigation. The window that opens has two tabs: Agents and Areas. As you can imagine, the one we are interested in is Areas. There, the default areas of Unity’s navigation system are already defined: Walkable, Not Walkable, and Jump. Notice that the right column defines the transit cost of each area. This is where we will define the area types we saw in the previous section, with their corresponding costs.

Configuration of Walkable Areas and Their Costs
Configuration of Walkable Areas and Their Costs

Once the areas are defined, we need to associate each tile with the area type it belongs to. If you followed the 2D navigation article in Unity, you will have included a NavigationModifier component in the same GameObject as the tilemap containing the walkable tiles. Uncheck the Override Area field.

Then, add a NavigationModifierTilemap component. It has a list called Tile Modifiers. Add one element to that list for each type of walkable tile you want to define. Then drag each tile resource from its folder onto its respective element. This way, you can define which area each tile belongs to.

In my case, the configuration looks like this:

Assigning Tiles to Each Area
Assigning Tiles to Each Area

Now we need to regenerate the NavMesh so that it includes the transit costs we just defined for each area.

However, before clicking Bake in the NavigationSurface component, you must ensure you change the Render Meshes parameter. If it is configured as we left it in the 2D navigation article, this parameter will have the value Physics Colliders, so it will only consider objects with physical colliders to “cut” the walkable area of the NavMesh. We need to change it to Render Meshes. This way, it will use the tiles we defined as walkable (which are visual, renderable components) as a reference to shape the NavMesh. Once changed, we can click Bake.

My NavMesh Configuration
My NavMesh Configuration

If you make the editor show the NavMesh, you will see that it has several colors to indicate the different areas it detected.

NavMesh Generated with Different Areas
NavMesh Generated with Different Areas

Now you have your NavMesh, but if you run the scene at this point, your agent will probably stay still. To make it move, you must ensure it is configured to use the walkable areas. This is done by setting the AreaMask parameter of the NavMeshAgent so that it includes all the zones where walking is allowed.

NavMeshAgent Configuration
NavMeshAgent Configuration

In the screenshot above, you can see that I configured the AreaMask to include the areas Ground, Grass, Water, and Sand.

Now, if you run the game, your agent should be able to move through the walkable zones, always choosing the path with the lowest cost.

You will probably notice that, although the agent chooses the lowest-cost route, it will not slow down if it is forced to cross a slow zone. To make that happen, you will need to call the static method NavMesh.SamplePosition() from the agent’s Update() to analyze its current position at all times. This method returns an output parameter of type AI.NavMeshHit, which has a mask property with the area associated with that point. Normally, a point is associated with only one area, but even so, the mask property is (as its name suggests) a 32-bit mask—one bit for each navigation area Unity allows to be defined. You just need to find the index of the bit set to 1 to know the navigation area of the analyzed point. With that index, you can call another static method, NavMesh.GetAreaCost(), to obtain the cost of the area we are on and act accordingly (usually by dividing the agent’s speed by the area’s cost).

Conclusion

With this, we have reviewed the two options we have in Unity to navigate a scene built with a tilemap:

  1. Use cost data associated with tiles to perform classic pathfinding with our implementation of Dijkstra or with the A* implementation integrated in Godot.
  2. Use mesh navigation with slow zones.

If your scenes are moderately sized, you can afford option 2 if, for some reason, you prefer to model the scene with a graph.

However, as soon as your scene grows in size, you will prefer option 3. It offers the best performance and is the easiest to configure.

08 December 2025

Dictionary Serialization in Unity

In development, serialization consists of changing the format of an object in memory so that it can be stored on disk.

One of the most popular serialization formats is JSON, a text standard that allows you to save data in files following a key-based structure very similar to Python dictionaries.

When you serialize an object—an instance of a class—into JSON, what you do is select the key data of the object and convert them into fields and values in a JSON file. What are the key data of the object? It’s the minimum subset of data that allows you to reconstruct the object in the state you need. That reconstruction process is called deserialization and is the inverse of the other: you read the file and use its content to create an instance of the class with a state as close as possible to the original.

Unity is constantly serializing and deserializing. When we use the inspector to assign a value to a public field of a MonoBehaviour, Unity serializes that value into its own format so that you have it in the inspector the next time you start the editor. By default, Unity performs this process with all public fields of MonoBehaviours and ScriptableObjects, but you can also force it on private fields if you define them preceded by the [SerializeField] attribute. This attribute also allows us to edit private fields in the inspector. Without it, the inspector only shows public fields. Be careful—this does not mean you should use this attribute on all private fields of your MonoBehaviour. Its use only makes sense for those fields that contain base values for your class, that is, those you would configure in the inspector before the game runs. Preceding a calculated field with [SerializeField] would make no sense unless you wanted to preserve that calculated value for a later execution.

That was precisely the case that led me to write this article. I’m writing a class that allows me to analyze a scenario and generate a graph representing all its walkable areas. The graph creation process is irrelevant here, but my idea was for the graph to be generated during development, saved, and loaded at runtime. In other words, I wanted my graph to be saved in a ScriptableObject. One of the components of my graph is based on a dictionary whose keys are the integer coordinates of a rectangular grid, and whose values are nodes with links to neighboring nodes. And the problem arises because Unity does not know how to serialize dictionaries. It can serialize lists, but not dictionaries. That’s why you can edit lists in the inspector but not dictionaries.

Dictionaries are one of the most common data structures in development, so I’m not the first to encounter this problem. It’s so common that other engines, like Godot, proudly support dictionary serialization.

How can you work around this problem? Well, you can create a class that outwardly behaves like a dictionary but internally is based on two lists—one for keys and one for values. During serialization, those two lists would be saved since Unity can process lists. During deserialization, those two lists would be read and used to create an internal dictionary in memory from which the class would offer its functionality to the rest of the game components. This solution is perfectly legitimate, but by now it’s so widely used that it has already been included in a multi-type serialization tool (not just dictionaries) called Odin Serializer. So it would be like reinventing the wheel. If, despite that, you want to do it yourself, Odin’s own page explains how, although it warns that the devil is in the details and that there can be odd situations when editing prefabs that may require significantly refining the initial implementation. That path has already been traveled by the Odin Serializer team, so I’ll explain how to use it.

Odin Serializer Logo
Odin Serializer Logo

Odin Serializer is an open-source and free tool. You can download it from its website as a Unity package containing all the source code files to serialize everything Odin supports. Odin Serializer is just the free entry point to a set of tools that are paid. From the free serializer, they offer a tool to create custom inspectors and another to find errors in your projects. Both are extremely powerful. The first lets you skip the UI Toolkit step when implementing convenient and efficient inspectors. The second detects the most common errors in Unity development and provides a set of custom attributes to add semantics to your fields and detect cases where their values do not match their semantics (or even prevent you from entering those values). Although for this article I only needed the free serializer, I recommend checking out their other tools and prices. They are one-time purchases and very affordable for an indie developer.

The download page only asks for your game’s base namespace to customize the namespaces of all included code files. The package includes an OdinSerializer folder containing the rest of the tool’s folders. You need to place that folder inside your game’s Scripts folder.

Once imported into your Scripts folder, OdinSerializer provides a set of specialized classes that inherit from the usual Unity ones:

  • SerializedBehaviour
  • SerializedComponent
  • SerializedMonoBehaviour
  • SerializedNetworkBehaviour
  • SerializedScriptableObject
  • SerializedStateMachineBehaviour
  • SerializedUnityObject

You just need to replace the Unity class your component inherits from with the equivalent Odin class so that Odin handles serialization for types Unity doesn’t support. All dictionaries, for example, will be serialized by Odin without any extra effort, so if you edit their content in the editor, it will persist between executions.

However, keep in mind that even if you serialize the dictionary content with Odin, Unity’s inspector will still be unable to display it. So don’t be surprised if your public dictionary still doesn’t appear in the inspector. For that, you’d need to install Odin Inspector, which is one of the paid components. Without that component, any modification of the serialized dictionary content from the editor would need to come from custom scripts executed in the editor. That was precisely my case, as I configured a button in my graph component’s inspector to generate the grid when clicked and save the values created in the component’s internal dictionary. If I modify the scenario, I just click the button again to regenerate the graph’s grid.

Odin Serializer should cover most of your needs for serializing dictionaries, but it’s not always that simple. Although I had used Odin Serializer in simpler projects, I couldn’t get it to work in the project that inspired this article. It generated my graph correctly when I clicked the button, but the graph wasn’t there on the next editor restart or level reload. For whatever reason, the dictionary containing the graph wasn’t saved when the level was stored. I’ve thought about it a lot and don’t know if it’s because I have several levels of nested dictionaries, or because the scripts containing those dictionaries are placed in nested prefabs. It could also be because my component uses a custom editor, adding another level of indirection to data serialization. Whatever the reason, I ultimately had no choice but to implement my own serialization—the very manual serialization I warned against if Odin could solve your problem. This time, I had to reinvent the wheel, though I was lucky to rely on the method explained on Odin Serializer’s own page. I’ll tell you how, in case it helps you out sometime.

As I said before, the key is to create a class that inherits from Dictionary to preserve its functionality. To give this new class the ability to be serialized in Unity, it must implement the ISerializationCallbackReceiver interface. When Unity receives the command to serialize a class—because the field of that class is public or marked with the [SerializeField] attribute—it expects that class to implement the ISerializationCallbackReceiver interface. This interface consists of two methods: OnBeforeSerialize(), where we implement how we want to save our object’s information during serialization, and OnAfterDeserialize(), where we implement how to recover the saved information to reconstruct the object’s state.

The class I created is based on two lists: one for the dictionary’s keys and another for its values. These lists will store the data to be preserved since Unity natively serializes lists.

Fields of my customizable dictionary
Fields of my customizable dictionary

Now the interface implementation. First, for the serialization process.

Serialization process of my customizable dictionary
Serialization process of my customizable dictionary

When Unity calls OnBeforeSerialize(), it’s time to save the object’s state information. To do this, I clear the previous content of the lists (lines 42 and 43) and then refill them with the updated list of keys and values from the dictionary. When the class instance closes, the dictionary’s content will be lost, but the lists will have been serialized along with the rest of the scene’s information.

Now the inverse process. The scene opens again, and Unity calls the OnAfterDeserialize() methods of all its objects so they can restore their previous state from the deserialized information.

Deserialization process of my customizable dictionary
Deserialization process of my customizable dictionary

As shown in the listing, since the lists were serialized, they will remain intact when OnAfterDeserialize() is called, allowing us to use them to restore the dictionary’s entries. In lines 34 to 36, you can see that I iterate through both lists to regenerate the entries of the class’s internal dictionary.

Now comes something important. Unity cannot serialize generic types, and the newly created class (UnitySerializedDictionary) is generic. The solution is to specialize the generic class for each use case. Once concretized, the resulting class can be serialized by Unity. That’s why I have a separate static file with the different concretized versions of the generic dictionary.

Concrete classes derived from the generic one
Specialized classes derived from the generic one

To avoid any temptation to use the generic class directly without concretizing it, it’s best to mark it as abstract.

These specialized versions are the ones we can use in our code, with the assurance that Unity will preserve their content between executions.

Using the concretized dictionary
Using the specialized dictionary


MonoBehaviour that uses our concretized and serializable dictionary
MonoBehaviour that uses our specialized and serializable dictionary

I hope you found this useful and that it helps you feel confident about using dictionaries in your components.

28 October 2025

Implementation of a tool to measure distances in Unity scenes

Article cover

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
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
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
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
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
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
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

Assets/Editor/DrawMeasuringTape.cs
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
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

Assets/Editor/DrawMeasuringTape.cs
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
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
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.