Addressables Asset (4)

Using Addressables at runtime

Once you have your Addressable assets organized into groups and built into AssetBundles, you must still load, instantiate, and, in the end release them at runtime.

Addressables uses a reference counting system to make sure that assets are only kept in memory while they are needed. See Memory management for more information about reference counting and how you can minimize the amount of memory used by assets at any given time.

Addressables provides several options and APIs for loading and instantiating Addressable assets. See Loading Addressable assets for information and examples, including:

  • Loading an single asset

  • Loading multiple assets

  • Loading an AssetReference

  • Loading Scenes

  • Loading assets by location

  • Instantiating objects from Addressables

  • Releasing Addressable assets

  • Using Addressables in a Scene

  • Downloading dependencies in advance

Addressables uses asynchronous operations for most loading tasks. See Operations for information on how to handle operations in your code, including:

  • Releasing AsyncOperationHandle instances

  • Coroutine- and IEnumerator-based operation handling

  • Event-based operation handling

  • Task-based operation handling

  • Using operations synchronously

  • Custom operations

  • Using typed versus untyped operation handles

  • Reporting operation progress

See the following for information about other runtime topics:

  • Customizing initialization

  • Loading additional catalogs

  • Updating catalogs

  • Modifying resource URLs at runtime

  • Getting the address of an asset at runtime

///

Addressables Initialization

The Addressables system initializes itself at runtime the first time you load an Addressable or make another call to an Addressable API. Call Addressables.InitializeAsync to initialize Addressables earlier (this function does nothing if initialization has already occurred).

The initialization operation performs the following tasks:

  • Sets up the ResourceManager and the ResourceLocators.

  • Loads configuration data created by Addressables from StreamingAssets.

  • Executes any initialization object operations.

  • Loads the content catalog. By default, Addressables first checks for updates to the content catalog and downloads a new catalog if available.

The following Addressables settings can change initialization behavior:

  • Only update catalogs manually: Addressables won't automatically check for an updated catalog. See Updating catalogs for information about manually updating your catalogs.

  • Build Remote Catalog: Addressables won't attempt to load remote content without a remote catalog.

  • Custom certificate handler: identify a custom certificate handler if you need one to access your remote asset hosting service.

  • Initialization object list: add IObjectInitializationDataProvider ScriptableObjects to your application that are invoked during the initialization operation.

The following runtime properties should be set before the initialization operation starts:

  • Custom URL transform function

  • ResourceManager exception handler

  • Static properties used for any custom runtime placeholders in your Profile variables

Initialization objects

You can attach objects to the Addressable Assets settings and pass them to the initialization process at runtime. For example, you can create a CacheInitializationSettings object to initialize Unity's Cache settings at runtime. To create your own types of initialization object, create a ScriptableObject that implements the IObjectInitializationDataProvider interface. Use this object to create the ObjectInitializationData asset that Addressables includes with your the runtime data.

Cache initialization objects

Use a CacheInitializationSettings object to initialize Unity's Cache settings at runtime.

To specify the cache initialization settings that the Addressables system should use:

  1. Create the CacheInitializationSettings asset (menu: Assets > Addressables > Initialization > Cache Initialization Settings).

Select the new asset file in the Project panel to view the settings in the Inspector

Addressables Asset (4)_第1张图片
  1. Adjust the settings as desired.

  1. Open the Addressables Settings Inspector (menu: Window > Asset Management > Addressables > Settings).

  1. In the Initialization Objects section of the Inspector, click the + button to add a new object to the list.

  1. Select your CacheInitializationSettings asset in the File dialog and click Open.

  1. The cache settings object is added to the list.

When Addressables initializes at runtime, it applies these settings to the default Unity Cache. The settings apply to all AssetBundles in the default cache, not just those downloaded by the Addressables system. See Caching for more information about the Unity cache system.

NOTE

Android applications built with Unity 202.1 or earlier or running on Android 9 or earlier can only play videos from uncompressed AssetBundles. You can use a CacheInitializationSettings object to disable recompression of the cache by disabling the Compress Bundles option.

///

Memory management

The Addressables system manages the memory used to load assets and bundles by keeping a reference count of every item it loads.

When an Addressable is loaded, the system increments the reference count; when the asset is released, the system decrements the reference count. When the reference count of an Addressable returns to zero, it is eligible to be unloaded. When you explicitly load an Addressable asset, you must also release the asset when you are done with it.

The basic rule of thumb to avoid "memory leaks" (assets that remain in memory after they are no longer needed) is to mirror every call to a load function with a call to a release function. You can release an asset with a reference to the asset instance itself or with the result handle returned by the original load operation.

Note, however, that released assets are not necessarily unloaded from memory immediately. The memory used by an asset is not freed until the AssetBundle it belongs to is also unloaded. (Released assets can also be unloaded by calling Resources.UnloadUnusedAssets, but that tends to be a slow operation which can cause frame rate hitches.)

AssetBundles have their own reference count (the system treats them like Addressables with the assets they contain as dependencies). When you load an asset from a bundle, the bundle's reference count increases and when you release the asset, the bundle reference count decreases. When a bundle's reference count returns to zero, that means none of the assets it contains are still in use and the bundle and all the assets it contains are unloaded from memory.

Use the Event Viewer to monitor your runtime memory management. The viewer shows when assets and their dependencies are loaded and unloaded.

Understanding when memory is cleared

An asset no longer being referenced (indicated by the end of a blue section in the Event Viewer) does not necessarily mean that asset was unloaded. A common applicable scenario involves multiple assets in an AssetBundle. For example:

  • You have three Assets (tree, tank, and cow) in an AssetBundle (stuff).

  • When tree loads, the profiler displays a single ref-count for tree, and one for stuff.

  • Later, when tank loads, the profiler displays a single ref-count for both tree and tank, and two ref-counts for the stuff AssetBundle.

  • If you release tree, it's ref-count becomes zero, and the blue bar goes away.

In this example, the tree asset is not actually unloaded at this point. You can load an AssetBundle, or its partial contents, but you cannot partially unload an AssetBundle. No asset in stuff unloads until the AssetBundle itself is completely unloaded. The exception to this rule is the engine interface Resources.UnloadUnusedAssets. Executing this method in the above scenario causes tree to unload. Because the Addressables system cannot be aware of these events, the profiler graph only reflects the Addressables ref-counts (not exactly what memory holds). Note that if you choose to use Resources.UnloadUnusedAssets, it is a very slow operation, and should only be called on a screen that won't show any hitches (such as a loading screen).

Avoiding asset churn

Asset churn is a problem that can arise if you release an object that happens to be the last item in an AssetBundle and then immediately reload either that asset or another asset in the bundle.

For example, say you have two materials, boat and plane that share a texture, cammo, which has been pulled into its own AssetBundle. Level 1 uses boat and level 2 uses plane. As you exit level 1 you release boat, and immediately load plane. When you release boat, Addressables unloads texture cammo. Then, when you load plane, Addressables immediately reloads cammo.

You can use the Event Viewer to help detect asset churn by monitoring asset loading and unloading.

AssetBundle memory overhead

When you load an AssetBundle, Unity allocates memory to store the bundle's internal data, which is in addition to the memory used for the assets it contains. The main types of internal data for a loaded AssetBundle include:

  • Loading cache: Stores recently accessed pages of an AssetBundle file. Use AssetBundle.memoryBudgetKB to control its size.

  • TypeTrees: Defines the serialized layout of your objects.

  • Table of contents: Lists the assets in a bundle.

  • Preload table: Lists the dependencies of each asset.

When you organize your Addressable groups and AssetBundles, you typically must make trade-offs between the size and the number of AssetBundles you create and load. On the one hand, fewer, larger bundles can minimize the total memory usage of your AssetBundles. On the other hand, using a larger number of small bundles can minimize the peak memory usage because you can unload assets and AssetBundles more easily.

While the size of an AssetBundle on disk is not the same as its size at runtime, you can use the disk size as an approximate guide to the memory overhead of the AssetBundles in a build. You can get bundle size and other information you can use to help analyze your AssetBundles from the Build Layout Report.

The following sections discuss the internal data used by AssetBundles and how you can minimize the amount of memory they require, where possible.

TypeTrees

A TypeTree describes the field layout of one of your data types.

Each serialized file in an AssetBundle contains a TypeTree for each object type within the file. The TypeTree information allows you to load objects that are deserialized slightly differently from the way they were serialized. TypeTree information is not shared between AssetBundles; each bundle has a complete set of TypeTrees for the objects it contains.

All the TypeTrees are loaded when the AssetBundle is loaded and held in memory for the lifetime of the AssetBundle. The memory overhead associated with TypeTrees is proportional to the number of unique types in the serialized file and the complexity of those types.

You can reduce the memory requirements of AssetBundle TypeTrees in the following ways:

  • Keep assets of the same types together in the same bundles.

  • Turn off TypeTrees -- turning off TypeTrees makes your AssetBundles smaller by excluding this information from a bundle. However, without TypeTree information, you may encounter serialization errors or undefined behavior when loading older bundles with a newer version of Unity or after making even small script changes in your project.

  • Prefer simpler data types to reduce TypeTree complexity.

You can test the impact that TypeTrees have on the size of your AssetBundles by building them with and without TypeTrees disabled and comparing the sizes. Use BuildAssetBundleOptions.DisableWriteTypeTree to disable TypeTrees in your AssetBundles. Note that not all platforms support TypeTrees and some platforms require TypeTrees (and ignore this setting).

If you disable TypeTrees in a project, always rebuild local Addressable groups before building a new player. If you are distributing content remotely, only update content using the same version (including patch number) of Unity that you used to produce your current player and don't make even minor code changes. (When you are juggling multiple player versions, updates, and versions of Unity, you might not find the memory savings from disabling TypeTrees to be worth the trouble.)

Table of contents

The table of contents is a map within the bundle that allows you to look up each explicitly included asset by name. It scales linearly with the number of assets and the length of the string names by which they are mapped.

The size of your table of contents data is based on the total number of assets. You can minimize the amount of memory dedicated to holding table of content data by minimizing the number of AssetBundles loaded at a given time.

Preload table

The preload table is a list of all the other objects that an asset references. Unity uses the preload table to load these referenced objects when you load an asset from the AssetBundle.

For example, a Prefab has a preload entry for each of its components as well as any other assets it may reference (materials, textures, etc). Each preload entry is 64 bits and can reference objects in other AssetBundles.

When an asset references another asset that in turn references other assets, the preload table can become large because it contains the entries needed to load both assets. If two assets both reference a third asset, then the preload tables of both contain entries to load the third asset (whether or not the referenced asset is Addressable or in the same AssetBundle).

As an example, consider a situation in which you have two assets in an AssetBundle (PrefabA and PrefabB) and both of these prefabs reference a third prefab (PrefabC), which is large and contains several components and references to other assets. This AssetBundle contains two preload tables, one for PrefabA and one for PrefabB. Those tables contain entries for all the objects of their respective prefab, but also entries for all the objects in PrefabC and any objects referenced by PrefabC. Thus the information required to load PrefabC ends up duplicated in both PrefabA and PrefabB. This happens whether or not PrefabC is explicitly added to an AssetBundle.

Depending on how you organize your assets, the preload tables in AssetBundles could become quite large and contain many duplicate entries. This is especially true if you have several loadable assets that all reference a complex asset, such as PrefabC in the situation above. If you determine that the memory overhead from the preload table is a problem, you can structure your loadable assets so that they have fewer complex loading dependencies.

Memory implications of loading AssetBundle dependencies

Loading an Addressable asset also loads all of the AssetBundles containing its dependencies. An AssetBundle dependency occurs when an asset in one bundle references an asset in another bundle. An example of this is a material referencing a texture. For more information see Asset and AssetBundle dependencies.

Addressables calculates dependencies between bundles at the bundle level. If one asset references an object in another bundle, then the entire bundle has a dependency on that bundle. This means that even if you load an asset in the first bundle that has no dependencies of its own, the second AssetBundle is still loaded into memory.

For Example:

BundleA contains Addressable Assets RootAsset1 and RootAsset2. RootAsset2 references DependencyAsset3, which is contained in BundleB. Even though RootAsset1 has no reference to BundleB, BundleB is still a dependency of RootAsset1 because RootAsset1 is in BundleA, which has a reference on BundleB.

NOTE

Prior to Addressables 1.13.0, the dependency graph was not as thorough as it is now. In the example above, RootAsset1 would not have had a dependency on BundleB. This previous behavior resulted in references breaking when an AssetBundle being referenced by another AssetBundle was unloaded and reloaded. This fix may result in additional data remaining in memory if the dependency graph is complex enough.

To avoid loading more bundles than are required, you should strive to keep the dependencies between AssetBundles as simple as possible. There are several diagnostic tools included with Addressables that can be used to accomplish this:

  • Analyze Tool

  • Build Layout Report

/

Loading Addressable assets

The Addressables class provides several methods for loading Addressable assets. You can load assets one at a time or in batches. To identify the assets to load, you pass either a single key or a list of keys to the loading function. A key can be one of the following objects:

  • Address: a string containing the address you assigned to the asset

  • Label: a string containing a label assigned to one or more assets

  • AssetReference object: an instance of AssetReference

  • IResourceLocation instance: an intermediate object that contains information to load an asset and its dependencies.

When you call one of the asset loading functions, the Addressables system begins an asynchronous operation that carries out the following tasks:

  1. Looks up the resource locations for the specified keys (except IResourceLocation keys)

  1. Gathers the list of dependencies

  1. Downloads any remote AssetBundles that are required

  1. Loads the AssetBundles into memory

  1. Sets the Result object of the operation to the loaded objects

  1. Updates the Status of the operation and calls any Completed event listeners

If the load operation succeeds, the Status is set to Succeeded and the loaded object or objects can be accessed from the Result object.

If an error occurs, the exception is copied to the OperationException member of the operation object and the Status is set to Failed. By default, the exception is not thrown as part of the operation. However, you can assign a handler function to the ResourceManager.ExceptionHandler property to handle any exceptions. Additionally, you can enable the Log Runtime Exceptions option in your Addressable system settings to record errors to the Unity Console.

When you call loading functions that can load multiple Addressable assets, you can specify whether the entire operation should abort if any single load operation fails or whether the operation should load any assets it can. In both cases, the operation status is set to failed. (Set the releaseDependenciesOnFailure parameter to true in the call to the loading function to abort the entire operation on any failure.)

See Operations for more information about asynchronous operations and writing asynchronous code in Unity scripts.

Correlating loaded assets to their keys

When you load multiple assets in one operation, the order in which individual assets are loaded is not necessarily the same as the order of the keys in the list you pass to the loading function.

If you need to associate an asset in a combined operation with the key used to load it, you can perform the operation in two steps:

  1. Load the IResourceLocation instances with the list of asset keys.

  1. Load the individual assets using their IResourceLocation instances as keys.

The IResourceLocation object contains the key information so you can, for example, keep a dictionary to correlate the key to an asset. Note that when you call a loading function, such as LoadAssetsAsync, the operation first looks up the IResourceLocation instances that correspond to a key and then uses that to load the asset. When you load an asset using an IResourceLocation, the operation skips the first step. Thus, performing the operation in two steps does not add significant additional work.

The following example loads the assets for a list of keys and inserts them into a dictionary by their address (PrimaryKey). The example first loads the resource locations for the specified keys. When that operation is complete, it loads the asset for each location, using the Completed event to insert the individual operation handles into the dictionary. The operation handles can be used to instantiate the assets, and, when the assets are no longer needed, to release them.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.Events;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceLocations;

internalclassLoadWithLocation : MonoBehaviour
{
    public Dictionary> operationDictionary;
    public List keys;
    public UnityEvent Ready;

    IEnumerator LoadAndAssociateResultWithKey(IList keys)
    {
        if (operationDictionary == null)
            operationDictionary = new Dictionary>();

        AsyncOperationHandle> locations
            = Addressables.LoadResourceLocationsAsync(keys,
                Addressables.MergeMode.Union, typeof(GameObject));

        yieldreturn locations;

        var loadOps = new List(locations.Result.Count);

        foreach (IResourceLocation location in locations.Result)
        {
            AsyncOperationHandle handle =
                Addressables.LoadAssetAsync(location);
            handle.Completed += obj => operationDictionary.Add(location.PrimaryKey, obj);
            loadOps.Add(handle);
        }

        yieldreturn Addressables.ResourceManager.CreateGenericGroupOperation(loadOps, true);

        Ready.Invoke();
    }

    voidStart()
    {
        Ready.AddListener(OnAssetsReady);
        StartCoroutine(LoadAndAssociateResultWithKey(keys));
    }

    privatevoidOnAssetsReady()
    {
        float x = 0, z = 0;
        foreach (var item in operationDictionary)
        {
            Debug.Log($"{item.Key} = {item.Value.Result.name}");
            Instantiate(item.Value.Result,
                new Vector3(x++ * 2.0f, 0, z * 2.0f),
                Quaternion.identity, transform);
            if (x > 9)
            {
                x = 0;
                z++;
            }
        }
    }

    privatevoidOnDestroy()
    {
        foreach (var item in operationDictionary)
        {
            Addressables.Release(item.Value);
        }
    }
}

Note that the loading function creates a group operation with ResourceManager.CreateGenericGroupOperation. This allows the function to continue after all the loading operations have finished. In this case, the function dispatches a "Ready" event to notify other scripts that the loaded data can be used.

Loading assets by location

When you load an Addressable asset by address, label, or AssetReference, the Addressables system first looks up the resource locations for the assets and uses these IResourceLocation instances to download the required AssetBundles and any dependencies. You can perform the asset load operation in two steps by first getting the IResourceLocation objects with LoadResourceLocationsAsync and then using those objects as keys to load or instantiate the assets.

IResourceLocation objects contain the information needed to load one or more assets.

The LoadResourceLocationsAsync method never fails. If it cannot resolve the specified keys to the locations of any assets, it returns an empty list. You can restrict the types of asset locations returned by the function by specifying a specific type in the type parameter.

The following example loads locations for all assets labeled with "knight" or "villager":


AsyncOperationHandle> handle
    = Addressables.LoadResourceLocationsAsync(
        newstring[] {"knight", "villager"},
        Addressables.MergeMode.Union);

yieldreturn handle;

//...

Addressables.Release(handle);

Loading locations of subobjects

Locations for SubObjects are generated at runtime to reduce the size of the content catalogs and improve runtime performance. When you call LoadResourceLocationsAsync with the key of an asset with subobjects and don't specify a type, then the function generates IResourceLocation instances for all of the subobjects as well as the main object (if applicable). Likewise, if you do not specify which subobject to use for an AssetReference that points to an asset with subobjects, then the system generates IResourceLocations for every subobject.

For example, if you load the locations for an FBX asset, with the address, "myFBXObject", you might get locations for three assets: a GameObject, a Mesh, and a Material. If, instead, you specified the type in the address, "myFBXObject[Mesh]", you would only get the Mesh object. You can also specify the type using the type parameter of the LoadResourceLocationsAsync function.

Asynchronous Loading

The Addressables system API is asynchronous and returns an AsyncOperationHandle for use with managing operation progress and completion. Addressables is designed to content location agnostic. The content may need to be downloaded first or use other methods that can take a long time. To force synchronous execution, See Synchronous Addressables for more information.

When loading an asset for the first time, the handle is done after a minimum of one frame. You can wait until the load has completed using different methods as shown below. If the content has already been loaded, execution times may differ between the various asynchronous loading options shown below.

  • Coroutine: Always be delayed at minimum of one frame before execution continues.

  • Completed callback: Is a minimum of one frame if the content has not already been loaded, otherwise the callback is invoked in the same frame.

  • Awaiting AsyncOperationHandle.Task: Is a minimum of one frame if the content has not already been loaded, otherwise the execution continues in the same frame.

using System.Collections;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

internalclassAsynchronousLoading : MonoBehaviour
{
    privatestring address = "tree";
    private AsyncOperationHandle loadHandle;

    // always minimum of 1 frameIEnumerator LoadAssetCoroutine()
    {
        loadHandle = Addressables.LoadAssetAsync(address);
        yieldreturn loadHandle;
    }

    // minimum of 1 frame for new asset loads// callback called in current frame for already loaded assetsvoidLoadAssetCallback()
    {
        loadHandle = Addressables.LoadAssetAsync(address);
        loadHandle.Completed += h =>
        {
            // Loaded here
        };
    }

    // minimum of 1 frame for new asset loads// await completes in current frame for already loaded assetsasyncvoidLoadAssetWait()
    {
        loadHandle = Addressables.LoadAssetAsync(address);
        await loadHandle.Task;
    }

    privatevoidOnDestroy()
    {
        Addressables.Release(loadHandle);
    }
}

Unloading Addressable assets

See Unloading Addressables.

Using Addressables in a Scene

If a Scene is itself Addressable, you can use Addressable assets in the scene just as you would any assets. You can place Prefabs and other assets in the Scene, assign assets to component properties, and so on. If you use an asset that is not Addressable, that asset becomes an implicit dependency of the Scene and the build system packs it in the same AssetBundle as the Scene when you make a content build. (Addressable assets are packed into their own AssetBundles according to the group they are in.)

NOTE

Implicit dependencies used in more than one place can be duplicated in multiple AssetBundles and in the built-in scene data. Use the Check Duplicate Bundle Dependencies rule in the Analyze tool to find unwanted duplication of assets.

If a Scene is NOT Addressable, then any Addressable assets you add directly to the scene hierarchy become implicit dependencies and Unity includes copies of those assets in the built-in scene data even if they also exist in an Addressable group. The same is true for any assets, such as Materials, assigned to a component on a GameObject in the scene.

In custom component classes, you can use AssetReference fields to allow the assignment of Addressable assets in non-Addressable scenes. Otherwise, you can use addresses and labels to load assets at runtime from a script. Note that you must load an AssetReference in code whether or not the Scene is Addressable.

Operations

Many tasks in the Addressables need to load or download information before they can return a result. To avoid blocking program execution, Addressables implements such tasks as asynchronous operations.

In contrast to a synchronous operation, which doesn’t return control until the result is available, an asynchronous operation returns control to the calling function almost immediately. However, the results may not be available until some time in the future. When you call a function, such as LoadAssetAsync, it doesn't return the loaded assets directly. Instead, it returns an AsyncOperationHandle object, which you can use to access the loaded assets when they become available.

You can use the following techniques to wait for the results of an asynchronous operation (while allowing other scripts to continue processing).

  • Coroutines and IEnumerator loops

  • Events

  • Tasks

NOTE

You can block the current thread to wait for the completion of an asynchronous operation. Doing so can introduce performance problems and frame rate hitches. See Using operations synchronously.

Releasing AsyncOperationHandle instances

Methods, like LoadAssetsAsync, return AsyncOperationHandle instances that both provide the results of the operation and a way to release both the results and the operation object itself. You must retain the handle object for as long as you want to use the results. Depending on the situation, that might be one frame, until the end of a level, or even the lifetime of the application. Use the Addressables.Release function to release operation handles and any associated addressable assets.

Releasing an operation handle decrements the reference count of any assets loaded by the operation and invalidates the operation handle object itself. See Memory management for more information about reference counting in the Addressables system.

In cases where you don’t need to use the results of an operation beyond a limited scope, you can release the handles right away. A few Addressables methods, such as UnloadSceneAsync allow you to automatically release the operation handle when it's complete.

If an operation is unsuccessful, you should still release the operation handle. Normally, Addressables releases any assets that it loaded during a failed operation, but releasing the handle still clears the handle’s instance data. Note that some functions, like LoadAssetsAsync, which load multiple assets, give you the option to either retain any assets that it could load or to fail and release everything if any part of the load operation failed.

Coroutine- and IEnumerator-based operation handling

The AsyncOperationHandle implements the IEnumerator interface and will continue iteration until the operation is complete. In a coroutine, you can yield the operation handle to wait for the next iteration. When complete, the execution flow continues to the following statements. Recall that you can implement the MonoBehaviour Start function as a coroutine, which is a good way to have a GameObject load and instantiate the assets it needs.

The following script loads a Prefab as a child of its GameObject using a Start function coroutine. It yields the AsyncOperationHandle until the operation finishes and then uses the same handle to instantiate the Prefab.

using System.Collections;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

internalclassLoadWithIEnumerator : MonoBehaviour
{
    publicstring address;
    AsyncOperationHandle opHandle;

    public IEnumerator Start()
    {
        opHandle = Addressables.LoadAssetAsync(address);

        // yielding when already done still waits until the next frame// so don't yield if done.if (!opHandle.IsDone)
            yieldreturn opHandle;

        if (opHandle.Status == AsyncOperationStatus.Succeeded)
        {
            Instantiate(opHandle.Result, transform);
        }
        else
        {
            Addressables.Release(opHandle);
        }
    }

    voidOnDestroy()
    {
        Addressables.Release(opHandle);
    }
}

Note that Addressables.LoadAssetsAsync is not able to be canceled once started. However, releasing the handle before it has finished will decrement the handle reference count and it will automatically release when the load is complete.

See Coroutines for more information.

Grouping operations in a coroutine

You will probably encounter situations in which you want to perform several operations before moving on to the next step in your game logic. For example, you want to load a number of Prefabs and other assets before you start a level.

If the operations all load assets, you can combine them with a single call to the Addressables.LoadAssetsAsync function. The AsyncOperationhandle for this method works the same as LoadAssetAsync; you can yield the handle in a coroutine to wait until all the assets in the operation load. In addition, you can pass a callback function to LoadAssetsAsync and the operation calls that function when it finishes loading a specific asset. See Loading multiple assets for an example.

Another option is to use the ResourceManager.CreateGenericGroupOperation to create a group operation that completes when all of its members finish.

Event-based operation handling

You can add a delegate function to the Completed event of an AsyncOperationHandle. The operation calls the delegate function when it's finished.

The following script performs the same function as the example in Coroutine- and IEnumerator-based operation handling, but uses an event delegate instead of a coroutine.

using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

internalclassLoadWithEvent : MonoBehaviour
{
    publicstring address;
    AsyncOperationHandle opHandle;

    voidStart()
    {
        // Create operation
        opHandle = Addressables.LoadAssetAsync(address);
        // Add event handler
        opHandle.Completed += Operation_Completed;
    }

    privatevoidOperation_Completed(AsyncOperationHandle obj)
    {
        if (obj.Status == AsyncOperationStatus.Succeeded)
        {
            Instantiate(obj.Result, transform);
        }
        else
        {
            Addressables.Release(obj);
        }
    }

    voidOnDestroy()
    {
        Addressables.Release(opHandle);
    }
}

Note that the handle instance passed to the event delegate is the same as that returned by the original function call. You can use either to access the results and status of the operation and, ultimately, to release the operation handle and loaded assets.

Task-based operation handling

The AsyncOperationHandle provides a Task object that you can use with the C# async and await keywords to sequence code that calls asynchronous functions and handles the results.

The following example loads Addressable assets using a list of keys. The differences between this task-based approach and the coroutine or event-based approaches are in the signature of the calling function, which must include the async keyword and the use of the await keyword with the operation handle’s Task property. The calling function, Start() in this case, suspends operation while the task finishes. Execution then resumes and the example instantiates all the loaded Prefabs (in a grid pattern).

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

internalclassLoadWithTask : MonoBehaviour
{
    // Label or address strings to loadpublic List keys = new List() {"characters", "animals"};

    // Operation handle used to load and release assets
    AsyncOperationHandle> loadHandle;

    publicasyncvoidStart()
    {
        loadHandle = Addressables.LoadAssetsAsync(
            keys, // Either a single key or a List of keys 
            addressable =>
            {
                // Called for every loaded asset
                Debug.Log(addressable.name);
            }, Addressables.MergeMode.Union, // How to combine multiple labels false); // Whether to fail if any asset fails to load// Wait for the operation to finish in the backgroundawait loadHandle.Task;

        // Instantiate the resultsfloat x = 0, z = 0;
        foreach (var addressable in loadHandle.Result)
        {
            if (addressable != null)
            {
                Instantiate(addressable,
                    new Vector3(x++ * 2.0f, 0, z * 2.0f),
                    Quaternion.identity,
                    transform); // make child of this objectif (x > 9)
                {
                    x = 0;
                    z++;
                }
            }
        }
    }

    privatevoidOnDestroy()
    {
        Addressables.Release(loadHandle);
        // Release all the loaded assets associated with loadHandle// Note that if you do not make loaded addressables a child of this object,// then you will need to devise another way of releasing the handle when// all the individual addressables are destroyed.
    }
}
IMPORTANT

The AsyncOperationHandle.Task property is not available on the Unity WebGL platform, which doesn't support multitasking.

When you use Task-based operation handling, you can use the C# Task class methods such as WhenAll to control which operations you run in parallel and which you want to run in sequence. The following example illustrates how to wait for more than one operation to finish before moving onto the next task:

// Load the Prefabsvar prefabOpHandle = Addressables.LoadAssetsAsync(
    keys, null, Addressables.MergeMode.Union, false);

// Load a Scene additivelyvar sceneOpHandle
    = Addressables.LoadSceneAsync(nextScene,
        UnityEngine.SceneManagement.LoadSceneMode.Additive);

await System.Threading.Tasks.Task.WhenAll(prefabOpHandle.Task, sceneOpHandle.Task);

Using operations synchronously

You can wait for an operation to finish without yielding, waiting for an event, or using async await by calling an operation’s WaitForCompletion method. This method blocks the current program execution thread while it waits for the operation to finish before continuing in the current scope.

Avoid calling WaitForCompletion on operations that can take a significant amount of time, such as those that must download data. Calling WaitForCompletion can cause frame hitches and interrupt UI responsiveness.

In Unity 2020.1 or earlier, Unity also waits for all other pending asynchronous operations to finish, so the delay in execution can be much longer than that required for just the single operation for which you call this method. In Unity 2020.2 or later, the performance impact can be less pronounced, at least when loading assets that have already been downloaded.

The following example loads a Prefab asset by address, waits for the operation to complete, and then instantiates the Prefab:

using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

internalclassLoadSynchronously : MonoBehaviour
{
    publicstring address;
    AsyncOperationHandle opHandle;

    voidStart()
    {
        opHandle = Addressables.LoadAssetAsync(address);
        opHandle.WaitForCompletion(); // Returns when operation is completeif (opHandle.Status == AsyncOperationStatus.Succeeded)
        {
            Instantiate(opHandle.Result, transform);
        }
        else
        {
            Addressables.Release(opHandle);
        }
    }

    voidOnDestroy()
    {
        Addressables.Release(opHandle);
    }
}

Custom operations

To create a custom operation, extend the AsyncOperationBase class and override its virtual methods.

You can pass the derived operation to the ResourceManager.StartOperation method to start the operation and receive an AsyncOperationHandle struct. The ResourceManager registers operations started this way and shows them in the Addressables Event Viewer.

Executing the operation

The ResourceManager invokes the AsyncOperationBase.Execute method for your custom operation once the optional dependent operation completes.

Completion handling

When your custom operation completes, call AsyncOperationBase.Complete on your custom operation object. You can call this within the Execute method or defer it to outside the call. AsyncOperationBase.Complete notifies the ResourceManager that the operation has finished. The ResourceManager invokes the associated AsyncOperationHandle.Completed events for the relevant instances of the custom operation.

Terminating the operation

The ResourceManager invokes the AsyncOperationBase.Destroy method for your custom operation when the operation AsyncOperationBase.ReferenceCount reaches zero. The AsyncOperationBase.ReferenceCount is decreased when the AsyncOperationHandle that references it is released using Addressables.Release or when AsyncOperationBase.DecrementReferenceCount is called by a custom operation internally. AsyncOperationBase.Destroy is where you should release any memory or resources associated with your custom operation.

Using typed versus typeless operation handles

Most Addressables methods that start an operation return a generic AsyncOperationHandle struct, allowing type safety for the AsyncOperationHandle.Completed event and for the AsyncOperationHandle.Result object. You can also use a non-generic AsyncOperationHandle struct and convert between the two handle types as desired.

Note that a runtime exception occurs if you attempt to cast a non-generic handle to a generic handle of an incorrect type. For example:

// Load asset using typed handle:
AsyncOperationHandle textureHandle = Addressables.LoadAssetAsync("mytexture");

// Convert the AsyncOperationHandle to an AsyncOperationHandle:
AsyncOperationHandle nonGenericHandle = textureHandle;

// Convert the AsyncOperationHandle to an AsyncOperationHandle:
AsyncOperationHandle textureHandle2 = nonGenericHandle.Convert();

// This will throw and exception because Texture2D is required:
AsyncOperationHandle textureHandle3 = nonGenericHandle.Convert();

Reporting operation progress

AsyncOperationHandle has two methods that you can use to monitor and report the progress of the operation:

  • GetDownloadStatus returns a DownloadStatus struct. This struct contains information about how many bytes have been downloaded and how many bytes still need to be downloaded. The DownloadStatus.Percent reports the percentage of bytes downloaded.

  • AsyncOperationHandle.PercentComplete returns an equally-weighted aggregate percentage of all the sub-operations that are complete. For example, if an operation has five sub-operations, each of them represents 20% of the total. The value doesn't factor in the amount of data that must be downloaded by the individual sub-operations.

For example, if you called Addressables.DownloadDependenciesAsync and five AssetBundles needed to be downloaded, GetDownloadStatus would tell you what percentage of the total number of bytes for all sub-operations had been downloaded so far. PercentComplete would tell you what percentage of the number of operations had finished, regardless of their size.

On the other hand, if you called LoadAssetAsync, and one bundle had to be downloaded before an asset could be loaded from it, the download percentage might be misleading. The values obtained from GetDownloadStatus would reach 100% before the operation finished, because the operation had additional sub-operations to conduct. The value of PercentComplete would be 50% when the download sub-operation finished and 100% when the actual load into memory was complete.

Loading a single asset

Use the LoadAssetAsync method to load a single Addressable asset, typically with an address as the key:

using System.Collections;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

internalclassLoadAddress : MonoBehaviour
{
    publicstring key;
    AsyncOperationHandle opHandle;

    public IEnumerator Start()
    {
        opHandle = Addressables.LoadAssetAsync(key);
        yieldreturn opHandle;

        if (opHandle.Status == AsyncOperationStatus.Succeeded)
        {
            GameObject obj = opHandle.Result;
            Instantiate(obj, transform);
        }
    }

    voidOnDestroy()
    {
        Addressables.Release(opHandle);
    }
}
NOTE

You can use a label or other type of key when you call LoadAssetAsync, not just an address. However, if the key resolves to more than one asset, only the first asset found is loaded. For example, if you call this method with a label applied to several assets, Addressables returns whichever one of those assets that happens to be located first.

///

Loading multiple assets

Use the LoadAssetsAsync method to load more than one Addressable asset in a single operation. When using this function, you can specify a single key, such as a label, or a list of keys.

When you specify multiple keys, you can specify a merge mode to determine how the sets of assets matching each key are combined:

  • Union : include assets that match any key

  • Intersection : include assets that match every key

  • UseFirst: include assets only from the first key that resolves to a valid location

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

internalclassLoadMultiple : MonoBehaviour
{
    // Label strings to loadpublic List keys = new List() {"characters", "animals"};

    // Operation handle used to load and release assets
    AsyncOperationHandle> loadHandle;

    // Load Addressables by Labelpublic IEnumerator Start()
    {
        float x = 0, z = 0;
        loadHandle = Addressables.LoadAssetsAsync(
            keys,
            addressable =>
            {
                //Gets called for every loaded asset
                Instantiate(addressable,
                    new Vector3(x++ * 2.0f, 0, z * 2.0f),
                    Quaternion.identity,
                    transform);

                if (x > 9)
                {
                    x = 0;
                    z++;
                }
            }, Addressables.MergeMode.Union, // How to combine multiple labels false); // Whether to fail and release if any asset fails to loadyieldreturn loadHandle;
    }

    privatevoidOnDestroy()
    {
        Addressables.Release(loadHandle);
        // Release all the loaded assets associated with loadHandle// Note that if you do not make loaded addressables a child of this object,// then you will need to devise another way of releasing the handle when// all the individual addressables are destroyed.
    }
}

You can specify how to handle loading errors with the releaseDependenciesOnFailure parameter. If true, then the operation fails if it encounters an error loading any single asset. The operation and any assets that did successfully load are released.

If false, then the operation loads any objects that it can and does not release the operation. In the case of failures, the operation still completes with a status of Failed. In addition, the list of assets returned has null values where the failed assets would otherwise appear.

Set releaseDependenciesOnFailure to true when loading a group of assets that must be loaded as a set in order to be used. For example, if you are loading the assets for a game level, it might make sense to fail the operation as a whole rather than load only some of the required assets.

//

Loading an AssetReference

The AssetReference class has its own load method, LoadAssetAsync.

using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

internalclassLoadFromReference : MonoBehaviour
{
    // Assign in Editorpublic AssetReference reference;

    // Start the load operation on startvoidStart()
    {
        AsyncOperationHandle handle = reference.LoadAssetAsync();
        handle.Completed += Handle_Completed;
    }

    // Instantiate the loaded prefab on completeprivatevoidHandle_Completed(AsyncOperationHandle obj)
    {
        if (obj.Status == AsyncOperationStatus.Succeeded)
        {
            Instantiate(reference.Asset, transform);
        }
        else
        {
            Debug.LogError("AssetReference failed to load.");
        }
    }

    // Release asset when parent object is destroyedprivatevoidOnDestroy()
    {
        reference.ReleaseAsset();
    }
}

You can also use the AssetReference object as a key to the Addressables.LoadAssetAsync methods. If you need to spawn multiple instances of the asset assigned to an AssetReference, use Addressables.LoadAssetAsync, which gives you an operation handle that you can use to release each instance.

See AssetReference for more information about using AssetReferences.

//

Loading Scenes

Use the Addressables.LoadSceneAsync method to load an Addressable Scene asset by address or other Addressable key object.

NOTE

Addressables.LoadSceneAsync uses the Unity Engine SceneManager.LoadSceneAsync method internally. API that affects the behaviour of SceneManager.LoadSceneAsync will most likely affect Addressables.LoadSceneAsync in the same way, for example Application.backgroundLoadingPriority.

The remaining parameters of the method correspond to those used with the SceneManager.LoadSceneAsync method:

  • loadMode: whether to add the loaded Scene into the current Scene or to unload and replace the current Scene.

  • loadSceneParameters: includes loadMode in addition to localPhysicsMode, used when loading the Scene to specify whether a 2D and/or 3D physics Scene should be created

  • activateOnLoad: whether to activate the scene as soon as it finishes loading or to wait until you call the SceneInstance object's ActivateAsync method. Corresponds to the AsyncOperation.allowSceneActivation option. Defaults to true.

  • priority: the priority of the AsyncOperation used to load the Scene. Corresponds to the AsyncOperation.priority option. Defaults to 100.

WARNING

Setting the activateOnLoad parameter to false blocks the AsyncOperation queue, including the loading of any other Addressable assets, until you activate the scene. To activate the scene, call the ActivateAsync method of the SceneInstance returned by LoadSceneAsync. See AsyncOperation.allowSceneActivation for additional information.

The following example loads a scene additively. The Component that loads the Scene, stores the operation handle and uses it to unload and release the Scene when the parent GameObject is destroyed.

using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceProviders;
using UnityEngine.SceneManagement;

internalclassLoadSceneByAddress : MonoBehaviour
{
    publicstring key; // address stringprivate AsyncOperationHandle loadHandle;

    voidStart()
    {
        loadHandle = Addressables.LoadSceneAsync(key, LoadSceneMode.Additive);
    }

    voidOnDestroy()
    {
        Addressables.UnloadSceneAsync(loadHandle);
    }
}

See the Scene loading project in the Addressables-Sample repository for additional examples.

If you load a Scene with LoadSceneMode.Single, the Unity runtime unloads the current Scene and calls Resources.UnloadUnusedAssets. See Releasing Addressable assets for more information.

NOTE

In the Editor, you can always load scenes in the current project, even when they are packaged in a remote bundle that is not available and you set the Play Mode Script to Use Existing Build. The Editor loads the Scene using the asset database.

///

Synchronous Workflow

Synchronous Addressables APIs help to more closely mirror Unity asset loading workflows. AsyncOperationHandles now have a method called WaitForCompletion() that force the async operation to complete and return the Result of the operation.

API

TObject WaitForCompletion()

Result

The result of WaitForCompletion is the Result of the async operation it is called on. If the operation fails, this returns default(TObject).

It is possible to get a default(TObject) for a result when the operation doesn't fail. Async operations that auto release their AsyncOperationHandles on completion are such cases. Addressables.InitializeAsync() and any API with a autoReleaseHandle parameter set to true will return default(TObject) even though the operations themselves succeeded.

Performance

It is worth noting that calling WaitForCompletion may have performance implications on your runtime when compared to Resources.Load or Instantiate calls directly. If your AssetBundle is local or has been previously downloaded and cached, these performance hits are likely to be negligible. However, this may not be the case for your individual project setup.

All currently active Asset Load operations are completed when WaitForCompletion is called on any Asset Load operation, due to how Async operations are handled in the Engine. To avoid unexpected stalls, use WaitForCompletion when the current operation count is known, and the intention is for all active operations to complete synchronously.

When using WaitForCompletion, there are performance implications. When using 2021.2.0 or newer, these are minimal. Using an older version can result in delays that scale with the number of Engine Asset load calls that are loading when WaitForCompletion is called.

It is not recommended that you call WaitForCompletion on an operation that is going to fetch and download a remote AssetBundle. Though, it is possible if that fits your specific situation.

Code Sample

voidStart()
{
    //Basic use case of forcing a synchronous load of a GameObjectvar op = Addressables.LoadAssetAsync("myGameObjectKey");
    GameObject go = op.WaitForCompletion();

    //Do work...

    Addressables.Release(op);
}

Scenes

Due to engine limitations scenes cannot be completed synchronously. Calling WaitForCompletion on an operation returned from Addressables.LoadSceneAsync will not completely load the scene, even if activateOnLoad is set to true. It will wait for dependencies and assets to complete but the scene activation must be done asynchronously. This can be done using the sceneHandle, or by the AsyncOperation from ActivateAsync on the SceneInstance as shown below.

IEnumerator LoadScene(string myScene){
    var sceneHandle = Addressables.LoadSceneAsync(myScene, LoadSceneMode.Additive);
    SceneInstance sceneInstance = sceneHandle.WaitForCompletion();
    yield return sceneInstance.ActivateAsync();

    //Do work... the scene is now complete and integrated
}
NOTE

Unloading a scene cannot be completed synchronously. Calling WaitForCompleted on a scene unload will not unload the scene or any assets, and a warning will be logged to the console.

NOTE

Due to limitations with Scene integration on the main thread through the SceneManager API, it is possible to lock the Editor or Player when calling WaitForCompletion in association with scene loading. The issue primarily surfaces when loading two scenes in succession, with the second scene load request having WaitForCompletion called from its AsyncOperationHandle. Since scene loading takes extra frames to fully integrate on the main thread, and WaitForCompletion locks the main thread, you could hit a situation where Addressables has been informed by the SceneManager that the first scene is fully loaded, even though it hasn't completed finished all the required operations. At this point, the scene is fully loaded, but the SceneManager attempts to call UnloadUnusedAssets, on the main thread, if the scene was loaded in Single mode. Then, the second scene load request locks the main thread with WaitForCompletion, but cannot begin loading because SceneManager requires the UnloadUnusedAssets to complete before the next scene can begin loading. In order to avoid this deadlock, it is advised that you either load successive scenes asynchronously, or ensure a sufficient delay is added between scene load requests.

Synchronous Addressables with Custom Operations

Addressables supports custom AsyncOperations which support unique implementations of InvokeWaitForCompletion. This method can be overridden to implement custom synchronous operations.

Custom operations work with ChainOperations and GroupsOperations. If you require chained operations to be completed synchronously, ensure that your custom operations implement InvokeWaitForCompletion and create a ChainOperation using your custom operations. Similarly, GroupOperations are well suited to ensure a collection of AsyncOperations, including custom operations, complete together. Both ChainOperation and GroupOperation have their own implementations of InvokeWaitForCompletion that relies on the InvokeWaitForCompletion implementations of the operations they depend on.

WebGL

WebGL does not support WaitForCompletion. On WebGL, all files are loaded using a web request. On other platforms, a web request gets started on a background thread and the main thread spins in a tight loop while waiting for the web request to finish. This is how Addressables does it for WaitForCompletion when a web request is used.

Since WebGL is single-threaded, the tight loop blocks the web request and the operation is never allowed to finish. If a web request finishes the same frame it was created, then WaitForCompletion wouldn't have any issue. However, we cannot guarantee this to be the case, and likely it isn't the case for most instances.

///

Transforming resource URLs

Addressables provides the following ways to modify the URLs it uses to load assets at runtime:

  • Static properties in a Profile variable

  • Implementing an ID transform function

  • Implementing a WebRequestOverride method

Static Profile variables

You can use a static property when defining the RemoteLoadPath Profile variable to specify all or part of the URL from which your application loads remote content, including catalogs, catalog hash files, and AssetBundles. See Profile variable syntax for information about specifying a property name in a Profile variable. The value of your static property must be set before Addressables initializes. Changing the value after initialization has no effect.

ID transform function

You can assign a function to the Addressables.ResourceManager object's InternalIdTransformFunc property to individually change the URLs from which Addressables loads assets. You must assign the function before the relevant operation starts, otherwise the default URL is used.

Using TransformInternalId grants a fair amount of flexibility, especially in regards to remote hosting. Given a single IResourceLocation, you can transform the ID to point towards a server specified at runtime. This is particularly useful if your server IP address changes or if you use different URLS to provide different variants of your application assets.

The ResourceManager calls your TransformInternalId function when it looks up an asset, passing the IResourceLocation instance for the asset to your function. You can change the InternalId property of this IResourceLocation and return the modified object to the ResourceManager.

The following example illustrates how you could append a query string to all URLs for AssetBundles:

using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.ResourceManagement.ResourceProviders;
using UnityEngine.AddressableAssets;

staticclassIDTransformer
{
    //Implement a method to transform the internal ids of locationsstaticstringMyCustomTransform(IResourceLocation location)
    {
        if (location.ResourceType == typeof(IAssetBundleResource)
            && location.InternalId.StartsWith("http", System.StringComparison.Ordinal))
            return location.InternalId + "?customQueryTag=customQueryValue";

        return location.InternalId;
    }

    //Override the Addressables transform method with your custom method.//This can be set to null to revert to default behavior.
    [RuntimeInitializeOnLoadMethod]
    staticvoidSetInternalIdTransform()
    {
        Addressables.InternalIdTransformFunc = MyCustomTransform;
    }
}

WebRequest override

You can assign a function to the Addressables object's WebRequestOverride property to individually modify the UnityWebRequest from which is used to download files, such as an AssetBundle or catalog json file. You must assign the function before the relevant operation starts, otherwise the default UnityWebRequest is used.

The ResourceManager calls your WebRequestOverride function before UnityWebRequest.SendWebRequest is called. Passing the UnityWebRequest for the download to your function.

The following example illustrates how you could append a query string to all URLs for AssetBundles and catalogs:

using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.AddressableAssets;

internalclassWebRequestOverride : MonoBehaviour
{
    //Register to override WebRequests Addressables creates to downloadprivatevoidStart()
    {
        Addressables.WebRequestOverride = EditWebRequestURL;
    }

    //Override the url of the WebRequest, the request passed to the method is what would be used as standard by Addressables.privatevoidEditWebRequestURL(UnityWebRequest request)
    {
        if (request.url.EndsWith(".bundle", StringComparison.OrdinalIgnoreCase))
            request.url = request.url + "?customQueryTag=customQueryValue";
        elseif (request.url.EndsWith(".json", StringComparison.OrdinalIgnoreCase) || request.url.EndsWith(".hash", StringComparison.OrdinalIgnoreCase))
            request.url = request.url + "?customQueryTag=customQueryValue";
    }
}

///

Preloading dependencies

When you distribute content remotely, you can sometimes improve perceived performance by downloading dependencies in advance of when your application needs them. For example, you can download essential content on start up the first time a player launches your game to make sure that they don't have to wait for content in the middle of game play.

Downloading dependencies

Use the Addressables.DownloadDependenciesAsync method to make sure that all the dependencies needed to load an Addressable key are available either in local content installed with the app or the download cache.

string key = "assetKey";

// Check the download size
AsyncOperationHandle getDownloadSize = Addressables.GetDownloadSizeAsync(key);
yieldreturn getDownloadSize;

//If the download size is greater than 0, download all the dependencies.if (getDownloadSize.Result > 0)
{
    AsyncOperationHandle downloadDependencies = Addressables.DownloadDependenciesAsync(key);
    yieldreturn downloadDependencies;
}
TIP

if you have a set of assets that you want to pre-download, you can assign the same label, such as "preload", to the assets and use that label as the key when calling Addressables.DownloadDependenciesAsync. Addressables downloads all the AssetBundles containing an asset with that label if not already available (along with any bundles containing the assets' dependencies).

Progress

An AsyncOperationHandle instance provides two ways to get progress:

  • AsyncOperationHandle.PercentComplete: reports the percentage of sub-operations that have finished. For example, if an operation uses six sub-operations to perform its task, the PercentComplete indicates the entire operation is 50% complete when three of those operations have finished (it doesn't matter how much data each operation loads).

  • AsyncOperationHandle.GetDownloadStatus: returns a DownloadStatus struct that reports the percentage in terms of total download size. For example, if an operation has six sub-operations, but the first operation represented 50% of the total download size, then GetDownloadStatus indicates the operation is 50% complete when the first operation finishes.

The following example illustrates how you could use GetDownloadStatus to check the status and dispatch progress events during the download:

using System.Collections;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.Events;
using UnityEngine.ResourceManagement.AsyncOperations;

internalclassPreloadWithProgress : MonoBehaviour
{
    publicstring preloadLabel = "preload";
    public UnityEvent ProgressEvent;
    public UnityEvent CompletionEvent;
    private AsyncOperationHandle downloadHandle;

    IEnumerator Start()
    {
        downloadHandle = Addressables.DownloadDependenciesAsync(preloadLabel, false);
        float progress = 0;

        while (downloadHandle.Status == AsyncOperationStatus.None)
        {
            float percentageComplete = downloadHandle.GetDownloadStatus().Percent;
            if (percentageComplete > progress * 1.1) // Report at most every 10% or so
            {
                progress = percentageComplete; // More accurate %
                ProgressEvent.Invoke(progress);
            }

            yieldreturnnull;
        }

        CompletionEvent.Invoke(downloadHandle.Status == AsyncOperationStatus.Succeeded);
        Addressables.Release(downloadHandle); //Release the operation handle
    }
}

To discover how much data you need to download in order to load one or more assets, you can call Addressables.GetDownloadSizeAsync:


AsyncOperationHandle getDownloadSize =
    Addressables.GetDownloadSizeAsync(key);

The Result of the completed operation is the number of bytes that must be downloaded. If Addressables has already cached all the required AssetBundles, then Result is zero.

Always release the download operation handle after you have read the Result object. If you don't need to access the results of the download operation, you can automatically release the handle by setting the autoReleaseHandle parameter to true, as shown in the following example:

using System.Collections;
using UnityEngine;
using UnityEngine.AddressableAssets;

internalclassPreload : MonoBehaviour
{
    public IEnumerator Start()
    {
        yieldreturn Addressables.DownloadDependenciesAsync("preload", true);
    }
}

Clearing the dependency cache

If you want to clear any AssetBundles cached by Addressables, call Addressables.ClearDependencyCacheAsync. This function clears the cached AssetBundles containing the assets identified by a key along with any bundles containing those assets' dependencies.

Note that ClearDependencyCacheAsync only clears assets bundles related to the specified key. If you updated the content catalog such that the key no longer exists or it no longer depends on the same AssetBundles, then these no-longer-referenced bundles remain in the cache until they expire (based on cache settings).

To clear all AssetBundles, you can use functions in the UnityEngine.Caching class.

///

AssetBundle Loading

The Addressables system packs your assets in AssetBundles and loads these bundles "behind the scenes" as you load individual assets. You can control how AssetBundles load which are exposed on the BundledAssetGroupSchema class. You can set these options through the scripting API or under the Advanced options in the inspector of the AddressablesAssetGroup inspector.

UnityWebRequestForLocalBundles

Addressables can load AssetBundles via two engine APIs: UnityWebRequest.GetAssetBundle, and AssetBundle.LoadFromFileAsync. The default behavior is to use AssetBundle.LoadFromFileAsync when the AssetBundle is in local storage and use UnityWebRequest when the AssetBundle path is a URL.

You can override this behavior to use UnityWebRequest for local Asset Bundles by setting BundledAssetGroupSchema.UseUnityWebRequestForLocalBundles to true. It can also be set through the BundledAssetGroupSchema GUI.

A few of these situations would include:

  1. You are shipping local AssetBundles that use LZMA compression because you want your shipped game package to be as small as possible. In this case, you would want to use UnityWebRequest to recompress those AssetBundles LZ4 into the local disk cache.

  1. You are shipping an Android game and your APK contains AssetBundles that are compressed with the default APK compression.

  1. You want the entire local AssetBundle to be loaded into memory to avoid disk seeks. If you use UnityWebRequest and have caching disabled, the entire AssetBundle file will be loaded into the memory cache. This increases your runtime memory usage, but may improve loading performance as it eliminates disk seeking after the initial AssetBundle load. Both situations 1 and 2 above result in the AssetBundle existing on the player device twice (original and cached representations). This means the initial loads (decompressing and copying to cache) are slower than subsequent loads (loading from cache)

Handling Download Errors

When a download fails, the RemoteProviderException contains errors that can be used to determine how to handle the failure. The RemoteProviderException is either the AsyncOperationHandle.OperationException or an inner exception. As shown below:

using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.Exceptions;

internalclassHandleDownloadError : MonoBehaviour
{
    private AsyncOperationHandle m_Handle;

    voidLoadAsset()
    {
        m_Handle = Addressables.LoadAssetAsync("addressKey");
        m_Handle.Completed += handle =>
        {
            string dlError = GetDownloadError(m_Handle);
            if (!string.IsNullOrEmpty(dlError))
            {
                // handle what error
            }
        };
    }

    stringGetDownloadError(AsyncOperationHandle fromHandle)
    {
        if (fromHandle.Status != AsyncOperationStatus.Failed)
            returnnull;

        RemoteProviderException remoteException;
        System.Exception e = fromHandle.OperationException;
        while (e != null)
        {
            remoteException = e as RemoteProviderException;
            if (remoteException != null)
                return remoteException.WebRequestResult.Error;
            e = e.InnerException;
        }

        returnnull;
    }
}

Possible error strings:

  • "Request aborted"

  • "Unable to write data"

  • "Malformed URL"

  • "Out of memory"

  • "No Internet Connection"

  • "Encountered invalid redirect (missing Location header?)"

  • "Cannot modify request at this time"

  • "Unsupported Protocol"

  • "Destination host has an erroneous SSL certificate"

  • "Unable to load SSL Cipher for verification"

  • "SSL CA certificate error"

  • "Unrecognized content-encoding"

  • "Request already transmitted"

  • "Invalid HTTP Method"

  • "Header name contains invalid characters"

  • "Header value contains invalid characters"

  • "Cannot override system-specified headers"

  • "Backend Initialization Error"

  • "Cannot resolve proxy"

  • "Cannot resolve destination host"

  • "Cannot connect to destination host"

  • "Access denied"

  • "Generic/unknown HTTP error"

  • "Unable to read data"

  • "Request timeout"

  • "Error during HTTP POST transmission"

  • "Unable to complete SSL connection"

  • "Redirect limit exceeded"

  • "Received no data in response"

  • "Destination host does not support SSL"

  • "Failed to transmit data"

  • "Failed to receive data"

  • "Login failed"

  • "SSL shutdown failed"

  • "Redirect limit is invalid"

  • "Not implemented"

  • "Data Processing Error, see Download Handler error"

  • "Unknown Error"

Unloading Addressable assets

The Addressables system uses reference counting to determine whether an asset is in use - as a result, you must release every asset that you load or instantiate when you're done with it. See Memory Management for more information.

When you unload a Scene, the AssetBundle it belongs to is unloaded. This unloads assets associated with the Scene, including any GameObjects moved from the original Scene to a different Scene.

Unity automatically calls UnloadUnusedAssets when it loads a Scene using the LoadSceneMode.Single mode. To prevent the Scene and its assets from being unloaded, maintain a reference to the scene load operation handle until the Scene should be unloaded manually. You can do this by using ResourceManager.Acquire on the load operation handle. Conventional methods of preserving the assets such as Object.DontDestroyOnLoad or HideFlags.DontUnloadUnusedAsset will not work.

Individual Addressables and their operation handles that you loaded separately from the Scene are not released. You must call Resources.UnloadUnusedAssets or UnloadAsset to free these assets. (The exception to this is that any Addressable assets that you instantiated using Addressables.InstantiateAsync with trackHandle set to true, the default, are automatically released.)

/

Managing catalogs at runtime

By default, the Addressables system manages the catalog automatically at runtime. If you built your application with a remote catalog, the Addressables system automatically checks to see if you have uploaded a new catalog, and, if so, downloads the new version and loads it into memory.

You can load additional catalogs at runtime. For example, you could load a catalog produced by a separate, compatible project to load Addressable assets built by that project. (See Loading Content from Multiple Projects.)

If you want to change the default catalog update behavior of the Addressables system, you can turn off the automatic check and check for updates manually. See Updating catalogs.

Loading additional catalogs

Use Addressables.LoadContentCatalogAsync to load additional content catalogs, either from your hosting service or from the local file system. All that is required is for you to supply the location of the catalog you wish to load. After the operation to load the catalog is finished, you can call any Addressables loading functions using the keys in the new catalog.

If you provide the catalog hash file at the same URL as the catalog, Addressables caches the secondary catalog. When the client application loads the catalog in the future, it only downloads a new version of the catalog if the hash changes.

NOTE
  • The hash file does need to be in the same location and have the same name as your catalog. The only difference to the path should be the extension.

  • LoadContentCatalogAsync comes with a parameter autoReleaseHandle. In order for the system to download a new remote catalog, any prior calls to LoadContentCatalogAsync that point to the catalog you're attempting to load need to be released. Otherwise, the system picks up the Content Catalog load operation from our operation cache. If the cached operation is picked up, the new remote catalog is not downloaded. If set to true, the parameter autoReleaseHandle can ensure that the operation doesn't stick around in our operation cache after completing.

Once you load a catalog, you cannot unload it. You can, however, update a loaded catalog. You must release the operation handle for the operation that loaded the catalog before updating a catalog. See Updating catalogs for more information.

In general, there is no reason to hold on to the operation handle after loading a catalog. You can release it automatically by setting the autoReleaseHandle parameter to true when loading a catalog, as shown in the following example:

public IEnumerator Start()
{
    //Load a catalog and automatically release the operation handle.
    AsyncOperationHandle handle
        = Addressables.LoadContentCatalogAsync("path_to_secondary_catalog", true);
    yieldreturn handle;

    //...
}
NOTE

You can use the Catalog Download Timeout property of your Addressables settings to specify a timeout for downloading catalogs.

Updating catalogs

If the catalog hash file is available, Addressables checks the hash when loading a catalog to determine if the version at the provided URL is more recent than the cached version of the catalog. You can turn off the default catalog check, if desired, and call the Addressables.UpdateCatalogs function when you want to update the catalog. If you loaded a catalog manually with LoadContentCatalogAsync, you must release the operation handle before you can update the catalog.

When you call the UpdateCatalog function, all other Addressable requests are blocked until the operation is finished. You can release the operation handle returned by UpdateCatalogs immediately after the operation finishes (or set the autoRelease parameter to true).

If you call UpdateCatalog without providing a list of catalogs, Addressables checks all of the currently loaded catalogs for updates.

IEnumerator UpdateCatalogs()
{
    AsyncOperationHandle> updateHandle
        = Addressables.UpdateCatalogs();

    yieldreturn updateHandle;
    Addressables.Release(updateHandle);
}

You can also call Addressables.CheckForCatalogUpdates directly to get the list of catalogs that have updates and then perform the update:

IEnumerator CheckCatalogs()
{
    List catalogsToUpdate = new List();
    AsyncOperationHandle> checkForUpdateHandle
        = Addressables.CheckForCatalogUpdates();
    checkForUpdateHandle.Completed += op => { catalogsToUpdate.AddRange(op.Result); };

    yieldreturn checkForUpdateHandle;

    if (catalogsToUpdate.Count > 0)
    {
        AsyncOperationHandle> updateHandle
            = Addressables.UpdateCatalogs(catalogsToUpdate);
        yieldreturn updateHandle;
        Addressables.Release(updateHandle);
    }

    Addressables.Release(checkForUpdateHandle);
}
IMPORTANT

If you update a catalog when you have already loaded content from the related AssetBundles, you can encounter conflicts between the loaded AssetBundles and the updated versions. You can enable the Unique Bundle Ids option in your Addressable settings to eliminate the possibility of bundle ID collisions at runtime. However, enabling this option also means that more AssetBundles must typically be rebuilt when you perform a content update. See Content update builds for more information. Another option is to first unload any content and AssetBundles that must be updated, which can be a slow operation.

//

Getting addresses at runtime

By default, Addressables uses the address you assign to an asset as the PrimaryKey value of its IResourceLocation instance. (If you disable the Include Addresses in Catalog option of the Addressables group to which the asset belongs, the PrimaryKey could be a GUID, label, or an empty string.) If you want to get the address of an asset that you load with an AssetReference or label, you can first load the asset's locations, as described in Loading Assets by Location. You can then use the IResourceLocation instance to both access the PrimaryKey value and to load the asset.

The following example gets the address of the asset assigned to an AssetReference object named MyRef1:

var opHandle = Addressables.LoadResourceLocationsAsync(MyRef1);
yieldreturn opHandle;

if (opHandle.Status == AsyncOperationStatus.Succeeded &&
    opHandle.Result != null &&
    opHandle.Result.Count > 0)
{
    Debug.Log("address is: " + opHandle.Result[0].PrimaryKey);
}

Labels often refer to multiple assets. The following example illustrates how to load multiple Prefab assets and use their primary key value to add them to a dictionary:


Dictionary _preloadedObjects
    = new Dictionary();

private IEnumerator PreloadHazards()
{
    //find all the locations with label "SpaceHazards"var loadResourceLocationsHandle
        = Addressables.LoadResourceLocationsAsync("SpaceHazards", typeof(GameObject));

    if (!loadResourceLocationsHandle.IsDone)
        yieldreturn loadResourceLocationsHandle;

    //start each location loading
    List opList = new List();

    foreach (IResourceLocation location in loadResourceLocationsHandle.Result)
    {
        AsyncOperationHandle loadAssetHandle
            = Addressables.LoadAssetAsync(location);
        loadAssetHandle.Completed +=
            obj => { _preloadedObjects.Add(location.PrimaryKey, obj.Result); };
        opList.Add(loadAssetHandle);
    }

    //create a GroupOperation to wait on all the above loads at once. var groupOp = Addressables.ResourceManager.CreateGenericGroupOperation(opList);

    if (!groupOp.IsDone)
        yieldreturn groupOp;

    Addressables.Release(loadResourceLocationsHandle);

    //take a gander at our results.foreach (var item in _preloadedObjects)
    {
        Debug.Log(item.Key + " - " + item.Value.name);
    }
}

/

Modification events

Modification events are used to signal to parts of the Addressables system when certain data is manipulated, such as an AddressableAssetGroup or an AddressableAssetEntry getting added or removed.

Modification events are triggered as part of SetDirty calls inside of Addressables. SetDirty is used to indicate when an asset needs to be re-serialized by the AssetDatabase. As part of this SetDirty, two modification event callbacks can trigger:

  • public static event Action OnModificationGlobal

  • public Action OnModification { get; set; }

which can be found on AddressableAssetSettings through a static, or instance, accessors respectively.

Code Samples

AddressableAssetSettings.OnModificationGlobal += (settings, modificationEvent, data) =>
        {
            if(modificationEvent == AddressableAssetSettings.ModificationEvent.EntryAdded)
            {
                //Do work
            }
        };AddressableAssetSettingsDefaultObject.Settings.OnModification += (settings, modificationEvent, data) =>
        {
            if (modificationEvent == AddressableAssetSettings.ModificationEvent.EntryAdded)
            {
                //Do work
            }
        };

Modification events pass in a generic object for the data associated with the event. Below is a list of the modification events and the data types that are passed with them.

The Data Passed with Each ModificationEvent:

  • GroupAdded The data passed with this event is the AddressableAssetGroup, or list of groups, that were added.

  • GroupRemoved The data passed with this event is the AddressableAssetGroup, or list of groups, that were removed.

  • GroupRenamed The data passed with this event is the AddressableAssetGroup, or list of groups, that were renamed.

  • GroupSchemaAdded The data passed with this event is the AddressableAssetGroup, or list of groups, that had schemas added to them.

  • GroupSchemaRemoved The data passed with this event is the AddressableAssetGroup, or list of groups, that had schemas removed from them.

  • GroupSchemaModified The data passed with this event is the AddressableAssetGroupSchema that was modified.

  • GroupTemplateAdded The data passed with this event is the ScriptableObject, typically one that implements IGroupTemplate, that was the added Group Template object.

  • GroupTemplateRemoved The data passed with this event is the ScriptableObject, typically one that implements IGroupTemplate, that was the removed Group Template object.

  • GroupTemplateSchemaAdded The data passed with this event is the AddressableAssetGroupTemplate that had a schema added.

  • GroupTemplateSchemaRemoved The data passed with this event is the AddressableAssetGroupTemplate that had a schema removed.

  • EntryCreated The data passed with this event is the AddressableAssetEntry that was created.

  • EntryAdded The data passed with this event is the AddressableAssetEntry, or list of entries, that were added.

  • EntryMoved The data passed with this event is the AddressableAssetEntry, or list of entries, that were moved from one group to another.

  • EntryRemoved The data passed with this event is the AddressableAssetEntry, or list of entries, that were removed.

  • LabelAdded The data passed with this event is the string label that was added.

  • LabelRemoved The data passed with this event is the string label that was removed.

  • ProfileAdded The data passed with this event is [BuildProfile] that was added.

  • ProfileRemoved The data passed with this event is the string of the profile ID that was removed.

  • ProfileModified The data passed with this event is [BuildProfile] that was modified, or null if a batch of BuildProfiles were modified.

  • ActiveProfileSet The data passed with this event if the string of the profile ID that is set as the active profile.

  • EntryModified The data passed with this event is the AddressableAssetEntry, or list of entries, that were modified.

  • BuildSettingsChanged The data passed with this event is the AddressableAssetBuildSettings object that was modified.

  • ActiveBuildScriptChanged The data passed with this event is the IDataBuilder build script that was set as the active builder.

  • DataBuilderAdded The data passed with this event is the ScriptableObject, typically one that implements IDataBuilder, that was added to the list of DataBuilders.

  • DataBuilderRemoved The data passed with this event is the ScriptableObject, typically one that implements IDataBuilder, that was removed from the list of DataBuilders.

  • InitializationObjectAdded The data passed with this event is the ScriptableObject, typically one that implements IObjectInitializationDataProvider, that was added to the list of InitializationObjects.

  • InitializationObjectRemoved The data passed with this event is the ScriptableObject, typically one that implements IObjectInitializationDataProvider, that was removed from the list of InitializationObjects.

  • ActivePlayModeScriptChanged The data passed with this event is the IDataBuilder that was set as the new active play mode data builder.

  • BatchModification The data passed with this event is null. This event is primarily used to indicate several modification events happening at the same time and the AddressableAssetSettings object needed to be marked dirty.

  • HostingServicesManagerModified The data passed is either going to be the HostingServicesManager, or the HttpHostingService that were modified.

  • GroupMoved The data passed with this event is the full list of AddressableAssetGroups.

  • CertificateHandlerChanged The data passed with this event is the new System.Type of the Certificate Handler to be used.

///

Diagnostic tools

The Addressables packages provides the following tools for analyzing your Addressables setup, performance, and build results:

  • Analyze tool: provides a set of utilities and reports that you can use to find and fix asset duplication and to see how the system packs the assets in your groups into bundles. The reports and utilities include the following (you can also create your own analyze rule classes to produce additional reports):

  • Check Duplicate Bundle Dependencies

  • Check Resources to Addressable Duplicate Dependencies

  • Check Scene to Addressable Duplicate Dependencies

  • Bundle Layout Preview

  • Profiler Module: provides a runtime profile profiler module integration of your Addressable asset, asset bundle, and object loading and unloading.

  • Event viewer: provides a runtime profile view of your Addressable operations, including asset loading and unloading.

  • Build layout report: describes how Addressables packs the assets in your groups into AssetBundles.

  • Build profile log: provides a build-time profiling file that you can view in a Chromium-based browser.

/

Profiler Module

The Addressables Profiler is a Unity Editor profiler module that can be used to determine what content is loaded from Addressables.

Requirements

  • Basic profiler usage supported from 2021 or newer. To view detailed profiler information 2022.2 or newer is required. All information in this documentation is for editor version 2022.2.

  • Build Reports must be enabled and the runtime being profiled requires a build report. To enable build reports, go to your editor preferences, select the Addressables preferences. Then enable “Debug Build Layout”.

Addressables Asset (4)_第2张图片
  • Collecting information about the running content requires build time data collection information for the debug build layout. These files are stored in the folder /Library/com.unity.addressables/buildReports. Each build you make creates a new build report file in this directory. When running the profiler, any incoming profiler data from the profiler target will be synced and look for the build report file for that runtime in the buildReports folder. If the build report does not exist, such as if the project was built using a different machine. Then the profiler will not display the information for that data. Selecting “Find in file system” will open a file select window. Which can be used to locate a build report file on disk elsewhere.

  • Package Unity Profiling Core API is required for the profiler to run. To install this package either install through the package manager or though the Addressables preferences window when enabling the Debug Build Layout.

  • Running in editor. Profiler module does not support Play Mode Scripts; “Use Asset Database (Fastest)” or “Simulate Groups (Advanced)”. Requiring the content to be built and using “Use Existing Build” based play mode scripts.

Opening the Profiler Module

To enable the profiler; Open the profiler window. Window > Analysis > Profiler. In the top right of the profiler window select the dropdown button labeled Profiler Modules. Enable the option named Addressable Assets.

Addressables Asset (4)_第3张图片

Viewing the Module

The module view can be used to observe how many Asset Bundles, Assets, Scenes and Catalogs are loaded at the frame in time.

Shown below we have 3 Assets and 1 Scene, from 1 Catalog, and 6 Asset Bundles.

Addressables Asset (4)_第4张图片

When a frame is selected. The detail pane will fill with information for that frame. Displaying a tree view for the loaded content. To modify which content is displayed. Select the detail pane toolbar dropdown button “View”. Giving view options.

  • Groups: Includes Groups in the tree view.

  • Asset Bundles: Includes Asset Bundles in the tree view.

  • Assets: Includes Assets in the tree view.

  • Objects: Includes which objects are loaded within an Asset.

  • Assets not loaded: This will also show assets that are within a loaded bundle, but not actively loaded.

The details pane is made up of two regions. On the left side is the Tree View of the content, which is used to view loaded content and is expanded into more in depth content. On the right side is the details inspector, which shows detailed information for the content selected from the Tree View.

Addressables Asset (4)_第5张图片

Content Tree View

The Tree View columns can be enabled or disabled based on your preferences. Context click on the Tree View header to display a list of the available columns.

Addressables Asset (4)_第6张图片

Each column will show information depending on the content in the row.

  • Name: Is the Group name, Asset Bundle file name, Asset will be the address of the asset, or the asset path if the address is not used. Object will be the object name, or asset type for scenes.

  • Type: Is the Type for the Asset or Object.

  • Handles: Is how many Addressables handles are actively holding onto the content. This is often referred to as Reference Count. During loading there is an additional handle to the content.

  • Status: Is what the state of the content at the time, this can be:

  • Queued: When an Asset Bundle is in the download queue.

  • Downloading: When an Asset Bundle is being downloaded.

  • Loading: When the content is being loaded in the background.

  • Active: When the content is loaded and in use.

  • Released: When content has been released (no more active handles to the content) and may still be in memory. See Memory management

  • %: When the status is downloading or loading, this displays the percentage progress of the download or load operation.

  • Source: For Asset Bundles only, will indicate either:

  • Local: The Asset Bundle was loaded from a local file on disk.

  • Cache: The Asset Bundle had previously been downloaded and cached to disk. Loading was from the cached file.

  • Download: The Asset Bundle had not been cached and needed to be downloaded.

  • Refs By: How many of another element references this content element.

  • Refs To: How many other elements the content references.

Released Assets

When content is released from Addressables. It may still be in memory until all content from the Asset Bundle is released, and any other Asset Bundle that can have a dependency on any Asset within the Asset Bundle is also released.

The following screenshot shows the “cube_assets_all.bundle” has some content that is released and some other content (UnityLogo.png) that is still in use. Released content is easier to distinguish by the faded font color.

Addressables Asset (4)_第7张图片

See Memory management for more information about managing the memory associated with your Addressable assets.

Filtering Content

The details view toolbar has a search bar. This can be used to filter by the content Name. Other fields can be filtered using search filter syntax as shown below:

Filter syntax is :. Where the field is a numerical field. Such as “handles:3”, the default equality is =. This can be changed to greater than “>” or less than “<” by including the symbol before the number, for example. “Handles:>2”.

Each filter can be written as the column name (without a space), or with the shorthand tag as below.

  • Handles: “h”

  • Type: “assetType”, “t”

  • Status: “s”

  • RefsTo: “rt”, “r”

  • RefsBy: “rb”, “p”

  • Source: “bundlesource”, “bs”, “b”

The type filter can also be used to filter by inclusion type. Explicit where an Asset is explicitly included into a Group through Addressables. Or Implicit, where the Asset was included into the Asset Bundle because another included references it. E.g. “type:explicit”

Inspecting Content Details

The Inspector pane can be used to gather more detailed information about content. Help on how content is loaded, and reference to and by the selected element.

Below is an example of the inspector for a loaded Asset Bundle.

Addressables Asset (4)_第8张图片

Contains any detailed information. This can be used to get more information about the element you have selected.

Contains any information that may be useful to know. Including any hints for any settings that may not be intended.

Contains references to other Asset Bundles. Here we see that This Asset Bundle has content that references one other Asset Bundle, and there is one other Asset Bundle that has content that references this element.

//

Event Viewer

Use the Addressables Event Viewer window to monitor memory management of your Addressables assets. The window shows when your application loads and unloads assets and displays the reference counts of all Addressables system operations. The window also shows approximate views of the application frame rate and the total amount of allocated managed memory. You can use these charts to detect how Addressables events such as loading and releasing assets affect application performance and to detect assets that you never release.

Use the Use Existing Build Play Mode script to get the most accurate information in the Event Viewer in Play mode. The Use Asset Database script doesn't account for any shared dependencies among the Assets and the Simulate Groups script gives a less accurate monitoring of reference counts.

IMPORTANT

To view data in the Event Viewer, you must enable the Send Profiler Events setting in your AddressableAssetSettings object's Inspector and make a new content build.

See Memory management for more information about managing the memory associated with your Addressable assets.

Viewing Addressables events

View Addressables asset lifespan charts and events in the Event Viewer window:

  1. Enable Send Profiler Events in your Addressables settings: a. Open your Addressable settings Inspector (menu: Window > Asset Management > Addressables > Settings) b. Under Diagnostics, check the Send Profiler Events option.

  1. Rebuild your Addressables content using the Default Build Script from the Addressables Groups window.

  1. Open the Event Viewer (menu: Window > Asset Management > Addressables > Event Viewer).

  1. Enter Play mode in the Editor.

Viewing Addressables events in a standalone player

To connect the Event Viewer to a standalone player, follow the steps under Viewing Addressables Events except for the last step where you enter Play mode in the Editor. Instead, perform the following, additional steps:

  1. Open the Build Settings window (menu: File > Build Settings).

  1. Check the Development Build option.

  1. Check the Autoconnect Profiler option.

  1. Open the Unity Profiler __window (menu: __Window > Analysis > Profiler).

  1. On the Build Settings window, click Build and Run.

The Event Viewer automatically connects to your standalone player and displays the Addressables events that occur.

See Profiler overview for more information about using the Unity Profiler.

Event Viewer window

To access the window in the Editor, select Window > Asset Management > Addressables > Event Viewer.

Addressables Asset (4)_第9张图片

The Event Viewer window

The window has three sections:

  • Toolbar:

  • Clear Events button: clears all recorded frames, erasing everything in the window.

  • Unhide All Hidden Events: returns any asset or operation lifelines that you have hidden to their normal, displayed state. Only shown when you have hidden events.

  • Frame counter: displays the position of the frame cursor and the number of recorded frames. (If the toolbar doesn't display the Frame counter, the frame cursor is at the current frame.)

  • Frame step (<>) buttons: steps the frame cursor through recorded frames. (You can also use the keyboard arrow keys.)

  • Current button: moves the frame cursor to the current frame.

  • Assets display: shows profiler and lifespan charts related to Addressables operations and assets.

  • FPS chart: the application frame rate.

  • MonoHeap chart: the amount of managed memory in use.

  • Event Counts: the number of Addressables events that occurred in a frame (view the events in the Event list).

  • Instantiation Counts: the number of calls to Addressables.InstantiateAsync in a frame.

  • Operation and asset lifespan charts: show when the system loads and releases operations or assets and display the reference counts. See Asset lifespan chart.

  • Event list: shows the Addressable operation events that occurred in the frame.

You can click in the chart area of the window to pause your application and set the position of the frame cursor. The frame cursor appears in the window as a vertical line and shows summary information for the selected frame.

NOTE

The FPS and MonoHeap charts include all factors that affect performance, not just those related to Addressable assets.

Asset lifespan chart

The asset lifespan chart shows when an asset or operation is created, its current reference count, and when it is destroyed.

To expand the display line showing the lifespan of an asset or operation, click the + button for the entry in the Assets list.

To view the sub-operations or subassets of an entry, click the expand icon (>) of the parent object.

You can remove the lifeline of an asset or operation from the chart by right-clicking it and choosing Hide Selected Events on the context menu. Click the Unhide All Hidden Events button on the toolbar to reveal the lifelines of any hidden events.

Addressables Asset (4)_第10张图片

An example asset lifespan and the related Addressables events

A lifespan chart shows the following information:

Before the asset was loaded (no display).

The asset load is in progress (dull blue).

The asset is loaded (blue). The green bar shows the current reference count of the asset.

The Event Viewer frame cursor, which shows information for the selected frame, in this case, the cursor indicates that the reference count of this asset is 4 during the selected frame.

After the asset is destroyed and unloaded (no display).

The events associated with specific frames (events are only shown at the position of the frame cursor).

/

Analyze tool

Analyze is a tool that gathers information on your Projects' Addressables layout. In some cases, Analyze may take appropriate actions to clean up the state of your Project. In others, Analyze is purely an informational tool that allows you to make more informed decisions about your Addressables layout.

Using Analyze

In the Editor, open the Addressables Analyze window (Window > Asset Management > Addressables > Analyze), or open it via the Addressables Groups window by clicking the Tools > Window > Analyze button.

The Analyze window displays a list of Analyze rules, along with the following operations:

Starts the analysis for any selected rules or their children.

Performs the fix action for any selected rules or their children (must be a Fixable rule).

Opens the clear options to clear the results for any selected rules or their children.

Opens the options to import a saved analysis result or export results to disk.

Fixable rules are displayed under the "Fixable Rules" item.

unfixable rules are displayed under the "Unfixable Rules" item.

The analyze operation

The analyze operation gathers the information needed by the rule. Run this action on a rule or set of rules to gather data about the build, dependency maps, and more. Each rule must gather any required data and report it back as a list of AnalyzeResult objects.

No action should be taken to modify any data or the state of the Project during the analyze step. Based on the data gathered in this step, the fix operation may be the appropriate course of action. Some rules, however, only contain an analyze step, as no reasonably appropriate and universal action can be taken based on the information gathered. Check Scene to Addressable Duplicate Dependencies and Check Resources to Addressable Duplicate Dependencies are examples of such rules.

Rules that are purely informational and contain no fix operation are categorized as Unfixable Rules. Those that do have a fix operation are categorized as Fixable Rules.

The clear step

The clear operation removes any data gathered by the analysis and updates the TreeView accordingly.

The fix operation

For Fixable Rules, you may choose to run the fix operation. The fix operation uses data gathered during the analyze step to perform any necessary modifications and resolve the issues.

The provided Check Duplicate Bundle Dependencies rule is an example of a fixable rule. Problems detected by this rule's analysis can be fixed because there is a reasonably appropriate action that can be taken to resolve them.

Provided Analyze rules

Fixable rules

Check Duplicate Bundle Dependencies

This rule checks for potentially duplicated assets, by scanning all groups with BundledAssetGroupSchemas and projecting the asset group layout. This essentially requires triggering a full build, so this check is time-consuming and performance-intensive.

Issues: Duplicated assets result from assets in different groups sharing dependencies, for example two Prefabs that share a material existing in different Addressable groups. That material (and any of its dependencies) would be pulled into both groups containing the Prefabs. To prevent this, the material must be marked as Addressable, either with one of the Prefabs, or in its own space, thereby putting the material and its dependencies in a separate Addressable group.

Resolution: If this check discovers any issues, run the fix operation on this rule to create a new Addressable group to which to move all dependent assets.

Exceptions: If you have an asset containing multiple objects, it is possible for different groups to only pull in portions of the asset, and not actually duplicate. An FBX with many meshes is an example of this. If one mesh is in "GroupA" and another is in "GroupB", this rule will think that the FBX is shared, and extract it into its own group if you run the fix operation. In this edge case, running the fix operation is actually harmful, as neither group would have the full FBX asset.

Also note that duplicate assets may not always be an issue. If assets will never be requested by the same set of users (for example, region-specific assets), then duplicate dependencies may be desired, or at least be inconsequential. Each Project is unique, so fixing duplicate asset dependencies should be evaluated on a case-by-case basis.

Unfixable rules

Check Resources to Addressable Duplicate Dependencies

This rule detects if any assets or asset dependencies are duplicated between built Addressable data and assets residing in a Resources folder.

Issues: These duplicates mean that data will be included in both the application build and the Addressables build.

Resolution: This rule is unfixable, because no appropriate action exists. It is purely informational, alerting you to the redundancy. You must decide how to proceed and what action to take, if any. One example of a possible manual fix is to move the offending asset(s) out of the Resources folder, and make them Addressable.

Check Scene to Addressable Duplicate Dependencies

This rule detects any assets or asset dependencies that are shared between the Scenes in the Editor Scene list and Addressables.

Issues: These duplicates mean that data will be included in both the application build and the Addressables build.

Resolution: It is purely informational, alerting you to the redundancy. You must decide how to proceed and what action to take, if any. One example of a possible manual fix is to pull the built-in Scene(s) with duplicated references out of Build Settings and make it an Addressable Scene.

Bundle Layout Preview

This rule will show how assets explicitly marked as Addressable will be laid out in the Addressable build. Given these explicit assets, we also show what assets are implicitly referenced by, and therefore will be pulled into, the build.

Data gathered by this rule does not indicate any particular issues. It is purely informational.

Extending Analyze

Each unique Project may require additional Analyze rules beyond what comes pre-packaged. The Addressable Assets System allows you to create your own custom rule classes.

See the Custom analyze rule project in the Addressables-Sample repository for an example.

AnalyzeRule objects

Create a new child class of the AnalyzeRule class, overriding the following properties:

  • CanFix tells Analyze if the rule is deemed fixable or not.

  • ruleName is the display name you'll see for this rule in the Analyze window.

You'll also need to override the following methods, which are detailed below:

  • List RefreshAnalysis(AddressableAssetSettings settings)

  • void FixIssues(AddressableAssetSettings settings)

  • void ClearAnalysis()

TIP

If your rule is designated unfixable, you don't have to override the FixIssues method.

RefreshAnalysis

This is your analyze operation. In this method, perform any calculations you'd like and cache any data you might need for a potential fix. The return value is a List list. After you'd gathered your data, create a new AnalyzeResult for each entry in your analysis, containing the data as a string for the first parameter and a MessageType for the second (to optionally designate the message type as a warning or error). Return the list of objects you create.

If you need to make child elements in the TreeView for a particular AnalyzeResult object, you can delineate the parent item and any children with kDelimiter. Include the delimiter between the parent and child items.

FixIssues

This is your fix operation. If there is an appropriate action to take in response to the analyze step, execute it here.

ClearAnalysis

This is your clear operation. Any data you cached in the analyze step can be cleaned or removed in this function. The TreeView will update to reflect the lack of data.

Adding custom rules to the GUI

A custom rule must register itself with the GUI class using AnalyzeSystem.RegisterNewRule(), in order to show up in the Analyze window. For example:

using UnityEditor;
using UnityEditor.AddressableAssets.Build;
using UnityEditor.AddressableAssets.Build.AnalyzeRules;

classMyRule : AnalyzeRule
{
    // Rule code...
}

// Register rule
[InitializeOnLoad]
classRegisterMyRule
{
    staticRegisterMyRule()
    {
        AnalyzeSystem.RegisterNewRule();
    }
}
AnalyzeRule classes

In order to make it faster to setup custom rules, Addressables includes the following classes, which inherit from AnalyzeRule:

  • BundleRuleBase is a base class for handling AnalyzeRule tasks. It includes some basic methods to retrieve information about bundle and resource dependencies.

  • Check bundle duplicates base classes help check for bundle dependency duplicates. Override the FixIssues method implementation to perform some custom action.

  • CheckBundleDupeDependencies inherits from BundleRuleBase and includes further methods for AnalyzeRule to check bundle dependencies for duplicates and a method to attempt to resolve these duplicates.

  • CheckResourcesDupeDependencies is the same, but resource dependencies specific.

  • CheckSceneDupeDependencies is the same, but for scene dependencies specific.

Loading from Multiple Projects

Should your situation require a multi-project workflow, such as a large project broken up across multiple Unity projects that have the Addressables package installed, we have Addressables.LoadContentCatalogAsync to link together code and content across the various projects. Studios with teams that works on many facets of an application simultaneously may find benefit with this workflow.

Setting up multiple projects

The main items to note for a multi-project setup is to make sure:

  1. Each project uses the same version of the Unity Editor

  1. Each project uses the same version of the Addressables package

From there projects can contain whatever you see fit for your given situation. One of your projects must be your "main project" or "source project". This is the project that you'll actually build and deploy your game binaries from. Typically, this source project is primarily comprised of code and very little to no content. The main piece of content that you would want in the primary project would be a bootstrap scene at minimum. It may also be desirable to include any scenes that need to be local for performance purposes before any AssetBundles have had a chance to be downloaded and cached.

Secondary projects are, in most cases, the exact opposite. Mostly content and little to no code. These projects need to have all remote Addressable Groups and Build Remote Catalog turned on. Any local data built into these projects cannot be loaded in your source project's application. Non-critical scenes can live in these projects and be downloaded by the primary project when requested.

The Typical Workflow

Once you have your projects setup, the workflow generally is as follows:

  1. Build remote content for all secondary projects

  1. Build Addressables content for source project

  1. Start source project Play Mode or build source project binaries

  1. In source project, use Addressables.LoadContentCatalogAsync to load the remote catalogs of your other various projects

  1. Proceed with game runtime as normal. Now that the catalogs are loaded Addressables is able to load assets from any of these locations.

In regards to the source project, it should be noted that it may be worth having some minimal amount of content built locally with that project. Each project is unique, and so has unique needs, but having a small set of content needed to run your game in the event of internet connection issues or other various problems may be advisable.

Handling Shaders

Addressables builds a Unity built in shader bundle for each set of Addressables player data that gets built. This means that when multiple AssetBundles are loaded that were built in secondary projects, there could be multiple built in shader bundles loaded at the same time.

Depending on your specific situation, you may need to utilize the Shader Bundle Naming Prefix on the AddressableAssetSettings object. Each built in shader bundle needs to be named different from others built in your other projects. If they're not named differently you'll get The AssetBundle [bundle] can't be loaded because another AssetBundle with the same files is already loaded. errors.

Continuous integration

You can use a Continuous Integration (CI) system to perform your Addressables content builds and your application player builds. This section provides general guidelines for building Addressables with CI systems, but note that every project has its own requirements and constraints, so some guidelines might not apply in all cases.

Selecting a content builder

One of the main choices when building Addressables content is selecting a content builder. By default, if you call AddressableAssetSettings.BuildPlayerContent() it uses the BuildScriptPackedMode script as the IDataBuilder instance. The BuildPlayerContent() function checks the ActivePlayerDataBuilder setting and calls into that script's BuildDataImplementation(..)

If you've implemented your own custom IDataBuilder and want to use it for your CI builds, set the ActivePlayerDataBuilderIndex property of AddressableAssetSettings. By default, you can access the correct settings instance through AddressableAssetSettingsDefaultObject.Settings. This index refers to the position of the IDataBuilder in the list of AddressableAssetSettings.DataBuilders. The following code sample demonstrates how to set a custom IDataBuilder:

#if UNITY_EDITORusing UnityEditor.AddressableAssets;
    using UnityEditor.AddressableAssets.Build;
    using UnityEditor.AddressableAssets.Settings;
    using UnityEngine;

    internalclassCustomDataBuilder
    {
        publicstaticvoidSetCustomDataBuilder(IDataBuilder builder)
        {
            AddressableAssetSettings settings
                = AddressableAssetSettingsDefaultObject.Settings;

            int index = settings.DataBuilders.IndexOf((ScriptableObject)builder);
            if (index > 0)
                settings.ActivePlayerDataBuilderIndex = index;
            elseif (AddressableAssetSettingsDefaultObject.Settings.AddDataBuilder(builder))
                settings.ActivePlayerDataBuilderIndex
                    = AddressableAssetSettingsDefaultObject.Settings.DataBuilders.Count - 1;
            else
                Debug.LogWarning($"{builder} could not be found " +
                                 $"or added to the list of DataBuilders");
        }
    }
#endif

Cleaning the Addressables content builder cache

IDataBuilder implementations define a ClearCachedData() method, which cleans up any files created by that data builder. For example, the default BuildScriptPackedMode script deletes the following:

  • The content catalog

  • The serialized settings file

  • The built AssetBundles

  • Any link.xml files created

You can call IDataBuilder.ClearCachedData() as part of your CI process to make sure the build does not use files generated by previous builds.

Cleaning the Scriptable Build Pipeline cache

Cleaning the Scriptable Build Pipeline (SBP) cache cleans the BuildCache folder from the Library directory along with all the hash maps generated by the build and the Type Database. The Library/BuildCache folder contains .info files created by SBP during the build which speeds up subsequent builds by reading data from these .info files instead of re-generating data that hasn't changed.

To clear the SBP cache in a script without opening a prompt dialog, call BuildCache.PurgeCache(false).

Best Practice

When building Addressables content or player builds with command line arguments or through continuous integration, you should restart the Editor for each target platform. This ensures that Unity can't invoke -executeMethod until after script compilation completes for a platform. For more information about using command line arguments, see Command line arguments.

///

Addressables Samples

The Addressables package provides a few samples that can be downloaded into your project. Samples are accessed via Window > Package Manager > Addressables.

These samples include examples on how to disable asset importing during a build, creating custom build and playmode scripts, as well as providing an AddressableUtility class.

When a sample is downloaded and imported, it is placed inside the Assets/Samples/Addressables/{AddressablesVersionNumber} path of your project.

Addressables Utility

This sample contains a set of utility functions for Addressables. Currently, the script contains a static method GetAddressFromAssetReference which provides the Addressable address used to reference a given AssetRefence internally.

ComponentReference

This sample creates an AssetReference that is restricted to having a specific Component.

For more information see the ComponentReference sample project located in the Addressables Samples repository.

Custom Analyze Rules

This sample shows how to create custom AnalyzeRules for use within the Analyze window. Both rules follow the recommended pattern for adding themselves to the UI.

For more information see the Custom Analyze Rule sample project located in the Addressables Samples repository.

Custom Build and Playmode Scripts Sample

This sample includes two custom scripts: a custom play mode script (located in Editor/CustomPlayModeScript.cs of the Sample) and a custom build script (located in Editor/CustomBuildScript.cs of the Sample). This custom build script creates a build that only includes the currently open scene. A bootstrap scene is automatically created and a script is added that loads the built scene on startup. The custom play mode script works similarly to the Use Existing Build (requires built groups) play mode script already included. The methods added to accomplish this are CreateCurrentSceneOnlyBuildSetup and RevertCurrentSceneSetup on the CustomBuildScript.

For these examples, the build and load paths used by default are [UnityEngine.AddressableAssets.Addressables.BuildPath]/[BuildTarget] and {UnityEngine.AddressableAssets.Addressables.RuntimePath}/[BuildTarget] respectively.

The ScriptableObject of the class has already been created, but the Create menu can be used to make another ScriptableObject if you desire. For this CustomPlayModeScript the create menu path is Addressables/Content Builders/Use CustomPlayMode Script. By default, this creates a CustomPlayMode.asset ScriptableObject. The same goes for the CustomBuildScript.

Disable Asset Import on Build

This sample provides a script that disables asset importing during a player build. This improves build performance since AssetBundles are copied into StreamingAssets at build time. This Sample is only relevant for Editor versions below 2021.2. In 2021.2+, the Editor provides the ability to include folders outside of Assets/ into StreamingAssets.

When the sample is imported into the project, a player build without asset importing can be triggered by the new menu item Build/Disabled Importer Build. The build output is placed into DisabledImporterBuildPath/{EditorUserBuildSettings.activeBuildTarget}/ by default. The sample class DisableAssetImportOnBuild can be edited to alter the build path.

Import Existing Group

This sample contains a tool that imports group assets (for example from a custom package) to the current project.

The tool is located under Window/Asset Management/Addressables/Import Groups. The window requires a path to the AddressableAssetGroup.asset scriptable object, a name for the group, and a folder for any schemas related to the imported AddressableAssetGroup.

Prefab Spawner

This sample provides a basic script that instantiates and destroys a prefab AssetReference.

In order to use the sample, attach the provided script, PrefabSpawnerSample, to a GameObject in your scene. Assign an AdressableAsset to the AssetReference field of that script. If you're using the Use Existing Build playmode script, ensure that your Addressable content is built. Then, enter playmode.

你可能感兴趣的:(Unity3d,游戏引擎,unity,游戏,c#)