Continuous Integration Woes With Unity 3d

Why does Unity have such lackluster support?  

Unity, despite being one of (if not) the most prolific games frameworks in the industry, still provides a completely lackluster path to automation and CLI support. We can do better as a community.

DISCLAIMER:

I love the Unity framework. It’s my go-to framework for games development, and a cornerstone of the technology my company Immerse builds.

This blog post is a collection of frustrations, and while negative, I think it important to voice.

Because, despite all the fantastic work the Unity team has done recently, their automation story is quite simply an after-thought, and a glaring hole in an otherwise brilliant product.

The Problem

While heading up devops at Immerse, I’ve needed to apply myself to multiple facets of the business.

Now that significant effort has gone into ensuring our platform infrastructure is defined as code, I have turned my sights towards the other aspect of the company: our Unity 3d team and their needs.

Unity is a games platform, and a popular one at that. If you are entering the games development industry, chances are that you’ll be targeting the Unity framework.

It’s not hard to see why too, they boast a number of tantilizing features; out-of-the-box support for Windows, MacOS, Linux, iOS, Android, Playstation 4, Xbox One, Nintendo Switch, and more.

You would assume that such a prolific platform that positions themselves as a “developer friendly” solution would provide strong CLI tools for their customers to build their games.

Unfortunately not.

The Setting

Immerse is the first company I’ve worked at which had game-like build pipelines. Coming from a largely “docker” and “cloud” background, the concept of having Windows build servers was alien to me, even before adding the fact they were running Windows images that had been set up manually.

As the needs of the company grew, it became clear a manual solution was only going to get us so far.

I embarked on a journey to build an automated proof-of-concept for our Unity 3d pipelines.

An investigation into Unity’s SaaS solution “Cloud Build“ exposed the fact they are using MacOS for all build targets. This was certainly a surprise.

A Journey Into the Unknown

The first item to tackle was the manual setup of our Windows build agents.

Ideally, you want to avoid running Windows in the cloud due to both cost and automation difficulties. Unfortunately, Unity3d’s Linux support is almost non-existant.

An investigation into Unity’s SaaS solution “Cloud Build“ exposed the fact they are using MacOS for all build targets.

This was certainly a surprise.

I can only assume they picked MacOS because of its superior scripting ability. Windows has come a long way in the last decade when it comes to automation (powershell) and its recent support of Open Source. It still has a long way to come.

Still, computer games are notoriously “Windows-centric”, and it says a lot that Unity themselves choose to build games targetting the Windows platform in an entirely different OS.

Unfortunately for us, all our internal Unity tooling is built for the Windows OS, and deployed with Amazon Web Services, which still do not provide MacOS machines in the cloud.

Our first foray into Unity automation was a simple Powershell script. While it made sense at the time, Powershell as a tool can be very obtuse when compared to the other scripting tools available.

Once our build scripts became unwieldly, the next foray was to try and split the build scripts into Powershell modules. This quickly proved futile. It was demoralising when the approved solution to a problem in the Microsoft documentation was ten times the length of the same solution in BASH on a Unix system.

Javascript, the Unlikely Hero

Eventually we landed on a fantastic little library called OCLIF. OCLIF is a CLI framework for NodeJS written by the fantastic engineers at heroku.

OCLIF allowed us to develop a CLI tool which encapsulated our complicated build logic, and rid ourselves of Powershell.

By adopting this framework, we got multi-platform essentially for free, and self-updating functionality so we can push script updates to build agents without fuss.

Not only this, OCLIF allows us to avoid the most daunting part of building internal tooling with NodeJS: versioning and dependency hell. OCLIF outputs a self-contained package with the appropriate version of NodeJS as an artifact, and can also be configured to output installers for both Windows and MacOS.

We can now take a tool originally developed to automate our Unity pipelines, and give it to our developers. We can have them automatically “phone home” and check for the newest version of our internal tools without any overhead.

Eventually, our plan is to provide our third-party developers with the very same tools too, and to release it as Open Source soon.

Unity + Scripting: A Lament

We now had a multi-platform, self-installing, and automatically-updating CLI tool for orchestrating our Unity builds. Unfortunately, we still needed to send commands to Unity using its esoteric and clumsy CLI, not to mention licensing issues.

Despite Unity’s overwhelming popularity, its tooling is sorely lacking in terms of operations and automation. While this can be forgiven to some degree (considering how Unity provide their Cloud Build service for simple Unity builds), it is simply inadequate for complex Unity pipelines that involve unit tests, shared packages and multiple simultaneous builds.

Everything is sent to a single Unity.exe binary and depending on which task you wish to run you need to send a complicated amalgamation of flags to the binary.

At least the documentation is relatively well written. It can be found here.

Perhaps the most annoying issues I have come across with automating Unity is around licensing. Due to a UI bug, Unity can not register itself in “no-graphics” mode.

This makes running headless builds impossible without some pretty ugly work-arounds.

Not to mention this, you have to be extremely careful with license management, because if the Unity process crashes it can sometimes leave your license permanently “checked out”. You’ll need to log into the Unity site and deactivate it manually. Ew.

Here is the relevant post in the Unity forums.

There’s a pretty nasty UX/UI fail in Unity3D license activation. But there is a way around it.

First of all, here’s what’s going wrong: When you try to activate Unity3D on a headless server, using the nographics option, it doesn’t initialise the graphics card, but then it sits there waiting for you to click “I accept” on license terms you never can click on.

[…]

unless you are diligent about performing an exec /bin/bash on your container so that you can -returnlicense when updating your container, you are constantly going to be having to visit the Unity3D license management website to de-activate your license

justinlloyd

I like my OUTs to be STD

Another glaring issue is Unity’s logs when building projects from the command line.

On MacOS, by providing the -logfile argument without a value will cause Unity to default to STDOUT. Fine.

While I’d have preferred a more explicit flag, or perhaps even this behaviour by default. It’s at least supported in Mac and Linux. Windows, when given this exact same flag, does not.

Getting Windows to tail a file is a mess. Here is the top StackOverflow answer on the topic. Despite Powershell recently allowing you to add the -Wait flag to the Get-Content command, up until recently the recommended approach was to download an outdated Unix emulation tool to perform an operation which really should be available as part of the Operating System.

Even with the use of the Get-Content -Wait command, you now need to do unnecessary process management in your script to create the log file, begin watching it, create a child process to tail the content before starting the Unity build. Oh, and make sure you clean up those processes after the Unity process quits! Did you remember to return your license as well?

In Conclusion

Unity is a fantastic framework for developing games, for both beginners and seasoned developers. Heck, my favorite computer game, Hearthstone, was built with it!

Despite some fantastic developments in recent versions of Unity: the scriptable render pipeline, the DotNet core upgrade, and Vulcan support (among many others), the build and automation systems remain largely unloved.

While this probably makes perfect business sense, considering their Cloud Build service, it’s a glaring omission to an otherwise fully-featured tool.

The Open Source plugins and tools for Unity CI are sadly lacking too. Both the top Jenkins plugin and the top TeamCity plugin haven’t been updated for 3 years.

I am happy to contribute to an Open Source project if need be, or perhaps if there are tools out there I am not aware of (I would love to hear about them!).

So, to conclude I’d like to speak out to the Unity team, and the Unity community as a whole, and ask for better tools. A standard and supported approach to building Unity projects in the cloud is critical to the future of Unity and its adoption as an enterprise-ready games framework.

Let’s build something together and keep making awesome games. :)

comments powered by Disqus