How and why you should create your own package (Unity’s Package Manager)
Hello GameDev! Today I want to show you how and why you should create your own packages. So let’s start right away
My name is Vadim A.K.A Vadimskyi. I like to think of myself as a game developer. Although the large chunk of my professional time is spent writing utility functions, helper classes or even frameworks to boost my productivity /
And every time I stumble across the same problem. How do I efficiently reuse my utilities and frameworks? Sometimes I’ll fix a bug or add new feature. And then I need to update all other projects that depend on this reusable code. But being productive programmer, I just don’t have time to manually fix this type of issues. I have deadlines to meet and coffee to drink. Maybe there is a solution for this problem…
You can ask: but what about Asset Store? And sure, you can try to upload your framework to asset store. But if all you got is couple utility classes, you’ll never pass asset verification process. Not to mention that updating assets can be problem in itself
Well, starting from version 2018.3 Unity introduce to game developers the concept of Packages along with the Package Manager UI. Unity-team also emphasizes many times that package will be the future format for all asset store content. And in my opinion, now is the perfect time to dive in on package creation. Let’s see it in action.
Why don’t we open Unity and create simple blank project? From the structure point of view Package is a self-contained Unity-project although, naming convention is a little bit different
When you download packages through the Package Manager window, unlike content from Asset Store, the package content doesn’t get embedded directly to your project Assets folder but gets referenced inside the project by Assembly Definition file. The later implies that, you get rid of all the garbage from asset store and end up with a much cleaner project structure.
Every project will have this Packages folder and by default, an empty project comes with predefined packages. If you open manifest.json file, you will find a list off all required packages with the corresponding version. You probably notice that this folder only have manifest.json file. The actual packages are downloaded and stored inside Library\PackageCache folder. You can not edit package files inside this folder.
Go back to Unity and open Package Manager window. All packages that you can find with the search option are going to be from Unity Technologies and they are hosted on Unity private registry.
As of time this video is created Unity does not allow to host third party packages on their private repository luckily there are other options. For now, let’s focus on creating local package so that we can upload it from disk.
Throughout many years of developing android applications I come up with a handful of useful native utilities that help me handle android API that I can’t access from Unity. But copying scripts from project to project became king of tedious. So now is the perfect time to incapsulate these utilities inside a package
The main reason for packaging these plugins is being able to edit source code from single point of access and ability to update them with a single click inside Package Manager window
Let’s navigate back to our empty project Packages folder and create root folder for our package
The folder name should be the same as your package name and package names in Unity are following the specific convention. Separating by dots, type “com” then company or author name and finally, the project name.
The single most important file is package.json. Let’s create it firs. Unity provide a useful template, so that you don’t need to type every field manually.
Let’s copy this template to our newly created package.json file and figure out what each line does.
The name field is just a package name I described previously. Next comes package version number, the number describes, left to right, the package major version, minor version and patch version. These values must respect Semantic Versioning. I provide a link in the description for more info on this topic. But for now, let’s move onThe name field is just a package name I described previously. Next comes package version number, the number describes, left to right, the package major version, minor version and patch version. These values must respect Semantic Versioning. I provide a link in the description for more info on this topic. But for now, let’s move on. Next field is displayName. The name your put here will show up inside Unity editor packages folder and in the Package Manager Ui. Package description will only show inside Package Manager window when you click on the package name. In the unity field you need to specify the oldest supported Unity version. Optionally, you can even specify Unity release number. The package that is not compatible with your version of unity will not appear in the Package Manager window.The name field is just a package name I described previously. Next comes package version number, the number describes, left to right, the package major version, minor version and patch version. These values must respect Semantic Versioning. I provide a link in the description for more info on this topic. But for now, let’s move on
The URL fields all can be pointing to your personal site. If the Package Manager can’t reach the URL location it will instead use specific local files of which I will talk about soon.
The dependency list can contain links to other packages that your package is rely on. Other fields are not that relevant for this video so, you can research about them on your own. As usual, I will be providing links in the description.
For now, we are done with package.json file. If your open Package Manager window you will be able to see your local package. So far so good. Let’s start to add in some content.
The runtime scripts are going to folder called Runtime and if you want to add some editor scripts you still need to create Editor folder. All this naming is optional, but it is nice to follow the same convention especially if you want to contribute to packages ecosystem.
[Copy and paste plugins to Runtime folder] I find it very useful to provide samples whatever I can even if I do not plan on publishing and distributing my package. This way I can easily refresh my memory on how to actually use package content.
Looking back at Package Manager window you can see that some packages have this neat samples section with a handy button to upload them to your project. It is actually not that hard to achieve the same in our package.
Create a folder with the name “Samples~”. The character we add in the end is indication for Unity to ignore all contents inside this folder and does not track them with the .meta files. Next step is to add new field called “samples” to the package.json file. It is a json list that can hold as many samples as you like. Now you can open Unity again and see that package samples section has been updated.
And last but not least we need to create assembly definition for our Runtime scripts. Let’s do just that. Congratulations we created our first package! You can now reference it from any Unity project on your local computer.
But what if you want to share your package to fellow game developers or to your colleagues within a company? Well, it is time to learn how to distribute packages through git url . So, let’s dive into this topic.
I assume that you already familiar with the concept of version control systems and Git in particular. If not, then GIT GUD, literally, and come back when you are. Get yourself Git Desktop or Git Bash console utility and choose your preferred version-control repository provider. In my case it’s Github. Make sure you include README, gitignore and LICENSE files. [Create repository from GitDesktop app]. Rename LICENSE file to LICENSE.md so that Unity can open it from Package Manager window. Also create CHANGELOG.md file that will be containing all changes we committed to this repository. As with the license.md file, you can also access changelog file from Package Manager window.
The crucial step now is to open Unity project one more time and let Unity Engine generate missing .meta files for our newly created content. Go ahead and publish repository to github. Then copy repository https link and try to fetch it inside Package Manager window for different Unity project.
Well, isn’t that was easy? But, there are a couple of concerns i need to address. First, every time we committed some changes to the package we want to have the ability to update package from Package Manager UI. And for this to be possible, you need to increment package version number inside package.json file. Second, it would be nice to reflect changes to the package in the CHANGELOG file.
Both these points are sharing the same issue: every change to the package you commit require manually keeping track of package version and changelog. And if something is manual it also means that this something is error prone. So why don’t we automate the process of package release?
There is actually a github workflow plugin that was created specifically for this purpose. It is called Semantic-Release. And what it does is it automates the whole package release workflow including: determining the next version number, generating the release notes, and publishing the package.\
But how does the plugin know what version number to choose and what release notes to generate? Well, it all comes from commit messages structure. By default, semantic-release uses Angular Commit Message Conventions. Basically, every commit message should consist of message type and short summary.
Message type is determining what version part should be incremented. If your commit message contains type “fix” then the “patch” portion of version number will be incremented. If type is a feature, then the minor version and if it is a performance or BREAKING CHANGE then the major version number should be incremented. Let’s hook up this plugin to our package.
First, create the folder named “.github” inside your package root folder. Than in “.github” folder, create another folder named “workflows”. In this folder create “release.yml” file. Go back to root folder and create “.releaserc.json” file. You can copy and paste the content of this files from my git-repository for this tutorial. Link will be in the description.
Basically, every time there is a commit to master branch the “release.yml” script will fire up and scan commit message using semantic-release plugins. If it finds commit message type to be valid it will automatically generate new release for your package.
Let’s push these changes to the github repository. Then go ahead and change some scripts inside package folder so that we have something to commit for our first release. This time the commit message will have a “fix” type. Right after you push the commit if you open github page for your package and go to actions tab you will see that “release.yml” action is in progress.
Couple of seconds later and we got our first official release. You may wonder why release number was not incremented. Well the semantic-release plugin is keeping track of version number from your previous releases. And sins this was the first release that you create version number will always star from first.
Let’s make another change to package content and create new release. Just follow previous steps.
Now, imagine putting all this time and effort in creating your beautiful package to than realize that first: you cannot update this package from Package Manager window at least without a hurdle. And second: you cannot configure you package to de dependent on your other packages. And for me it was a deal breaker. Us of time I’m recording this video Unity have very limited support for importing packages directly from git links.Let’s make another change to package content and create new release. Just follow previous steps.
Basically, after you add package Unity saves package hash to the “packages-lock.json” file. You need to manually delete this file every time you want to update your package. As far as dependencies let’s think for a second when do we even want them? Picture this: You want to create a series of UI animation packages. I bet you a dollar that they will share utility logic or maybe low-level animation logic or even graphic assets. Heck, maybe they will share my android native plugins package.
You probably guess by now that you really don’t want to duplicate any type of logic. The whole purpose of this video was to encapsulate shared logic inside the package. And sure, you can add your dependency first to “manifest.json” file and then add dependency package name to the “package.json” file of your consumer package. But if you think about it this really come out as not an option. Just imagine every time you need to import some package you also need to manually find and import all package dependencies.
There are two solutions: One is Hosting your personal private upm repository. And second is by using opensource project called open-upm. If your goal is to setup nice package ecosystem within your company or local network than you should stick with deploying you own private repository. But if you just want to share your package to whoever might be interesting in it. That open-upm is the gold-mine that we need. Let’s briefly talk about how it works:
Unity supports the scoped registry that allow developers to setup 3rd-party registries to host custom packages. OpenUPM provides a public UPM registry, and a group of services and tools to support it. The downside of the scoped registry is that you need to maintain the scope field of the manifest.json file to make it work. It’s not a problem when working with a single namespace. But it’s a challenge to work with a public registry with various namespaces. The dependency chain makes the issue worse. For example, the package-a depends on b, and b depends on c. It’s a headache for a human to resolve all these dependencies to fill the scope field manually. To remove the pain to install a 3rd-party package, people from OpenUPM team created a command-line tool called openupm-cli to maintain the manifest file. It can add, remove, and search packages in a terminal app, like Bash, Git-Bash, CMD, or PowerShell. When Unity detects the change of the manifest file, it will resolve it and install or remove packages for you.
Let’s go ahead and install openupm-cli. Just make sure you have npm installed first. Open you preferred command line utility. And type “npm install -g openupm-cli”. Installation is done. Let’s upload our git package to openupm registry. Follow to “openupm.com/packages/add” fill out your package repository name and then fill the rest of the fields. Click Verify Package.
Right after, you will be prompted to submit pull request. The process goes like this: You need to fork the openupm github project then submit configuration file with information about you package and then create a pull request to the main openupm repository. If this is first package you add to openupm the verification process may take some time. Probably couple hours. The next pull requests will be handled automatically and much quicker.
When your pull request is accepted openupm server will automatically add new package to the registry. You can see the results following the link to your package. Go to “openupm.com/packages/your.package.name” there you should be able to see information about your package.
Let’s install your package using openupm-cli. Open you preferred command line tool and navigate to Unity project folder. And why don’t we fetch information about our package just to be sure it is ready to be used. Type “openupm search your.package.name” you should be able to see nice table with your package name, version and author information. And finally type “openupm add your.package.name”. Was not that hard actually.
If you now open manifest.json file inside packages folder you will see information about openupm scoped registry and associated packages. Now let’s make some changes to our package, commit and push to github. After new release created Upm server will automatically update package inside registry. For me it usually takes about 10-20 minutes. Go ahead and update the package from Package Manager window. Works perfectly!
Now i want to create new package and use our Native plugins package as a dependency. To create new package just follow the same routine we used previously. To add dependency open package.json file and add new field inside dependency object. The key would be our dependency package name value is package version. Now go ahead and upload new package to upm. And that’s it.
So today we learned how to create, update, maintain and publish your own Unity package. There are a lot of great package creators and infrastructure to support them but still there is a lot of room to grow. I strongly believe that this new feature will empower more developers to share their ideas and build up healthy ecosystem. If you are a Unity enthusiast like me don’t forget to like, commend and subscribe for more awesome content. And remember: be creative, work hard and code smart. See ya later, gamedev.