Switching between Local and Remote packages in Unity
While developing packages for Unity projects, both personal and at work I always found the process of swapping between using the remote package and switching to a local to make changes a bigger hassle than it needs to.
Default functionality
By default Unity Package Manager supports many different ways to add packages to a project, but I'll focus on the 3 most popular ones:
- Through an NPM registry
- Directly from a Git repository through a Git URL
- Local from your disk
First two options assume a read only workflow - you source the package for use in your project. When having a "Local Package" you can make any modifications you like and distribute the modified package through a NPM Registry or Git repository.
When preparing a package that you plan to share across multiple project, be it for personal purposes or at work it is important to be able to easily swap a remote package to a local to implement required changes or new features before publishing a new version.
To do that in Unity by default means you need to perform several steps:
- Find the desired package in Unity Package Manager
- Remove the package
- Wait for the game to reload domain
- Select: Install Package From Disk
- Navigate on your drive to the correct directory
- Wait for game to reload domain
This doesn't seem like a lot of steps, but they quickly add up if you have to update dependencies in other internal packages. This process has been quite cumbersome and tedious especially for other team members that would like to contribute to the shared packages but didn't feel comfortable swapping between.
Process Alternatives
There are two alternatives that you can employ instead of using the built in GUI.
Package manifest
Updating the UNITY_PROJECT/Packages/manifest.json
manually is one method to skip the multiple domain reload problem.
{
"dependencies": {
"com.cysharp.unitask": "2.5.10",
"com.dcfa_pixels.dragonecs": "https://github.com/DCFApixels/DragonECS.git",
"com.dcfa_pixels.dragonecs-auto_injections": "https://github.com/DCFApixels/DragonECS-AutoInjections.git",
"com.dcfa_pixels.dragonecs-unity": "https://github.com/DCFApixels/DragonECS-Unity.git",
"com.jakubslaby.chirp": "file:../../../../Shared/Chirp",
"com.jakubslaby.executionqueue": "file:../../../../Shared/Shared-ExecutionQueue",
"com.jakubslaby.grapheditor": "file:../../../../Shared/Shared-GraphEditor",
"com.jakubslaby.injectionframework": "file:../../../../Shared/Shared-InjectionFramework",
"com.jakubslaby.localpackageswitcher": "https://github.com/JakubSlaby/LocalPackageSwitcher.git",
"com.jakubslaby.logicgraph": "file:../../../../Shared/Shared-LogicGraph",
"com.unity.cinemachine": "3.1.3",
"com.unity.collab-proxy": "2.7.1",
"com.unity.entities": "1.3.14",
"com.unity.ide.rider": "3.0.36",
"com.unity.ide.visualstudio": "2.0.22",
"com.unity.probuilder": "6.0.5",
"com.unity.render-pipelines.universal": "17.0.4",
"com.unity.test-framework": "1.4.6",
"com.unity.timeline": "1.8.7",
"com.unity.ugui": "2.0.0"
}
You can replace the version strings with a project relative directory path to your package to update specific packages - this method is a little bit tedious and it's best to have a list of paths to replace somewhere if you're taking this route.
This was my initial approach when working on my personal shared packages.
Local NPM Registry
Creating a local NPM Registry and publishing your packages to that registry while developing packages is an alternative that I have used in the past, albeit with Nuget packages but the premise was the same.
The idea is that as you're working on the package in a dedicated project for authoring that package, you publish development versions to your local registry which, when added to your unity project will display available updates.
After you're done with your changes, you can publish the final version to the shared registry.
I won't go in to all of the details of this process as it's quite a complicated setup for most use cases apart from some enterprise processes. If you want to look deeper in to it, you can read more about setting up your local-npm registry.
Local Package Switcher
After a while experimenting with different approaches I decided to create a tool to help me manage this process. As I started building more shared packages and started re-using them across multiple projects I decided to automate the manifest.json
update process - thus Local Package Switcher tool.
The primary purpose was to simplify swapping packages from remote to local, and back to remote after new versions were published.
For that reason I created a window which lists all packages used within your project with clear labelling for the type of a project, along with a list of packages indexed through a path on your disk.
The recommended workflow is that you check out all of your shared package repositories to a single parent directory, which then can be configured as the root of the local packages in the Package Switcher.
Switching to a local package
When selecting a indexed package, it is much simpler now to "Add as package" which will replace the remote package already in the manifest.
Switching to remote package
Switching back to a remote package is equally simple, you can easily find all the latest available versions through the registry.
If you published a new version of the package you might need to click [refresh] to see it.
Indexing of remote versions works based on the same premise as Unity Package Manager, so it will source all added NPM Registries.
If a package was added through a Git link, and the repository will be accessible for the Package Switcher, you will have a option available to re-add the Git URL.
Use cases
I have been using Local Package Manager on all of my private projects for quite some time now, few months ago also deploying it at work with other developers using it to work with shared packages.
I would recommend this or a similar approach if you and/or your team quite often modifies shared packages.