Tutorial: Build scripts and Unity.

One of the first things I generally do when creating a new project at work is create a buildscript. Now these can come in many different forms, from using Powershell scripts, through to using a mixture of Powershell, Batch files and things like FAKE.

In the case of my game development I generally haven't done that approach, I'm not sure why if I'm honest.

For this tutorial I'll be going through how to make a basic buildscript that will just build a unity project, no deployment or anything like that, just the build itself.

Before we get started, this will also just cover a batch file version of the build script if you want to attempt something like FAKE theres a blog post by Jonathan Peppers on it here: https://jonathanpeppers.com/Blog/automating-unity3d-builds-with-fake

So first things first we need to setup a new static class called BuildHelper within unity, this will be placed in a folder called Editor as we don't want this included in the projects final build.

Edit it to look like the following:

using UnityEditor;

public static class BuildHelper
{
    
}

Next we'll need to setup the internal helper functions GetScenes() and SetupVariables().

GetScenes() will select any enabled scene and pass aback their path in a string array.
SetupVariables() will attempt to load a buildManifest.txt file and update the player settings with things like the version, build location etc.

Before we get around to building these two functions we'll need to add the following using statements at the top of the file:

using System.Linq;
using System.IO;
using System.Collections.Generic;

For GetScenes() we'll need to put the following code within the BuildHelper class:

private static string[] GetScenes()
{
    return EditorBuildSettings.scenes.Where(s => s.enabled).Select(s => s.path).ToArray();
}

For SetupVariables() we'll need to add the following:

private static string _buildLocation;

private static void SetupVariables()
{
    if (!File.Exists("./buildManifest.txt"))
    {
        //There's no buildManifest.txt file set some defaults.
        PlayerSettings.productName = "Product Name Here";
        PlayerSettings.companyName = "Luke Parker";
        PlayerSettings.forceSingleInstance = true;
        PlayerSettings.bundleVersion = "0.0.0.0";
        _buildLocation = "./Build/";
    }
    else
    {
        // buildManifest file exists load it up and setup the variables.
        using (var fs = new FileStream("./buildManifest.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
        {
            using (var sr = new StreamReader(fs))
            {
                var fileData = new Dictionary<string, string>();
                while (!sr.EndOfStream)
                {
                    var line = sr.ReadLine().Split('=');
                    fileData.Add(line[0], line[1].Replace("\"",""));
                }

                PlayerSettings.productName = fileData["ProductName"];
                PlayerSettings.companyName = fileData["CompanyName"];
                PlayerSettings.forceSingleInstance = true;
                PlayerSettings.bundleVersion = fileData["Version"];
                _buildLocation = fileData["BuildLocation"] + "/"+ fileData["Version"] + "/" + fileData["ProductName"].Replace(" ","_");
            }
        }
    }
}

Next we'll need the functions to call when actually firing off the build, these functions will mostly be the same with small tweaks depending on platform.

For this we'll need to add one for Windows, Linux and MacOS:

public static void Windows()
{
    SetupVariables();
    BuildPipeline.BuildPlayer(GetScenes(),_buildLocation + ".exe",BuildTarget.StandaloneWindows64,BuildOptions.None);
}

public static void Linux()
{
    SetupVariables();
    BuildPipeline.BuildPlayer(GetScenes(),_buildLocation, BuildTarget.StandaloneLinuxUniversal, BuildOptions.None);
}

public static void MacOS()
{
    SetupVariables();
    BuildPipeline.BuildPlayer(GetScenes(), _buildLocation + ".app", BuildTarget.StandaloneOSX, BuildOptions.None);
}

Once that has been done the next section will be building the batch script (in my case called build.cmd), this will take in a Unity Version, Product Name, Company Name, Target and Version. It will then make a path to the build location, compile the buildManifest.txt file, run unity (if it can find it based on the version) and then delete the buildManifest.txt when finished.

That is done by the following code:

@echo off

SET UNITYVERSION=2018.1.1f1
IF NOT [%1]==[] (set UNITYVERSION=%1)

SET PRODUCTNAME="Product Name"
IF NOT [%2]==[] (set PRODUCTNAME=%2)

SET COMPANYNAME="Company Name"
IF NOT [%3]==[] (set COMPANYNAME=%3)

SET TARGET=Windows
IF NOT [%4]==[] (set TARGET=%4)

SET VERSION=0.0.0.0
IF NOT [%5]==[] (set VERSION=%5)

SET BUILDLOCATION="./Build/%TARGET%/%VERSION%"

rmdir -S %BUILDLOCATION%
mkdir %BUILDLOCATION%

>buildManifest.txt (
    echo ProductName=%PRODUCTNAME%
    echo CompanyName=%COMPANYNAME%
    echo Version=%VERSION%
    echo BuildLocation=%BUILDLOCATION%
)

"E:\Programs\Unity\%UNITYVERSION%\Editor\Unity.exe" -quit -batchMode -executeMethod BuildHelper.%TARGET%

del /f buildManifest.txt

NOTE: The location of the Unity Editor might be different from the one stated above.

This file is placed in the root of your project folder and can be run via command line by using something like .\build.cmd 2018.1.1f1 'Sample Project' 'Luke Parker' Windows 0.0.0.1 or I think even on something like TeamCity.

I've setup a gist with both of the files over here: https://gist.github.com/lparkermg/453cf24d1055316fa48016882c1f218a

You might be wondering why I set a configurable unity version, well, since the introduction of the Unity Hub it's simple enough to have multiple versions installed and I wanted to accommodate that.


If you found that tutorial useful and want to support me in making more (or just generally support what I'm doing) then I have a few places you can do that:

Monzo.Me | Buy me a coffee | Buy My Books | Buy my Games

Luke Parker

Luke Parker