Automating Unity Mobile Build Systems

Automating the pipeline for Unity in iOS & Android projects

Hike
Hike Blog

--

By Rahul Sharma, Senior SDET, Hike

At Hike, we believe in bringing joy to people through our breakthrough and innovative products by challenging the status quo and doing the unthinkable. We have a strong desire to see a better world where human consciousness is evolving in the right direction.

Taking on the goal of channeling India’s competitive spirit, we introduced Rush by Hike at the starting of this year — a skill-based bite-sized gaming platform where players can use their skills to play, compete & win real cash.

In line with this, a lot of inputs went into thoughtfully launching Rush. One such input was the fundamental task to integrate Unity code into Android & iOS native projects. Using the export option in Unity editor — our dev teams integrated Android and iOS compatible code in our native apps, but it was still a manual process which was time-consuming as well as error-prone.

The Challenge

The challenge we faced before we developed this automation process was that the process of exporting iOS & Android projects from the Unity project and integrating it with native apps involves a lot many manual steps. In order to build a native app with unity app integration, the developer or the tester had to perform the below steps:

  1. Check out the Github repository for the Unity project and native app project.
  2. Open the Unity project in Unity Editor.
  3. Modify player settings and quality settings (refer to Fig 1 & Fig 2).
  4. Switch platform in build settings and set desired export settings (refer to Fig 3).
  5. Export the project.
  6. Copy required files in the native app project.
  7. Build native apps.

This entire manual process was very time-consuming and could lead to mistakes being made.

Fig 1: Player Settings of Unity Editor
Fig 2: Quality Settings of Unity Editor
Fig 3: Export/Build Interface of Unity Editor

How we achieved it

We used Jenkins pipelines for setting up an automation workflow for performing pre-export and post-export steps. Now, we just needed to automate the “export” step.

Export to iOS project

To export the Unity project to iOS project, we need to create a C# script ExportScript.cs in Unity Project under the directory Assets/Editor/.

// Assets/Editor/ExportScript.csusing System;using System.Collections;using System.Collections.Generic;using UnityEditor;using UnityEngine;// Add all active scenes to an arraystatic string[] SCENES = FindEnabledEditorScenes ();// Target Directory to export the projectstatic string TARGET_DIR =Environment.GetEnvironmentVariable(“EXPORT_DIR”);[MenuItem (“Custom/CI/Build iOS”)]static void ExportIOSBuild (){BuildOptions iosBuildOptions =Environment.GetEnvironmentVariable(“IOS_BUILD_OPTIONS”);EditorUserBuildSettings.SwitchActiveBuildTarget (BuildTarget.iOS);BuildPipeline.BuildPlayer (SCENES, TARGET_DIR, BuildTarget.iOS,iosBuildOptions);}// Method which searches for all active scenesprivate static string[] FindEnabledEditorScenes (){List<string> EditorScenes = new List<string> ();foreach (EditorBuildSettingsScene scene inEditorBuildSettings.scenes) {if (!scene.enabled)continue;EditorScenes.Add (scene.path);}return EditorScenes.ToArray ();}

In the above script, we are first saving all active scenes in an array. Then in the method ExportIOSBuild, we are setting some customizable build options, which we will discuss in the Modifying Unity Settings section. Finally, we are switching active targets to iOS and building the project.

Here we have used two environment variables:

  • The first environment variable EXPORT_DIR is the directory where you want the exported project.
  • The second environment variable IOS_BUILD_OPTIONS is set to have export settings, which we will be discussing in the Modifying Unity export settings section.

After the script is placed in the desired location, we need to run a bash command to execute the script.

unityVersion=2019.3.5f1unityFolder=~/unityProjectexport EXPORT_DIR=~/unityExported/export IOS_BUILD_OPTIONS=BuildOptions.AcceptExternalModificationsToPlayer/Applications/Unity/Hub/Editor/${unityVersion}/Unity.app/Contents/MacOS/Unity -batchmode -nographics -silent-crashes -projectpath${unityFolder} -logfile ~/Unity.log -executeMethod ExportScript.ExportIOSBuild -quit

Export to android project

Similar to the iOS project, we need a similar C# script in Assets/Editor/.

//Assets/Editor/ExportScript.csusing System;using System.Collections;using System.Collections.Generic;using UnityEditor;using UnityEngine;// Add all active scenes to an arraystatic string[] SCENES = FindEnabledEditorScenes ();// Target Directory to export the projectstatic string TARGET_DIR = Environment.GetEnvironmentVariable(“EXPORT_DIR”);[MenuItem (“Custom/CI/Build Android”)]static void ExportAndroidBuild (){BuildOptions androidBuildOptions = Environment.GetEnvironmentVariable(“ANDROID_BUILD_OPTIONS”);EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTarget.Android);BuildPipeline.BuildPlayer (SCENES, TARGET_DIR, BuildTarget.Android, androidBuildOptions);}// Method which searches for all active scenesprivate static string[] FindEnabledEditorScenes (){List<string> EditorScenes = new List<string> ();foreach (EditorBuildSettingsScene scene in EditorBuildSettings.scenes) {if (!scene.enabled)continue;EditorScenes.Add (scene.path);}return EditorScenes.ToArray ();}

Now we can execute the script for android in a similar way.

unityVersion=2019.3.5f1unityFolder=~/unityProjectexport EXPORT_DIR=~/unityExported/export ANDROID_BUILD_OPTIONS=BuildOptions.AcceptExternalModificationsToPlayer /Applications/Unity/Hub/Editor/${unityVersion}/Unity.app/Contents/MacOS/Unity -batchmode -nographics -silent-crashes -projectpath ${unityFolder} -logfile ~/Unity.log -executeMethod ExportScript.ExportAndroidBuild -quit

Modifying Unity Settings

In the above process, we used the default settings of Unity Editor. But in an ideal scenario, we must have options to modify these settings via environment variables.

We addressed this issue with the help of Groovy scripts for player settings and quality settings, and the same C# scripts to handle export settings.

Modifying Unity player settings

We can handle changes in unity player settings by making related changes to the ProjectSettings/ProjectSettings.asset file.

We can easily find the changes to be done in this file when a property changes, by observing the values in this file before and after the changes are made using Unity Editor UI.

For example, to change the target device while exporting to an iOS project we can replace targetDevice: .* with targetDevice: 0 for iPhone Only, targetDevice: 1 for iPad only and targetDevice: 2 for iPhone + iPad.

We can use any scripting language to make these replacements and verify the changes by opening the unity editor UI after changes are made.

def fileText = readFile ‘Unity/ProjectSettings/ProjectSettings.asset’if(env.targetDevice == “iPhone Only”){fileText = fileText.replaceAll(/\n(\s*)targetDevice: .*/,’\n$1targetDevice: 0’)}else if(env.targetDevice == “iPad Only”){fileText = fileText.replaceAll(/\n(\s*)targetDevice: .*/,’\n$1targetDevice: 1’)}else if(env.targetDevice == “iPhone + iPad”){fileText = fileText.replaceAll(/\n(\s*)targetDevice: .*/,’\n$1targetDevice: 2’)}writeFile file: ’Unity/ProjectSettings/ProjectSettings.asset’, text: fileText

Modifying Unity export settings

We can change export settings with the help of the above-mentioned environment variable IOS_BUILD_OPTIONS/ANDROID_BUILD_OPTIONS.

# Exampleexport ANDROID_BUILD_OPTIONS=”BuildOptions.CompressWithLz4 | BuildOptions.Development | BuildOptions.AllowDebugging | BuildOptions.AcceptExternalModificationsToPlayer”

Unity documentation for BuildOptions can be found here.

Modifying Unity quality settings

We can change Unity quality settings in the similar way we changed player settings. This time the file to be changed is ProjectSettings/QualitySettings.asset.

For example, to enable Async Upload Persistent Buffer while exporting to an android project, we can replace asyncUploadPersistentBuffer:\d with asyncUploadPersistentBuffer:1

def fileText = readFile ’Unity/ProjectSettings/QualitySettings.asset’if(env.asyncUploadPersistentBuffer){fileText = fileText.replaceAll(/asyncUploadPersistentBuffer: \d/, “asyncUploadPersistentBuffer: 1”)}

Conclusion

By automating the pipeline for Unity in iOS & Android projects, we were able to:

Eliminate Human Error

With the Jenkins pipeline, we automated the whole process and removed human intervention, thus eliminating human error.

Save time

As the process was completely automated, it saved time, thereby enabling the engineer to focus on other activities & tasks of the project.

Jenkins pipeline

The total effort saved can be observed as shown in the table below:

Pro Tip!

Since you made it so far, we have a pro tip for you! The export script can be made generic by using the lines below in the iOS and Android export codes.

using System;using System.Collections;using System.Collections.Generic;using UnityEditor;using UnityEngine;// Add all active scenes to an arraystatic string[] SCENES = FindEnabledEditorScenes ();// Target Directory to export the projectstatic string TARGET_DIR = Environment.GetEnvironmentVariable(“EXPORT_DIR”);[MenuItem (“Custom/CI/Build Android”)]static void ExportAndroidBuild (){BuildOptions androidBuildOptions = Environment.GetEnvironmentVariable(“ANDROID_BUILD_OPTIONS”);GenericBuild (SCENES, TARGET_DIR + “/android/”, BuildTarget.Android, androidBuildOptions);}[MenuItem (“Custom/CI/Build iOS”)]static void ExportIOSBuild (){BuildOptions iosBuildOptions = Environment.GetEnvironmentVariable(“IOS_BUILD_OPTIONS”);GenericBuild (SCENES, TARGET_DIR + “/ios/”, BuildTarget.iOS, iosBuildOptions);}// Method which searches for all active scenesprivate static string[] FindEnabledEditorScenes (){List<string> EditorScenes = new List<string> ();foreach (EditorBuildSettingsScene scene in EditorBuildSettings.scenes) {if (!scene.enabled)continue;EditorScenes.Add (scene.path);}return EditorScenes.ToArray ();}// Generic build methodstatic void GenericBuild (string[] scenes, string target_dir, BuildTarget build_target, BuildOptions build_options){EditorUserBuildSettings.SwitchActiveBuildTarget (build_target);BuildPipeline.BuildPlayer (scenes, target_dir, build_target, build_options);}

Sounds like something you might want to be a part of? Check out our open roles and apply here → work.hike.in 🚀

--

--