This tutorial provides an easy-to-understand introduction on how to run your mobile gaming CI/CD in the cloud using CircleCI, GameCI, and Unity.

What is Unity?

Unity is a game development platform created by Unity Technologies that enables developers to build games in C# for the following platform types:

  • Desktop PCs (Windows/macOS)
  • Mobile devices (iOS / Android)
  • Video game consoles (PlayStation / Xbox / Nintendo Switch)

Unity provides a library of ready-made resources and tools via the Unity Asset Store.

Unity has been adopted as a game development environment for projects of all scales and has a high market share, especially in mobile gaming.

Implementing mobile CI/CD in the cloud with CircleCI and GameCI

Developing consistently high quality games requires the use of good CI/CD tooling and practices. Mobile games in particular require high-performance CI/CD tools because of their frequent updates and large user bases. But the unique and complex requirements of mobile game building, testing, and distribution make configuring mobile CI/CD from scratch a daunting task. These requirements include:

  • Correctly and consistently configuring the build environment (especially for iOS!)
  • Installing the Unity Editor
  • Activating the Unity license
  • Preparing the build script

As a result, many mobile development teams fall into one or more anti-patterns, including:

  • Manually building and testing in a local development environment
  • Manually testing, building, and distributing that application
  • Performing CI/CD tasks in a quickly configurable but difficult-to-scale environment, such as self-hosted Jenkins

GameCI is an open source tool that was created to solve these common mobile game development CI/CD challenges. It boasts the following features:

  • CI/CD environment with Unity Editor pre-installed (Docker image)
  • Automated Unity license activation
  • Ability to run builds for any platform supported by Unity

GameCI integrates with CircleCI by using an orb. With the GameCI Unity orb, you can easily create CI/CD pipelines for Unity projects without writing a complex, custom build script from scratch.

In this tutorial, we will demonstrate how to set up a Unity mobile game CI/CD pipeline with CircleCI and GameCI.

Prerequisites

You can find all the code used in this tutorial in the sample repository.

This tutorial assumes you have already set up CircleCI with your VCS. If you have not, sign up for a free account and follow our quickstart guide to get your CI/CD pipeline up and running. Once you have your CircleCI account configured, you can proceed with the steps outlined in this tutorial.

Activating your Unity license

Before we can build a Unity project, we need to activate a Unity license. In this tutorial, we will be using a Unity Personal license. (Please note that the activation method differs depending on the license used. Refer to the following GameCI document for more details: Activation with GameCI).

To obtain an activated Unity license file, we first need to generate a Unity activation file. Let’s create a CircleCI configuration file in our project repo at the path .circleci/config.yml. Next, we’ll add the code shown below, and then commit our change. CircleCI will run the create-unity-activation-file job.

version: 2.1

orbs:
  unity: gameci/unity@1.6.0

workflows:
 create-unity-activation-file:
    jobs:
      - unity/create-activation-file 

After the job completes, we can download the activation file (Unity.alf) from the ARTIFACTS tab.

2023-10-02-create-activiation-file

Upload the activation file to Unity’s manual activation page to activate your Unity license.

2023-10-02-manual-activation

After the activation is complete, we can download the Unity license file (Unity_v20XX.x.ulf). We will store this file as a secret in a CircleCI context so that we can use it in our Unity jobs. First, we will create a context in CircleCI. In our new context, we’ll create the following variables with their corresponding values:

Building and distributing (Android)

The next step after activating our Unity license is to run a build. First, let’s run a build for the Android platform. Below, I am doing so in a job named build-android:

  build-android:
    executor:
      name: unity/ubuntu
      editor_version: 2021.3.26f1
      resource_class: xlarge
      target_platform: android
    steps:
      - checkout
      - unity/prepare-env
      - unity/build:
          build-target: Android
          compress: false
          store-artifacts: false
          persist-to-workspace: true

Let’s break down the build-android job in detail.

The executor uses the Unity orb to select an appropriate execution environment. resource_class determines the size of the build machine. We can choose from the Docker resource classes listed in the relevant CircleCI documentation.

Building Unity projects often requires provisioning expensive machines with large amounts of CPU and memory. CircleCI enables you to keep costs low by spinning these machines up only for the duration of the build job. CircleCI also aids you in rightsizing your build machines by logging each job’s resource usage in a set of intuitive graphs available in the Insights dashboard, shown below.

2023-10-02-resource-utilization-graph

Continuing our breakdown of the build-android job, the steps are an ordered list of tasks that make up the job. This job is doing the following:

  1. Checking out the source code (checkout)
  2. Preparing the license file we saved in our context (unity/prepare-env)
  3. Running a build for Android (unity/build)

This will create a binary (APK) for Android on the build machine. You could upload this APK as-is to the Google Play Store to release it, distribute it via Firebase App Distribution, or use another distribution method of your choice.

In our case, we will use fastlane to distribute our APK to Firebase App Distribution. Here is the relevant CircleCI config:

  beta-android:
    executor:
      name: ruby/default
      tag: 3.2.2
    steps:
      - checkout
      - attach_workspace:
          at: .
      - ruby/install-deps:
          key: android
      - run: bundle exec fastlane android beta

And here is the related fastlane config (/fastlane/Fastfile)

platform :android do
  lane :beta do
    firebase_app_distribution(
      app: "Firebase App ID",
      apk_path: "Builds/Android/Android.apk",
    )
  end
end

And that’s it! You’ve successfully built and distributed your Android game on the cloud!

Building and distributing (iOS)

Next, let’s run a build for iOS. Below we have a CircleCI job called export-ios:

 export-ios:
    executor:
      name: unity/ubuntu
      editor_version: 2021.3.26f1
      resource_class: xlarge
      target_platform: ios
    steps:
      - checkout
      - unity/prepare-env
      - unity/build:
          build-target: iOS
          compress: false
          store-artifacts: false
          persist-to-workspace: true

This export-ios job uses the same Unity orb as our Android job above. However, unlike the Android job, the unity/build job does not produce a binary (IPA). Instead, an Xcode project like the one below is generated on the build machine:

2023-10-02-xcode-project

In order to create our iOS binary (IPA), we must sign and build this Xcode project in a macOS environment. We will do this in a second job, build-and-beta-ios, shown below:

build-and-beta-ios:
  macos:
    xcode: 13.4.1
  resource_class: macos.m1.large.gen1
  steps:
    - checkout
    - attach_workspace:
        at: .
    - ruby/install-deps:
        key: ios
    - run: bundle exec fastlane ios beta 

Because this job requires a macOS environment, we are using a CircleCI M1 macOS VM.

In the final run step in the build-and-beta-ios job, we are using fastlane to do the following:

  • Perform code signing using fastlane match
  • Build the Xcode project that we generated
  • Distribute our app to Firebase App Distribution

Here is the related fastlane configuration code:

platform :ios do
  before_all do
    setup_circle_ci
  end

  lane :beta do
    match(type: "adhoc")

    update_code_signing_settings(
      use_automatic_signing: true,
      path: "Builds/iOS/iOS/Unity-iPhone.xcodeproj"
    )

    update_code_signing_settings(
      use_automatic_signing: false,
      team_id: ENV["sigh_com.tadashi0713.CircleCIUnityMobileDemo_adhoc_team-id"],
      code_sign_identity: 'iPhone Distribution',
      targets: 'Unity-iPhone',
      path: "Builds/iOS/iOS/Unity-iPhone.xcodeproj",
      profile_name: ENV["sigh_com.tadashi0713.CircleCIUnityMobileDemo_adhoc_profile-name"],
      profile_uuid: ENV["sigh_com.tadashi0713.CircleCIUnityMobileDemo_adhoc"]
    )

    build_app(
      project: "Builds/iOS/iOS/Unity-iPhone.xcodeproj",
      scheme: "Unity-iPhone",
      configuration: "Debug",
      export_method: "ad-hoc",
      xcargs: "-allowProvisioningUpdates",
      export_options: {
        compileBitcode: false
      }
    )

    firebase_app_distribution(
      app: "Firebase App ID"
    )
  end
end

Conclusion

Using CircleCI and GameCI together greatly simplifies the complex task of running Unity builds in a cloud-native environment. Using this approach you can:

  • Increase developer productivity via scalable, on-demand, pre-configured build environments
  • Reduce tool maintenance overhead by using GameCI to eliminate the need for complex, customized build scripts
  • Easily rightsize your build machines by checking resource usage with CircleCI’s resource utilization graphs

If you are struggling to automate your Unity builds in an elegant and maintainable fashion, or if you find yourself spending too much and budget configuring, maintaining, and troubleshooting your self-hosted CI/CD tooling, CircleCI and GameCI are definitely worth a shot. Sign up for your free account and get started today.