This post is written by Mina Pêcheux
This post has been updated in July 2022 to introduce some fixes to the codemagic.yaml file in the sample project, and to reflect that you no longer need to contact Codemagic solution engineers to set up Unity machines for you: You start using Unity on Codemagic right away, as it is preinstalled on Mac, Linux and Windows build machines.
TL;DR: Even though Unity does not default to Git for version control, it is possible to use Git for Unity projects. It also gives you the ability to use CI/CD services such as Codemagic to build, test, and publish your Unity app.
More often than not, your code projects will start small, but they will grow larger as time passes… and you’ll eventually be left with hundreds or even thousands of files to manage, with lots of versions and re-updates.
That’s when version control and versioning systems come in handy: They allow you to quickly implement solid management of your files and their history. Not to mention that if you aren’t working alone on the project, there’s no chance you won’t be using some kind of version control for collaboration.
But sadly, some very common tools are not seamlessly hookable with standard frameworks from the game dev community — for example, Git and Unity don’t always mix well by default. Don’t worry, though: We can remedy that!
And after we’re all set up with version control, we can even leverage tools like Codemagic to automate the “post-processing” and publishing of our Unity projects. :)
So, in this article, we’ll discuss:
- What it means to version your code and why you should do it.
- The different versioning solutions that exist today.
- How to properly set up a Unity project for versioning with Git and remote Git service providers.
- How we can use Codemagic to easily automate building, validation, and publishing of Unity projects.
Learn why you should use a CI/CD for your Unity projects from this article.
Ready? Let’s go!
What is code versioning, and why is it important?
Keeping track and trying out new ideas…
At the heart of a versioning system is, naturally, the idea of constantly updating and accessing a fully detailed log of the previous actions taken in the project. Navigating through this history allows you to go back to an older version to check for differences, restore saved data, or even test something “on the side” without endangering the main production code (using the system of branches).
Versioning files is pretty natural, of course — you can do it by copy-pasting your project’s folder on your desktop and renaming it with a .bkp
suffix… but tools like Git make versioning easier and way more robust!
Sharing code
Another essential aspect of versioning systems is the ability to team up and collaborate. Because you have a powerful management tool, you can safely share your modifications via the use of remote services (like the well-known GitHub, GitLab, or Bitbucket websites, for example) to have an up-to-date online copy of your project.
These Git-based services offer you plenty of tools, ranging from just a list of to-dos to full-fledged reporting or auto-deploys. But their primary goal is to help with synchronizing the data and facilitating the exchange of ideas!
Note: Storing your Git project remotely also gives you even more redundancy and can save you a lot of trouble if you ever lose your local data since you can just download it back from the internet! ;)
What about Git?
As explained on their website:
Git is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency.
Introduced by the creator of Linux, Linus Torvalds, and first released in 2005, Git is one of the most famous versioning tools. It follows the open-source and free philosophy and relies on peer-to-peer exchanges to share and synchronize data between collaborators. Basically, each collaborator has their own copy of the whole repository stored locally on their computer. In addition, this local folder is cleverly linked to the shared remote repository so that you can ensure that your local copy is up to date.
This distributed mode allows you to work on the repo (locally) even if you don’t have access to the network — you’ll just have to wait a while before you can share your new modifications with others!
Git also makes it easy to manage lots of branches, and it now has plenty of GUI clients to perform your commits visually if you don’t feel at ease with the command line.
Note: There are even games to help you learn Git, such as Learn Git Branching! :)
A quick note: Alternatives to Git
Now, to be perfectly honest, Git is sometimes considered imperfect for large-scale projects with a lot of binary files. That’s why some in the game dev industry prefer other solutions, like Perforce (Helix Core) or Plastic SCM. And Unity is tuned to work with those two versioning solutions rather than Git by default.
In contrast to Git, where each user has their own copy of the project, Perforce is a centralized versioning system. This means you have a single source of truth, but the tool will have to work a bit harder to compute differences and merges if two devs want to collaborate on the same piece of code. Also, as explained in this article (by Perforce, full disclosure…), Perforce is specifically designed to natively handle large binary files, whereas Git can get a bit lost with them (but we’ll see in the next section how we can cope with that!).
Plastic SCM is another concurrent solution that is now an official Unity product and insists on making repo visualization easy for all. It also tries to combine the best of both worlds by providing centralized and distributed features. However, it’s still pretty limited to the world of video games, and other devs might not have heard about it. And most importantly, many CI/CD tools won’t let you use Plastic SCM-based repositories.
All in all, choosing a versioning system requires that you make some tradeoffs and assess what interactions are most critical to you, as no system is intrinsically better than the others. But it’s true that Git has slowly risen to be the de facto standard in the industry, making it a commonly used version control system among many developers.
Setting up a Unity project for Git versioning
So, Unity doesn’t default to Git versioning. But this doesn’t mean we can’t set up a Git repository to manage our next Unity project: Let’s see how we can prepare clean and sound Git versioning for Unity in a couple of steps!
Adding the right .gitignore
to your Unity project
As is the case in many Git repositories, it’s essential to properly tell Git which files you want to version and which ones you want to ignore. There’s no need to keep track of the changes in your temporary log files, which Unity creates automatically!
A classic Unity .gitignore
file ignores the following:
- The “noisy” temporary files: Unity generates hundreds of temporary files when you open or close the editor, and it may mix various file types to handle the multiple build platforms
- The auto-generated files (like the Gradle builds and the Visual Studio project file)
- The packed assets
- Some other directories that can quickly get heavy, like
Recordings/
orMemoryCaptures/
To get a full example .gitignore
file, you can check out this sample. 🚀
If you plan on using a CI/CD solution to build and publish your project, you should also add files with secrets to your .gitignore
so that your private keys do not end up in a public repository. For example, you should add your .keystore
file for publishing to Google Play — otherwise, your credentials will be available to everyone and forever committed to the project’s history!
Configuring the Unity editor for use with Git
If you’ve ever looked at your Unity projects in a file browser, you might have noticed that the software automatically creates .meta
files for each of your assets. Those files contain metadata that describes the object in your project, as well as its dependencies on other assets.
It is usually best to commit these .meta
files so that other collaborators don’t get broken references. To do this, you need to open the Project Settings panel in Unity. Then, do the following:
- Go to the “Editor” tab to use plain text asset serialization in order to avoid unresolvable merge conflicts
- Go to the “Version Control” tab to use plain text serialization by turning the “Visible Meta Files” option on.
Note: Don’t forget to save your changes by explicitly going to the File > Save Project
menu! :)
Using Git LFS for your binaries
As game designers and developers, you and our teammates will frequently need to handle large binary files (images, movies, 3D assets, etc.). Even though Git tries its best to manage those kinds of files, it is initially meant to treat line-by-line differences, so constantly adding, removing, or modifying big binaries can make it go a bit crazy.
Git Large File Storage, or Git LFS, is a command line extension specifically designed to solve this issue. It uses a client written in Go and allows you to store your large files robustly and with faster uploads (pushes) or downloads (pulls).
The installation depends on your OS system, but usually, you can either download the binary and run the install.sh
script or compile it yourself. (Check out the link above for more info.) Once it’s installed, you can check that everything works fine by running:
git lfs env
This will tell you whether the command line tools are installed properly, and it will also indicate what endpoint LFS is pointing to. If you’re working with a remote Git repo, it should start with your HTTPS or SSH link — for example:
Endpoint=https://gitlab.com/MinaPecheux/sample-project.git/info/lfs
You can now easily track or untrack specific file types with Git LFS. Let’s say you want all of your .psd
(the default Photoshop file format) files to be tracked by LFS; in that case, you need to do two things:
- Set Git LFS to manage this file type with
git lfs track "*.psd"
- Commit this setup in the
.gitattributes
file
git add .gitattributes
git commit -m "track *.psd files using Git LFS"
And now, whenever you push a commit with some modifications in a .psd
file into your repo, you’ll see that part of the push message tells you about LFS:
$ git push origin main
Uploading LFS objects: 100% (1/1), 810 B, 1.2 KB/s
# ...
To https://github.com/git-lfs/git-lfs-test
67fcf6a..47b2002 main -> main
If you want to quickly check which file patterns you’re currently tracking with LFS, simply run:
git lfs ls-files
And by the way: If you want to add many file patterns at once, you can simply edit and commit the .gitattributes
file directly! :)
Other benefits of using Git for Unity
As I’ve mentioned in another article I wrote recently on Codemagic’s blog, automation is key to building solid and long-term projects. And now that we’ve introduced Git versioning, we can take advantage of Codemagic CI/CD to automate testing, building, and publishing processes for our Unity project. Codemagic can work with GitLab, GitHub, Bitbucket, or any other Git-based repository. The main requirement here is that it should be some kind of Git repository.
Why use Codemagic? It’s a fast and user-friendly tool that lets you build for all kinds of target platforms without hassle, run unit or integration tests, and even publish directly to common online stores, such as Google Play or the App Store.
So, in just a few configuration steps, you’ll be able to link up your nice Git repository to Codemagic, set up your work environment, and design your CI/CD pipeline steps with a single YAML file.
It is also possible to publish your Unity game in a breeze.
All you need to do is add a codemagic.yaml
file at the root of your Git project, similar to the following one, and set up a Codemagic account with the right privileges.
Here is the Codemagic configuration file for a sample Unity Pong clone with unit testing that I’ve shared on GitHub 🚀:
workflows:
unity-mac-workflow:
name: Unity Mac Workflow
environment:
groups:
# Add the group environment variables in Codemagic UI (either in Application/Team variables) - https://docs.codemagic.io/variables/environment-variable-groups/
- unity # <-- (Includes UNITY_SERIAL, UNITY_USERNAME, UNITY_PASSWORD)
vars:
UNITY_BIN: ${UNITY_HOME}/Contents/MacOS/Unity
scripts:
- name: Activate License
script: $UNITY_BIN -batchmode -quit -logFile -serial ${UNITY_SERIAL?} -username ${UNITY_USERNAME?} -password ${UNITY_PASSWORD?}
- name: Run Unit Tests
script: $UNITY_BIN -batchmode -executeMethod Runner.RunUnitTests -logFile -nographics -projectPath .
- name: Build
script: $UNITY_BIN -batchmode -quit -logFile -projectPath . -executeMethod BuildScript.BuildMac -nographics
artifacts:
- "mac/UnitTestingPong.app"
publishing:
scripts:
- name: Deactivate License
script: $UNITY_BIN -batchmode -quit -returnlicense -nographics</pre>
Here, we have a workflow that includes three steps: unit testing, building, and publishing. The configuration file also enables us to activate and deactivate the Unity license for publishing, as well as to re-integrate the right work environment.
Note: Be sure to check out Codemagic’s docs for additional info on setting up a Unity build workflow!
This environment is defined by adding a few environment variables in your Codemagic app settings:
And that’s it! Now, you can use Codemagic’s online UI to start builds from anywhere in the world, and you’ll get your artifacts (the build game, optional logs, etc.) back after a little while. :)
Conclusion
Git is honestly my go-to version control tool, and I was a bit surprised when I first discovered Unity’s issues with it. But these few tricks allow me to continue using my favorite versioning system to manage my game projects and to rely on Codemagic to wrap up the entire thing in a nice all-in-one workflow. :)
What about you: What’s your preferred version control tool?
I really hope you enjoyed this tutorial — and of course, don’t hesitate to share your ideas for other DevOps topics you’d like me to make Unity tutorials on!
Finally, if you’re interested in taking a look at the codemagic.yaml
file or other configuration files used in the sample project that we discuss in this post, you can find them on GitHub.
Mina Pêcheux is a freelance full-stack web & game developer. She’s also passionate about computer graphics, music, data science, and other topics! She runs her own blog. Alternatively, you can find her on Twitter.