Skip to content

Loading assets

samme edited this page Nov 4, 2024 · 23 revisions

Strategy

  • Load assets before using them
  • Load shared assets in a "boot" or "preload" scene and any non-shared assets in the scene they will be used in
  • Use a scene payload to load small assets before the scene preload() callback, if needed
  • For special cases, run the scene loader manually and access the newly loaded assets in event callbacks
  • You can remove assets if they are no longer needed to save some memory

About caching

Assets

There are many types, and you can read about all of them.

Assets need to be loaded before you can use them, but once loaded, they are available everywhere. It doesn't matter which loader or scene loaded them. Once loaded they are in the Texture Manager (this.textures) or the asset caches (this.cache).

Use unique keys (names) for assets.

Where to load assets

In a single-scene game, you'll load assets in that scene, naturally.

In a multi-scene game, it's convenient to load shared assets in a "boot" or "preloader" scene and then start the other scenes afterwards. Any non-shared assets can be loaded in the scene that uses them, and removed if necessary when that scene shuts down.

The loader

Each scene has a loader plugin, this.load, for loading assets.

Adding files to the loader

Each asset-loading method queues a resource by key and URL. You use the key to identify the asset later.

There is a (key, url) argument format:

this.load.image('treasure', 'treasure.png');

And an object config format:

this.load.image({ key: 'treasure', url: 'treasure.png' });

And an array format:

this.load.spritesheet([
  { key: 'mermaid', url: 'mermaid.png', frameConfig: { frameWidth: 16, frameHeight: 16 } },
  { key: 'merman', url: 'merman.png', frameConfig: { frameWidth: 16, frameHeight: 16 } }
]);

The loader will not add assets with duplicate keys (per asset type) at all:

this.load.image('sky', 'sky1.png');
// 'sky1.png' will be queued and (if loaded) stored as texture key 'sky'

// OOPS:
this.load.image('sky', 'sky2.png');
// 'sky2.png' will not be queued at all

Some key exceptions:

  • For some asset types (script(), sometimes pack()), you won't use the key again to retrieve anything, so it's not very important which key you choose.
  • For plugin(), sceneFile(), and scenePlugin(), key must be the global class name of the plugin or scene.

Relative URLs are resolved to the base URL of the document, unless you configure the loader's path or baseURL values.

Each asset-loading method creates a temporary File object for the loader which you can access through the load event handlers if you need. These last only for the duration of the load cycle.

You can write your own asset manifest pretty easily:

const files = {
  animation: [/* … */],
  audio: [/* … */],
  image: [/* … */],
  spritesheet: [/* … */],
};

this.load.animation(files.animation);
this.load.audio(files.audio);
this.load.image(files.images);
this.load.spritesheet(files.spritesheet};

A File Pack could do as well:

this.load.pack('pack1', {
  section1: {
    files: [
      { type: 'image', key: 'conch', url: 'conch.png' },
      { type: 'spritesheet', key: 'mermaid', url: 'mermaid.png', frameConfig: {/* … */} }
    ]
  }
});

A File Pack needs at least one named section (e.g., section1 above). If you're loading all the pack assets at once, it doesn't really matter what the section names are. The docs describe a method for downloading pack sections separately, but if you want to do this it seems more practical to download the entire pack with load.json() then select sections from the JSON data and pass those into load.pack().

You can add assets to the load queue while the loader is running:

this.load.json('level1', 'level1.json');
this.load.on('filecomplete-json-level1', (key, type, data) => {
  this.load.image(data.images);
  this.load.spritesheet(data.spritesheets);
});

load.multiatlas(), load.pack(), and load.spine() work this way.

Load events

preload()

Most of the time you will load assets in a scene preload() method.

When preload() returns, the loader starts automatically, then create() is called only after loading finishes. In the meantime the scene is in a LOADING state and still updates and renders any of its game objects, but doesn't call scene update(). You can show load progress this way.

Loading progress

It's best to create any game objects in the load START event and destroy them in the load COMPLETE event, so that restarting the loader doesn't cause any errors.

Scene payload

A scene payload is alternative to preload() that lets you load assets right when the scene starts. It's often used when you need to load a few small assets to use during preload(), and you don't want the trouble of starting an extra scene (and its loader) before that. A scene downloading a payload is not in a LOADING state and can't update or render anything, so it's best to keep payloads small.

The pack object structure is the same as the files portion of a pack file section.

const sceneConfig = {
  pack: {
    files: [
      { type: 'json', key: 'settings', url: 'settings.json' },
      { type: 'image', key: 'bar', url: 'bar.png' }
    ]
  },
  init: function () {
    // Pack has downloaded. 'settings' is in JSON cache.
    this.game.registry.merge(this.cache.json.get('settings'));
  },
  preload: function () {
    // 'bar' is in the Texture Manager.
    this.load.on('start', () => {
      const loadingBar = this.add.image(0, 0, 'bar');
    });
  }
};

Background loading

To load assets outside of preload(), you add files as usual, add listeners for the completion events, and then start the loader yourself.

this.load
  .image(['conch', 'treasure', 'trident'])
  .once('complete', () => {
    // All files complete
  })
  .start();

It's fine if two processes call the loader's start() separately. If it's already loading, it won't restart or clear the queue.

Other scenes should listen for "add" events from the game caches or Texture Manager:

this.cache.audio.on('add', (cache, key) => {
  if (key === 'music') {
    this.sound.play('music');
  }
});

this.textures.on('addtexture-map', (texture) => {
  // The key is also in `texture.key`.
  this.add.image(0, 0, 'map');
});

Removing assets

You can remove assets to save memory. Remove any game objects or scene objects (e.g., Sounds) using these assets first!

Remove textures from the Texture Manager:

this.textures.remove('conch');

and other assets from their respective caches:

this.cache.audio.remove('chime');
this.cache.json.remove('settings');

Removing assets to reuse their keys for different assets is usually a bad idea.

Clone this wiki locally