Good practice to load and access an asset (e.g. sound) from code?

Hello,

In Phaser (the 2D engine), there is a clear separation between the time at which you load your assets and the time at which you use your asset in the game.
Basically, you first load your assets in an asset manager, giving names to your assets, and then you access your assets during the game using the names you gave them:

var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'phaser-example', { preload: preload, create: create });

function preload() {
    game.load.image('einstein', 'assets/pics/ra_einstein.png');
}

function create() {
    var s = game.add.sprite(80, 0, 'einstein');
    s.rotation = 0.14;
}

Because I found this approach to be clean and simple. I am looking at a way to do it in BabylonJS.

It seems to me that I can load all of my assets easily with the AssetManager (Load Files with Assets Manager - Babylon.js Documentation). This object has a callback onFinish so that you can start the game (or game level) after all the assets are loaded.

My main problem is that, once I loaded the assets this way, how do I do to access them?

I thought that an easy way do to it was to use the scene as an asset “accessor” and access the assets this way:

let sound = scene.getSoundByName('violon18.vaw');
mySound.play();

However, I realised that the following functions do not exist:

let text = scene.getTextFileByName("text_name");
let binary = scene.getBinaryFileByName("binary_name");
let image = scene.getImageByName("image_name");
let texture = scene.getTextureByName("image_name");

Does that sound crazy to access the assets as if they were in a “library”? I think that would be very helpful on my side :smiley:

PS: In fact, I thought the asset container would be exactly that kind of library (Use an AssetContainer - Babylon.js Documentation) but no, it isn’t.

1 Like

Here’s a PG example posted by David Rousset which uses the asset manager to load audio and the play back on command.

https://www.babylonjs-playground.com/#PCY1J#8

Galen

1 Like

Perhaps this PG might be adaptable to your needs
Babylon.js Playground it is from http://www.html5gamedevs.com/topic/32480-loading-asynchronous-objects-into-array-for-later-use/?do=findComment&comment=187590

Yes I know these PG in fact :smiley: !

The one that @JohnK posted is the most relevant, but still, it is a shame that we need to create that “library” ourself while the scene already allows to store and access by names some of the assets (sounds, meshes, materials, etc).

They are not in the scene because they are not scene objects (we can definitely add it for textures if you think it is valuable)

Would be nice to be able to access by name everything that we loaded with the AssetManager after the loading is finished. Either from the scene itself or from the asset manager.

Like:

let mesh = assetManager.add("mesh1", scene)

Or:

let levelProgression = assetManager.getTextFile("levelProgression.json")

Or:

var image = assetManager.getImage("button1");
var button = BABYLON.GUI.Button.CreateImageOnlyButton("button", image);

Something like that, so that we can separate the asset loading or the asset instancing very clearly.

Here is a somewhat messy SoundAtlas and SoundPool.

The SoundAtlas does what is desired – provides access to the sounds after they’re loaded via an api that looks like this: BABYLON.Sound.FromAtlas('win sound', '/sounds/win.mp3', scene).

The SoundPool uses the Atlas but with pooling (I feel like this might be worth making into a BJS feature someday, but mine needs serious polish).

Its api looks like const mySound = SoundPool.get('/sounds/win.mp3') That will get a sound that automatically returns itself to the pool when it finishes playing. Another part of the api getManual will get a sound that needs returned manually.

To use the atlas, invoke setScene passing the scene, setAssetsManager passing an assets manager (so that one AM can be used to load all of the sounds and meshes etc, this just appends tasks to it), and then add as many sound file paths as desired.

The code is probably too much of a mess to use it as is… it’s just copypasta from a game I’m working on. Hopefully some of it, or the general idea, can be used.

1 Like

Like the idea! Do you mind creating an issue for it on the repo?

sounds to me that the AssetManager is an asset loader and what @timetocode described is an asset manager for the sounds :slight_smile:

Would be nice to have such a manager dealing with all the assets that can be loaded by the AssetManager. As the name AssetManager is already taken, that object could be an AssetLibrary? The name AssetAtlas could work too I guess; it’s just that Atlas sound like TextureAtlas which is an image file + a json file explaining where to find the texture in the image file.

I also have a similar thing for meshes which ends up looking like

const createPowerUp = (x, y, z) => {
    const powerUp = MeshAtlas.get('/1.0.0/powerUp.obj').createInstance()
    //... position it
}

Which I just mention b/c by the time there’s some sort of manager object spawning in objects (as opposed to loading a fully composed scene) perhaps instances or clones are desirable.

I started to program an AssetManager, and midway, I’ve found this code from the old forum:

let assetsManager = new BABYLON.AssetsManager(this._scene);
let assetsToLoad = [
    { name: "bgnd_color",      filename: "bgnd_color.png" },
    { name: "color_picker",    filename: "color_picker.png" },
    { name: "brush_size_icon", filename: "brush_size_icon.png" },
    { name: "increase_brush",  filename: "increase_brush.png" },
    { name: "decrease_brush",  filename: "decrease_brush.png" },
];
let assets = [];
assetsToLoad.forEach((obj) => {
    let assetTask;
    let fileExtension = obj.filename.split('.').pop().toLowerCase();
    switch(fileExtension) {
        case "png":
        case "jpg":
        case "jpeg":
        case "gif":
            assetTask = assetsManager.addTextureTask(obj.name, './images/' + obj.filename);
            break;
        case "dds":
            assetTask = assetsManager.addCubeTextureTask(obj.name, './images/' + obj.filename);
            break;
        case "hdr":
            assetTask = assetsManager.addHDRCubeTextureTask(obj.name, './images/' + obj.filename, 512);
            break;
        case "mp3":
        case "wav":
            assetTask = assetsManager.addBinaryFileTask(obj.name, './sounds/' + obj.filename);
            break;
        case "babylon":
        case "gltf":
        case "obj":
            assetTask = assetsManager.addMeshTask(obj.name, "", "", './models/' + obj.filename)
            break;
        case "json":
        case "txt":
            assetTask = assetsManager.addTextFileTask(obj.name, './data/' + obj.filename);
            break;
        default:
            console.log('Error loading asset "' + obj.name + '". Unrecognized file extension "' + fileExtension + '"');
            break;
    }
    assetTask.onSuccess = (task) => {
        switch(task.constructor) {
            case BABYLON.TextureAssetTask:
            case BABYLON.CubeTextureAssetTask:
            case BABYLON.HDRCubeTextureAssetTask:
                assets[task.name] = task.texture;
                break;
            case BABYLON.BinaryFileAssetTask:
                assets[task.name] = task.data;
                break;
            case BABYLON.MeshAssetTask:
                assets[task.name] = task.loadedMeshes;
                break;
            case BABYLON.TextFileAssetTask:
                assets[task.name] = task.text;
                break;
            default:
                console.log('Error loading asset "' + task.name + '". Unrecognized AssetManager task type.');
                break;
        }
    };
    assetTask.onError = (task, message, exception) => {
        console.log(message, exception);
    };
});
assetsManager.onProgress = (remainingCount, totalCount, lastFinishedTask) => {
    this._engine.loadingUIText = 'Loaded ' + remainingCount + ' of ' + totalCount + ' assets.';
};
assetsManager.onFinish = () => {
    // ACTIONS/EVENTS HERE
};

That I believe was posted by @inteja just here: http://www.html5gamedevs.com/topic/38587-is-there-a-way-to-use-the-babylonassetsmanager-to-load-textures/?do=findComment&comment=220272

I m going to reuse some of that code and put it into an object. Ideally I would also like to extend it to load the images for BABYLON.GUI :confused: but not how I am going to do.

2 Likes