I want to be able to load potentially pretty large scenes additively, but without causing a hitch in the game by doing so.
I’ve been building off the example project for additive loading, but adding some code where I am trying to load all the assets first, and make sure they are loaded, before actually loading the scene. The code is pretty rough around the edges while I try to see if this can work.
The basic process I have is this:
- Trigger loading process with a scene name, which is also a tag. All relevant assets in the asset registry are tagged with the scene name
- Find all assets with the tag and load them
- Check on update whether all the assets are loaded
- When all assets are loaded, add the scene
Here is my example project: https://playcanvas.com/editor/scene/1164703
I have deliberately made the ‘Van’ scene very heavy indeed, with a few really big textures. Nothing is checked to be preloaded.
Here’s how to see the process:
- Launch the project
- Click ‘Load Playboy Anim’
- Click left arrow and then ‘Load Van Model’
There’s a long hitch happening after the assets are supposed to be loaded, while adding them to the scene.
But here’s the thing, if you go back and click ‘Load Playbot Anim’ again it’s set up to remove the Van scene. And then if you go back and click ‘Load Van Model’ there’s only a very minor hitch as it brings the scene back in again.
On that second time around, it seems that the resources really have been loaded into memory. So my question is, how can I (asynchronously) load all these assets into memory such that when they are called to be added into the scene the first time, it is as slick as it is the second time around?
All the code is in scene-manager.js, but here is the code called to start loading the assets:
SceneManager.prototype._loadSelectedScene = function () {
if (!this._loadingScene) {
this._loadingScene = true;
this._loadSceneButton.active = false;
this.sceneName = this.sceneFilenameStrings[this._selectedSceneIndex];
this.scene = this.app.scenes.find(this.sceneName);
this.sceneLoaded = false;
this.assets = this.app.assets.findByTag(this.sceneName);
this.loadingAssets = true;
console.log('loadAssets: ' + this.sceneName);
var assetLoaded = function() {
};
for (var i = 0; i < this.assets.length; i++) {
this.assets[i].ready(assetLoaded);
console.log("Loading: " + i / (this.assets.length-1) + " (" + this.assets[i].name + ")");
this.app.assets.load(this.assets[i]);
}
}
};
And here is the code running on update checking whether the assets have actually loaded, and finally loading the scene:
SceneManager.prototype.update = function (dt) {
var self = this;
if(this.loadingAssets){
var assetsLoaded = true;
for(var j = 0; j < this.assets.length; j++){
if(!this.assets[j].loaded){
assetsLoaded = false;
break;
}
}
if(assetsLoaded){
this.loadingAssets = false;
console.log("All assets loaded!");
var numChildren = this.sceneRootEntity.children.length;
for(var i = 0; i < numChildren; i++){
var child = this.sceneRootEntity.children[i];
if(child.tags.has("Van")){
child.destroy();
}
if(child.tags.has(this.sceneName)){
this.sceneLoaded = true;
}
}
if(this.sceneLoaded){
this._loadingScene = false;
this._loadSceneButton.active = true;
return;
}
this.app.scenes.loadSceneHierarchy(this.scene.url, function (err, loadedSceneRootEntity) {
if (err) {
console.error(err);
} else {
loadedSceneRootEntity.reparent(self.sceneRootEntity);
self._loadingScene = false;
self._loadSceneButton.active = true;
}
});
}
}
};
Is there a way to do this loading asynchronously?