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