Asset Manager Upgrade Guide

Author: Santy-Wang, Xunyi

This article details what to expect when upgrading from loader to assetManager.

Before Creator v2.4, Acquire and load asset was implemented through the loader module (including the APIs loader.load, loader.loadRes, loader.loadResDir, etc.), which was primarily used to load resources. However, with the continuous development of Creator, developers’ demands for resource management have been increasing. The original loader module has been unable to meet a large number of resource management needs, and a new resource management module is in the air.

Therefore, Creator in v2.4 introduced a new resource management module — Asset Manager. Compared to the previous loader module, Asset Manager not only provides better loading performance, but also supports Asset Bundle, preload resources and more convenient resource release management. And Asset Manager also has strong extensibility, which greatly improves the development efficiency and user experience of developers. We recommend that all developers upgrade.

To bring a smooth upgrade experience, we will maintain compatibility with loader related APIs, and most of the game project can run as usual, except for a few that use incompatible special usage APIs that must be manually upgraded. And we will only remove full compatibility with loader when the time comes. If you are temporarily uncomfortable upgrading due to the project cycle, etc., you can keep the original writing while making sure the test passes.

Currently, when using those old APIs, the engine will output warnings and suggestions for upgradation. Please adjust the code according to the warnings and the instructions in this document and upgrade to the new usage. Unfortunately, due to an upgrade of the underlying layer, we have left behind a few incompatible APIs that will output error messages while running. If you have decided to make the upgrade, then please read the following carefully.

  • For the Artist and Game Designer, all resources in your project (e.g. scenes, animations, prefab) do not need to be modified or upgraded.
  • For Programmers, all APIs in the loader module that were used in the original code need to be changed to APIs from assetManager. The related content will be described in detail in this document.

Note: as v2.4 supports Asset Bundle, the subpackage feature in the project also needs to be upgraded, please refer to the Subpackage Upgrade Guide documentation for details.

Situations that require upgrading manually

  • You use APIs that start with loader in your own code, such as loader.loaderRes, loader.loadResDir, loader.release, etc.
  • You use APIs that start with AssetLibrary in your own code, such as AssetLibrary.loadAsset.
  • You use an API that starts with url in your own code, such as url.raw.
  • You use types such as Pipeline, LoadingItems in your own code.
  • You have used the macro.DOWNLOAD_MAX_CONCURRENT property in your own code.

Upgrade steps

  • Back up your old projects
  • Use Cocos Creator v2.4 in the Dashboard to open the project that needs to be upgraded, Creator will reimport the affected resources. The first import will take a little longer, and the main editor window will open after the import is complete. And more error or warning may appear on the Console panel, don’t worry, open the code editor to update your code according to the error or warning message.

Replace the loader related API with the assetManager related API.

As of v2.4, loader is no longer recommended and will be completely removed in subsequent releases, please replace it with the new resource management module assetManager.

The relevant interface replacement about loading

If you use loader.loadRes, loader.loadResArray, loader.loadResDir in your own code, use the corresponding API in assetManager for the replacement. You can refer to the following replacements.

  • loader.loadRes

    The parameters of resources.load are exactly equal to loader.loadRes. Replace with the following:

    1. // before
    2. loader.loadRes(...);
    3. // after
    4. resources.load(...);
  • loader.loadResArray

    For reducing learning costs, loadResArray has been merged with load and the first parameter of resources.load can support multiple paths, use resources.load to replace.

    1. // before
    2. loader.loadResArray(...);
    3. // after
    4. resources.load(...);
  • loader.loadResDir

    The parameters of resources.loadDir are equal to those of loader.loadResDir.

    1. // before
    2. loader.loadResDir(...);
    3. // after
    4. resources.loadDir(...);

    Note: to simplify the interface, the load completion callback for resources.loadDir will no longer provide a list of paths. Please avoid using the following:

    1. loader.loadResDir('images', Texture2D, (err, assets, paths) => console.log(paths));

    If you want to query the paths list, use the following form:

    1. const infos = resources.getDirWithPath('images', Texture2D);
    2. let paths = infos.map(function (info) {
    3. return info.path;
    4. });
  • loader.load

    If you use loader.load to load remote images or audios in your own code, there is a special API for this in the assetManager for ease of understanding, as follows:

    • Loading remote images

      1. // before
      2. loader.load('http://example.com/remote.jpg', (err, texture) => console.log(texture));
      3. // after
      4. assetManager.loadRemote('http://example.com/remote.jpg', (err, texture) => console.log(texture));
    • Loading remote audio

      1. // before
      2. loader.load('http://example.com/remote.mp3', (err, audioClip) => console.log(audioClip));
      3. // after
      4. assetManager.loadRemote('http://example.com/remote.mp3', (err, audioClip) => console.log(audioClip));
    • Loading remote text

      1. // before
      2. loader.load('http://example.com/equipment.txt', (err, text) => console.log(text));
      3. // after
      4. assetManager.loadRemote('http://example.com/equipment.txt', (err, textAsset) => console.log(textAsset.text));

Notes:

  1. If you use loader.downloader.loadSubpackage in your own code to load a subpackage, please refer to the Subpackage Upgrade Guide to upgrade it.
  2. To avoid unnecessary errors, loader.onProgress has no equivalent implementation in assetManager. You can implement your own global callback mechanism, but it is recommended that you pass callbacks to each load function to avoid interfering with each other during concurrent loading.

The relevant interface replacement about releasing

If you use loader.release, loader.releaseAsset, loader.releaseRes, loader.releaseResDir in your own code, please use the corresponding API in assetManager for replacement. You can refer to the following replacements.

  • loader.release

    loader.release can be replaced with assetManager.releaseAsset.

    Note: in order to avoid user attention to some obscure properties of the resource, assetManager.releaseAsset no longer accepts arrays, resource UUIDs, resource URLs for release, only the resource itself can be accepted for release.

    1. // before
    2. loader.release(texture);
    3. // after
    4. assetManager.releaseAsset(texture);
    5. // before
    6. loader.release([texture1, texture2, texture3]);
    7. // after
    8. [texture1, texture2, texture3].forEach(t => assetManager.releaseAsset(t));
    9. // before
    10. const uuid = texture._uuid;
    11. loader.release(uuid);
    12. // after
    13. assetManager.releaseAsset(texture);
    14. // before
    15. const url = texture.url;
    16. loader.release(url);
    17. // after
    18. assetManager.releaseAsset(texture);

    Note: to increase ease of use, releasing resource dependencies in assetManager will no longer require manual access to resource dependencies, and an attempt will be made within assetManager.releaseAsset to automatically release the associated dependencies, for example:

    1. // before
    2. const assets = loader.getDependsRecursively(texture);
    3. loader.release(assets);
    4. // after
    5. assetManager.releaseAsset(texture);
  • loader.releaseAsset

    loader.releaseAsset can be replaced directly with assetManager.releaseAsset.

    1. // before
    2. loader.releaseAsset(texture);
    3. // after
    4. assetManager.releaseAsset(texture);
  • loader.releaseRes

    operator.releaseRes can be replaced directly with resources.release.

    1. // before
    2. loader.releaseRes('images/a', Texture2D);
    3. // after
    4. resources.release('images/a', Texture2D);
  • loader.releaseAll

    loader.releaseAll can be replaced directly with assetManager.releaseAll.

    1. // before
    2. loader.releaseAll();
    3. // after
    4. assetManager.releaseAll();

Notse:

  1. For security reasons, loader.releaseResDir does not have a corresponding implementation in assetManager, please use assetManager.releaseAsset or resources.release for individual resource releases.
  2. Since the assetManager.releaseAsset automatically releases dependent resources, you no longer need to explicitly call loader.getDependsRecursively. If you need to find the dependency of the resource, please refer to the relevant API in assetManager.dependUtil.
  3. For security reasons, assetManager only supports the Auto Release property set in the scene, and loader.setAutoRelease, loader.setAutoReleaseRecursively, loader.isAutoRelease APIs have been removed. It is recommended that you use the new auto-release mechanism based on reference counting. Please refer to the Release Of Resources documentation for details.

Extension-related interface replacements

  • Pipeline

    If you have methods in your code that use loader.insertPipe, loader.insertPipeAfter, loader.appendPipe, loader.addDownloadHandlers, loader.addLoadHandlers series APIs to extend the loading process of loader, or directly use loader.assetLoader, loader.md5Pipe, loader.downloader, loader.loader, loader.subPackPipe, here are the detailed alternatives.

    Because assetManager is a more general module and no longer inherits from Pipeline, assetManager no longer implements handler.insertPipe, handler.insertPipeAfter, handler.appendPipe. Please replace with the following code:

    1. // before
    2. const pipe1 = {
    3. id: 'pipe1',
    4. handle: (item, done) => {
    5. let result = doSomething(item.uuid);
    6. done(null, result);
    7. }
    8. };
    9. const pipe2 = {
    10. id: 'pipe2',
    11. handle: (item, done) => {
    12. let result = doSomething(item.content);
    13. done(null, result);
    14. }
    15. };
    16. loader.insertPipe(pipe1, 1);
    17. loader.appendPipe(pipe2);
    18. // after
    19. function pipe1 (task, done) {
    20. let output = [];
    21. for (let i = 0; i < task.input.length; i++) {
    22. let item = task.input[i];
    23. item.content = doSomething(item.uuid);
    24. output.push(item);
    25. }
    26. task.output = output;
    27. done(null);
    28. }
    29. function pipe2 (task, done) {
    30. let output = [];
    31. for (let i = 0; i < task.input.length; i++) {
    32. let item = task.input[i];
    33. item.content = doSomething(item.content);
    34. output.push(item);
    35. }
    36. task.output = output;
    37. done(null);
    38. }
    39. assetManager.pipeline.insert(pipe1, 1);
    40. assetManager.pipeline.append(pipe2);

    Notes:

    1. assetManager no longer inherits by Pipeline, but by multiple Pipeline instances owned under assetManager. Please refer to the Pipeline and Task documentation for details.
    2. For ease of use, the definition of Pipe no longer requires the definition of an object with a handle method and an id, just a single method. See Pipeline and Task documentation for details.
    3. In order to simplify the logic and improve performance, what is processed in Pipe is no longer a item but a task object, see Pipeline and Task documentation for details.
    4. In order to reduce learning costs, APIs in the form of insertPipeAfter are no longer supported in Pipeline, so please use insert to insert the specified location.
  • addDownloadHandlers, addLoadHandlers

    For modularity reasons, addDownloadHandlers and addLoadHandlers are not implemented in assetManager, please refer to the following for replacement:

    1. // before
    2. const customHandler = (item, cb) => {
    3. let result = doSomething(item.url);
    4. cb(null, result);
    5. };
    6. loader.addDownloadHandlers({png: customHandler});
    7. // after
    8. const customHandler = (url, options, cb) => {
    9. let result = doSomething(url);
    10. cb(null, result);
    11. };
    12. assetManager.downloader.register('.png', customHandler);

    Or:

    1. // before
    2. const customHandler = (item, cb) => {
    3. let result = doSomething(item.content);
    4. cb(null, result);
    5. };
    6. loader.addLoadHandlers({png: customHandler});
    7. // after
    8. const customHandler = (file, options, cb) => {
    9. let result = doSomething(file);
    10. cb(null, result);
    11. };
    12. assetManager.parser.register('.png', customHandler);

    Notes:

    1. Since both the download module and the parsing module rely on extensions to match the corresponding processing method. So when calling register, the incoming first parameter needs to start with ..
    2. For the sake of modularity, the custom processing method will no longer pass in an item object, but will pass in its associated information directly. The custom processing method of downloader passes in the URL to be downloaded, and parser passes in the file to be parsed. For more information about downloader and parser, please refer to the Download and Parse documentation.
    3. The new expansion mechanism provides an additional options parameter that can greatly increase flexibility. However, if you don’t need to configure the engine’s built-in or custom parameters, you can ignore it. Please refer to the Optional parameter documentation for details.
  • downloader, loader, md5Pipe, subPackPipe

    loader.downloader can be replaced by assetManager.downloader, and loader.loader can be replaced by assetManager.parser. For details, see Download and Parse documentation or the corresponding API documentation assetManager.downloader and assetManager.parser.

    Note: for performance, modularity and readability reasons, loader.assetLoader, loader.md5Pipe, loader.subPackPipe have been merged into assetManager.transformPipeline and you should avoid using any of the methods and properties in these three modules. Details about assetManager.transformPipeline can be found in Pipeline and Tasks documentation.

Other changes

The url and AssetLibrary have been removed, so avoid using any methods and properties of url and AssetLibrary.

Pipeline can be replaced by AssetManager.Pipeline:

  1. // before
  2. const pipe1 = {
  3. id: 'pipe1',
  4. handle: function (item, cb) {
  5. let result = doSomething(item);
  6. cb(null, result);
  7. }
  8. }
  9. const pipeline = new Pipeline([pipe1]);
  10. // after
  11. function pipe1 (task, cb) {
  12. task.output = doSomething(task.input);
  13. cb(null);
  14. }
  15. const pipeline = new AssetManager.Pipeline('test', [pipe1]);

Note: LoadingItem is no longer supported in assetManager, please avoid using this type.

To support more loading strategies, macro.DOWNLOAD_MAX_CONCURRENT has been removed from macro and you can replace it with the following:

  1. // before
  2. macro.DOWNLOAD_MAX_CONCURRENT = 10;
  3. // after
  4. assetManager.downloader.maxConcurrency = 10;

Or:

  1. // before
  2. macro.DOWNLOAD_MAX_CONCURRENT = 10;
  3. // after (set a preset value)
  4. assetManager.presets['default'].maxConcurrency = 10;

Please refer to the Download and Parse documentation for details.