Version: Unity 6.7 Alpha (6000.7)
Language : English
Assign assets to an AssetBundle
AssetBundle compression formats

Build assets into AssetBundles

To build assets into an AssetBundle, you must assign assets to an AssetBundle, either in the Unity Editor or through a script. You can then create and use a script to build the AssetBundles. For information on the best approaches for organizing assets into AssetBundles, refer to Preparing assets for AssetBundles.

Note: This workflow describes the creation of AssetBundles with the built-in BuildPipeline.BuildAssetBundles API. A more user-friendly alternative is to use the Addressables package.

AssetBundle build script

To build AssetBundles, you must create a build script and place it a folder called Editor in the Assets folder.

The following script is an example of an AssetBundle build script. It adds a menu item at the bottom of the Assets menu called Build AssetBundles. When you select Build AssetBundles the BuildAllAssetBundles method is called. A progress bar appears while the build takes all the assets you labeled with an AssetBundle name and uses them to populate AssetBundles at the path that assetBundleDirectory defines.

using UnityEditor;
using System.IO;

public class CreateAssetBundles
{
    [MenuItem("Assets/Build AssetBundles")]
    static void BuildAllAssetBundles()
    {
        // Ensure the AssetBundles directory exists, and if it doesn't, create it.
        string assetBundleDirectory = "Assets/AssetBundles";
        if (!Directory.Exists(assetBundleDirectory))
            Directory.CreateDirectory(assetBundleDirectory);

        // Build all AssetBundles and place them in the specified directory.
        BuildPipeline.BuildAssetBundles(assetBundleDirectory,
                                        BuildAssetBundleOptions.None,
                                        BuildTarget.StandaloneWindows);
    }
}

The script has the following arguments:

  • assetBundleDirectory: The directory to output the AssetBundles to within the current Unity project. The folder doesn’t need to be in the Assets folder. In the code example, it creates the folder on demand if it doesn’t exist.
  • BuildAssetBundleOptions.None: The default value for the build options argument. You can use this argument to specify one or more flags to enable a variety of optional behaviours. For example, this argument controls the choice of compression algorithm. For a full list of options available refer to the BuildAssetBundleOptions API documentation.
  • BuildTarget.StandaloneWindows: Defines the target platform for the AssetBundles. Alternatively, you can call EditorUserBuildSettings.activeBuildTarget, which returns the platform profile currently set as active in the Build ProfilesA set of customizable configuration settings to use when creating a build for your target platform. More info
    See in Glossary
    window.

Build subsets of AssetBundles

When you give the BuildPipeline.BuildAssetBundles method no specific AssetBundle names, it builds all AssetBundles defined in the project. If you only want to build a subset of AssetBundles, you query the AssetDatabase for defined AssetBundles and then pass a filtered list to the build pipeline.

The following script demonstrates how to retrieve all AssetBundle names and their assigned assets, allowing you to filter or modify the list before building:


using UnityEditor;
using System.IO;
using UnityEngine;
using System.Collections.Generic;

public class BuildSubsetAssetBundles
{
    [MenuItem("Assets/Build Selected AssetBundles")]
    static void BuildSpecificAssetBundles()
    {
        string assetBundleDirectory = "Assets/AssetBundles";
        if (!Directory.Exists(assetBundleDirectory))
        {
            Directory.CreateDirectory(assetBundleDirectory);
        }

        List<AssetBundleBuild> builds = new List<AssetBundleBuild>();
        string[] allAssetBundleNames = AssetDatabase.GetAllAssetBundleNames();

        // Example: Only build AssetBundles that start with "environment"
        foreach (string bundleName in allAssetBundleNames)
        {
            if (bundleName.StartsWith("environment"))
            {
                AssetBundleBuild build = new AssetBundleBuild
                {
                    assetBundleName = bundleName,
                    assetNames = AssetDatabase.GetAssetPathsFromAssetBundle(bundleName)
                };
                builds.Add(build);
            }
        }

        if (builds.Count > 0)
        {
            BuildPipeline.BuildAssetBundles(assetBundleDirectory,
                                            builds.ToArray(),
                                            BuildAssetBundleOptions.None,
                                            BuildTarget.StandaloneWindows);
            Debug.Log($"Built {builds.Count} specific AssetBundles.");
        }
        else
        {
            Debug.Log("No AssetBundles matching criteria found to build.");
        }
    }

    [MenuItem("Assets/Log All AssetBundle Assignments")]
    static void LogAllAssetBundleAssignments()
    {
        string[] allAssetBundleNames = AssetDatabase.GetAllAssetBundleNames();
        Debug.Log($"Total AssetBundles Defined: {allAssetBundleNames.Length}");
        foreach (string bundleName in allAssetBundleNames)
        {
            string[] assetPaths = AssetDatabase.GetAssetPathsFromAssetBundle(bundleName);
            Debug.Log($"AssetBundle: {bundleName} (Assets: {assetPaths.Length})");
            foreach (string path in assetPaths)
            {
                Debug.Log($"  - {path}");
            }
        }
    }
}

This approach allows your script to respect existing InspectorA Unity window that displays information about the currently selected GameObject, asset or project settings, allowing you to inspect and edit the values. More info
See in Glossary
assignments while giving you granular control over which AssetBundles are built.

Include AssetBundles in a Player build

This example demonstrates an advanced AssetBundle build script that builds AssetBundles and includes them in a Player build. It introduces several concepts:

  • Using the active build profile: The script sets the sceneA Scene contains the environments and menus of your game. Think of each unique Scene file as a unique level. In each Scene, you place your environments, obstacles, and decorations, essentially designing and building your game in pieces. More info
    See in Glossary
    list, flags, and settings based on the active build profile. Before using this script, create build profiles in the Build Profiles window to configure the builds.
  • Dynamic output paths: The script uses a naming convention for the output path based on the profile name and a timestamp, similar to what a basic build server might do.
  • AssetBundle builds: The script performs an AssetBundle build followed by a Player build.
  • Type preservation: The script provides type information from the AssetBundle build as an input to the Player build, so that managed code stripping doesn’t remove types used in the AssetBundles.
  • Build callbacks: The script uses a BuildPlayerProcessor build callback to inject the AssetBundles into the StreamingAssets folder of the Player build.
using UnityEngine;
using System.IO;
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using UnityEditor.Build.Profile;

// Example BuildScript that supports building the current build profile.
//
//
// It builds AssetBundles and includes them in the player build.
// Each time it runs it builds into a new directory derived from the
// current build profile and timestamp.
public class BuildScript
{
    public const string kBuildRootPath = "Build"; // All builds are inside this top level project folder
    public const string kAssetBundleDirectory = "AssetBundles";
    public const string kPlayerDirectory = "Player";
    public const string kAppName = "MyGame";
    public const string kTextureSourceDirectory = "Assets/Textures";
    public const string kTextureSearchPattern = "*.png";

    // Global variable so that RegisterContentForPlayer can find the correct AssetBundles to include in the player build
    public static string gCurrentBuildRootPath = null;

    [MenuItem("Build/Build Active Profile")]
    public static void BuildPlayerAndBundles()
    {
        var profile = BuildProfile.GetActiveBuildProfile();
        if (profile == null)
            throw new BuildFailedException("No active build profile is set." +
                "Use the Build Profiles window or the `-activeBuildProfile` cli argument");

        // Use a timestamp so that each build goes to a unique output folder
        var timeStamp = System.DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
        gCurrentBuildRootPath = $"{kBuildRootPath}/{profile.name}/{timeStamp}";

        // Build AssetBundles so that they can be shipped inside the player
        var assetBundleBuildPath = BuildAssetBundles(gCurrentBuildRootPath);

        // To preserve types used by the AssetBundles
        var assetBundleManifestPath = assetBundleBuildPath + "/AssetBundles.manifest";

        // Build the player
        var playerBuildOptions = new BuildPlayerWithProfileOptions()
        {
            buildProfile = profile,
            locationPathName = CreatePlayerOutputPath(gCurrentBuildRootPath),
            assetBundleManifestPath = assetBundleManifestPath,

            // These options can be adjusted as needed.
            // Note: the development and compression flags come from the build profile
            options = BuildOptions.CleanBuildCache | BuildOptions.StrictMode
        };

        // Convenient for manual testing
        if (!Application.isBatchMode)
            playerBuildOptions.options |= BuildOptions.AutoRunPlayer;

        var report = BuildPipeline.BuildPlayer(playerBuildOptions);

        gCurrentBuildRootPath = null;

        if (report.summary.result != BuildResult.Succeeded)
            throw new BuildFailedException("Player build failed, see Editor log for details");

        Debug.Log($"Completed build to {playerBuildOptions.locationPathName}");
    }

    private static string BuildAssetBundles(string buildRootDirectory)
    {
        var assetBundlePath = buildRootDirectory + "/" + kAssetBundleDirectory;

        if (!Directory.Exists(assetBundlePath))
            Directory.CreateDirectory(assetBundlePath);

        // For simplicity in this example, define a single AssetBundle,
        // containing all the textures found inside a hard-coded directory in the project
        string[] texturePaths = Directory.GetFiles(kTextureSourceDirectory, kTextureSearchPattern, SearchOption.AllDirectories);

        var assetBundleContents = new AssetBundleBuild()
        {
            assetBundleName = "textures.bundle",
            assetNames = texturePaths
        };

        // The target platform will be automatically set based on the active build profile
        var assetBundleBuildOptions = new BuildAssetBundlesParameters()
        {
            outputPath = assetBundlePath,
            bundleDefinitions = new AssetBundleBuild[] { assetBundleContents }
        };

        AssetBundleManifest manifest = BuildPipeline.BuildAssetBundles(assetBundleBuildOptions);

        if (manifest == null)
            throw new BuildFailedException("AssetBundle build failed, see Editor log for details");

        return assetBundlePath;
    }

    private static string CreatePlayerOutputPath(string buildRootDirectory)
    {
        var playerOutputFolder = $"{buildRootDirectory}/{kPlayerDirectory}";

        if (!Directory.Exists(playerOutputFolder))
            Directory.CreateDirectory(playerOutputFolder);

        var playerPath = $"{playerOutputFolder}/{kAppName}";

        // This property will match the target in the active build profile
        var target = EditorUserBuildSettings.activeBuildTarget;

        // See "Build path requirements for target platforms" in the Unity Manual
        if ((target == BuildTarget.StandaloneWindows64) ||
            (target == BuildTarget.StandaloneWindows))
            playerPath += ".exe";
        else if (target == BuildTarget.StandaloneOSX)
            playerPath += ".app";
        else if (target == BuildTarget.StandaloneLinux64)
            playerPath += ".x86_64";
        else if (target == BuildTarget.Android)
            playerPath += ".aab";

        return playerPath;
    }
}

// Put the AssetBundle build directory into the StreamingAssets folder of the player output.
// This approach keeps built content separate from the source project, avoiding clutter in "Assets/StreamingAssets".
public class RegisterContentForPlayer : BuildPlayerProcessor
{
    public override void PrepareForBuild(BuildPlayerContext buildPlayerContext)
    {
        var currentBuildPath = BuildScript.gCurrentBuildRootPath;

        if (string.IsNullOrEmpty(currentBuildPath))
            // Do not do anything if we are not in a build initiated by BuildScript
            return;

        buildPlayerContext.AddAdditionalPathToStreamingAssets(currentBuildPath + "/" + BuildScript.kAssetBundleDirectory);
    }

    public override int callbackOrder => 1;
}

To use this script in the Unity Editor:

  1. Select the build profile in the Build Profiles window.
  2. Select Build > Build Active Profile from the menu.

You can also invoke this script from the command line. On Windows, the command is as follows:

.\Unity.exe -batchmode -projectPath "C:\UnityProjects\CLIBuildExample" -activeBuildProfile "Assets\Settings\Build Profiles\MyWindowsProfile.asset" -executeMethod BuildScript.BuildPlayerAndBundles -logFile C:\logs\buildlog.txt -quit

An equivalent command on macOS is as follows:

Unity -batchmode -projectPath "~/UnityProjects/CLIBuildExample" -activeBuildProfile "Assets/Settings/Build Profiles/MyWeb - Desktop - Development.asset" -executeMethod BuildScript.BuildPlayerAndBundles -logFile "~/logs/buildlog.txt" -quit

Note: Adjust the paths in these command lines to match your device configuration and the path to your Unity project. For more information about command line arguments, refer to Build a player from the command line.

Perform a clean build

When you create an official AssetBundle release, perform a clean build to ensure Unity rebuilds all content during the build process. To perform a clean build, pass the BuildAssetBundleOptions.ForceRebuildAssetBundle flag as an option to BuildPipeline.BuildAssetBundles.

In some projects, you can delete the Library/ShaderCache directory to force a full recompilation of shaders, or to reclaim disk space from obsolete shaderA program that runs on the GPU. More info
See in Glossary
data. However, deleting the ShaderCache folder increases the time it takes for Unity to create another build.

For more information on clean builds, refer to Create a clean build.

Changing target platform

The BuildPipeline.BuildAssetBundles API allows you to specify the target and sub target platform for deploying AssetBundles.

If the specified target platform differs from the one configured in Build Profiles, Unity must recompile Editor scriptsA piece of code that allows you to create your own Components, trigger game events, modify Component properties over time and respond to user input in any way you like. More info
See in Glossary
and reimport assets such as textures that have platform-specific representations. After the build is complete, Unity restores the original target platform settings.

This process can significantly increase build times. Additionally, the script containing the BuildPipeline.BuildAssetBundles call continues to execute as compiled for the current target platform, not the specified build target. This can cause issues if the build script or callback scripts rely on platform-specific code or assemblies.

To avoid this issue, ensure that any code executed during a build dynamically checks the target platform (for example, using if statements) rather than relying on platform-specific conditional compilation (such as, #ifdef statements). It’s best practice to always set the target to the desired target, then launch the script that builds AssetBundles to avoid any issues from code outside your control such as build callbacks inside packages.

For command line builds, use the --buildTarget or -activeBuildProfile command line argument to align the target platform with your build requirements. For more information, refer to Create a build from the command line.

Rebuilding assets incrementally

Each AssetBundle has a hash that Unity uses to determine whether it needs to be rebuilt. Unity determines how to rebuild AssetBundles incrementally as follows:

  1. If a .manifest file from a previous build of that AssetBundle exists, Unity compares the IncrementalBuildHash hashes from both builds.
  2. If the AssetBundle hashes match, then Unity calculates and compares their type tree hashes. Unity uses TypeTreeHash as a secondary hash to detect whether any objects used in the AssetBundle have newer serialization formats. You can ignore this check by specifying the BuildAssetBundleOptions.IgnoreTypeTreeChanges flag.
  3. If the AssetBundle hash and the type tree hash don’t match, then Unity rebuilds the AssetBundle unless you’ve specified BuildAssetBundleOptions.ForceRebuildAssetBundle, which rebuilds the AssetBundle every time.
  4. Unity serializes the newly calculated hash values into the new AssetBundle’s .manifest file.

The AssetBundle IncrementalBuildHash considers inputs such as the target platform, included assets, dependencies, build options, and platform-specific settings like meshThe main graphics primitive of Unity. Meshes make up a large part of your 3D worlds. Unity supports triangulated or Quadrangulated polygon meshes. Nurbs, Nurms, Subdiv surfaces must be converted to polygons. More info
See in Glossary
stripping and lighting configurations. However, this hash doesn’t consider every possible build influence, which might lead to crashes or unexpected failures if the incremental build systems doesn’t detect all changes. Use incremental builds for internal development but perform a clean build when creating a release build.

Warning: The TypeTreeHash is distinct from the main AssetBundle input hash (IncrementalBuildHash). A change in the TypeTreeHash can force an incremental build without changing the AssetBundle input hash value, so the AssetBundle input hash isn’t an ideal value for tracking file versions. It’s more reliable to use a hash based on the content or other version numbering scheme. For more information, refer to Cache version hash.

Additional resources

Assign assets to an AssetBundle
AssetBundle compression formats