Why Use Double Builds for Your Unity 3D Game
Texture Compatibility
One of the key reasons is asset compatibility. In particular, texture support differs significantly between Mobile Web (Android & iOS devices) and Desktop Web (macOS, Linux, Windows). To better understand why this matters, let’s take a closer look at the data:
Desktop Browsers | iOS and Android browser | |
RGB Compressed ETC2 | No | Yes |
RGB Crunched ETC | No | Yes |
RGBA Compressed ETC2 | No | Yes |
RGBA Crunched ETC2 | No | Yes |
RGB Compressed DXT1, also known as BC1 | Partial (*) | No |
RGB Crunched DXT1, also known as BC1 | Partial (*) | No |
RGBA Compressed DXT5, also known as BC3 | Partial (*) | No |
RGBA Crunched DXT5, also known as BC3 | Partial (*) | No |
* With linear rendering on web browsers that do not support sRGB DXT, textures are decompressed to RGBA32 at load time.
DXT1/5 formats will work on mobile, but it will fall back to uncompressed RGBA consuming more RAM and increasing load times due to texture conversion.
Refer to the Unity documentation for more details: https://docs.unity3d.com/2021.3/Documentation/Manual/class-TextureImporterOverride.html
Asset Optimisation
Build separation makes it possible to deliver optimised assets tailored for mobile devices.
Mobile screens are extremely dense in terms of dpi (pixels per inch). This means a pixel shader on a small 5–6 inch display performs nearly the same amount of calculations as on a full 2K desktop monitor with a much weaker mobile GPU. On top of that, the CPU is significantly weaker and often limited to single-threaded performance.
That’s where double builds shine – it allow you to supply alternative assets and settings for mobile, such as:
- Optimized shaders and materials
- Adjusted shadow quality
- Simplified lighting (e.g., fully baked with support for just 1–2 dynamic lights)
- Models with reduced geometry (High-poly models are difficult to transform using the limited vertex units available on mobile GPUs, and the extra detail is unnecessary for such small displays)
- Simplified, GPU-friendly or GPU-baked animations (Relevant for all Web builds. Skeletal animations that cannot be fully processed on the GPU quickly become a bottleneck.)
Behaviour and Code Optimisation
Script defines, code generation, and similar techniques let us deliver platform-specific code and behaviors without introducing performance penalties or unnecessary abstraction layers.
This approach makes it possible to:
- Optimize input methods for different devices (touch, mouse, controller, etc.)
- Adapt UI layouts for smaller form factors or specific interaction styles
- Replace or simplify heavy routines with optimized ones
How to Double Build
Note: Here you can find my example project demonstrating build automatisation, Mobile/Desktop Web build separation, and Dockerization support: https://github.com/AndreiMaksimovich/Unity-Build-and-Test-Automation
Separated Builds
An example of this approach is available in the repository mentioned above.
The core idea is to generate separate builds for each required platform type – such as Mobile Web, Desktop Web, Mobile Tablets (touch screens, 9-inch+), etc, and then route the player to the appropriate build automatically.
Merged Build
The idea behind this method is to merge multiple builds into a single one, allowing us to select different data, code, and asset sources before the Unity player initializes programatically. Let’s explore how this can be achieved.
Let’s examine the default Unity Web template in Unity 6.2 – specifically, the index.html
file between lines 61 and 81:
var buildUrl = "Build";
var loaderUrl = buildUrl + "/{{{ LOADER_FILENAME }}}";
var config = {
arguments: [],
dataUrl: buildUrl + "/{{{ DATA_FILENAME }}}",
frameworkUrl: buildUrl + "/{{{ FRAMEWORK_FILENAME }}}",
#if USE_THREADS
workerUrl: buildUrl + "/{{{ WORKER_FILENAME }}}",
#endif
#if USE_WASM
codeUrl: buildUrl + "/{{{ CODE_FILENAME }}}",
#endif
#if SYMBOLS_FILENAME
symbolsUrl: buildUrl + "/{{{ SYMBOLS_FILENAME }}}",
#endif
streamingAssetsUrl: "StreamingAssets",
companyName: {{{ JSON.stringify(COMPANY_NAME) }}},
productName: {{{ JSON.stringify(PRODUCT_NAME) }}},
productVersion: {{{ JSON.stringify(PRODUCT_VERSION) }}},
showBanner: unityShowBanner,
};
What we’re looking at here is the configuration for initializing a Unity instance. The key point is that this configuration is scripted – meaning we can dynamically change the Data file URL, Wasm file URL, and Streaming Assets URL on the fly, effectively switching both the codebase and assets.
Now let’s take a look at the index.html
file in a web build:
var buildUrl = "Build";
var loaderUrl = buildUrl + "/Build.loader.js";
var config = {
arguments: [],
dataUrl: buildUrl + "/Build.data.gz",
frameworkUrl: buildUrl + "/Build.framework.js.gz",
codeUrl: buildUrl + "/Build.wasm.gz",
streamingAssetsUrl: "StreamingAssets",
companyName: "DefaultCompany",
productName: "Demo",
productVersion: "1.0.2",
showBanner: unityShowBanner,
};
This can be easily modified to something like:
const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
var buildUrl = "Build";
var loaderUrl = buildUrl + "/Build.loader.js";
var config = {
arguments: [],
dataUrl: buildUrl + (isMobile ? "/Build.Mobile.data.gz" : "/Build.data.gz"),
frameworkUrl: buildUrl + "/Build.framework.js.gz",
codeUrl: buildUrl + (isMobile ? "/Build.Mobile.wasm.gz" : "/Build.wasm.gz"),
streamingAssetsUrl: (isMobile ? "StreamingAssets.Mobile" : "StreamingAssets"),
companyName: "DefaultCompany",
productName: "Demo",
productVersion: "1.0.2",
showBanner: unityShowBanner,
};
This provides a simple way to differentiate and deliver separate code and assets for Mobile and Desktop.
How this can be automated:
- Create a custom template where
dataUrl
,codeUrl
, andstreamingAssetsUrl
are replaced as shown above. - Use this template to build the project separately for Mobile and Desktop.
- Merge the builds into one – essentially by copying the Data, Wasm, and Asset files from one build into the other, and renaming them to match the names from the template.
Note: This is a simplified explanation, but it works and can be extended to fit specific requirements.