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.