Ok so here is my workaround. I don’t call that a solution since it would imply writing a proper MultiFile loader. Still busy trying to figure that one out. At least it unblocks me releasing a beta demo of my little game…
TL;DR;
Short version:
- don’t use loader.audio if Webkit is detected
- don’t allow update to process anything until further notice.
- on each audio asset: use a fetch → convert to buffer → decodeAudio as shown below.
- register a listener: this.sound.on(‘decoded’) to detect when resources are loaded. keep track of decoded count before allowing the update loop to process anything.
- don’t forget to unregister your ‘decoded’ event in the shutdown sequence of your scene.
Sorry @rich for desacrating your splendid code, but I’m not good enough (yet?) with Phaser to write a proper MultiFile implementation that properly works during the preload phase… I hope I can book a support session with you to help me implement a proper FileTypesManager.register("iOsCapacitorSafeAudio", ...)
Now the long version.
detecting webkit (capacitor or not, desktop or mobile, just does the trick):
window && typeof window.webkitConvertPointFromNodeToPage === "function"
ok,
This hack should help as a quick fix though.
Add these 5 members to the scene:
private doneNormalPreload = false;
private doneUglyPreload = false;
private countToDecode = 0;
private decodedCount = 0;
private canLoop = false;
preload
Preloading happens like usual: images, jsons, tilesets, etc. except for sounds, if Webkit is detected (can’t detect capacitor, so, this does the trick).
it must however also reset the following and register an event listener on the scene’s sound system:
this.doneNormalPreload = false;
this.doneUglyPreload = false;
this.decodedCount = 0;
this.canLoop = false;
// every time an audio file is decoded we increment the counter till we reach the desired amount
this.sound.on("decoded", (audiokey) => {
this.decodedCount++;
if (this.decodedCount === this.countToDecode) {
this.uglyLoadComplete();
}
});
// filter out the audio assets that are already loaded, otherwise the "decoded" event handler will run into trouble
const newAudioAssets = audioAssets.filter((asset) => !webAudiosoundManager.game.cache.audio.entries.entries[asset.key]);
// just in case the audio asset count to preload has dropped to 0
this.countToDecode = newAudioAssets.length;
if (this.countToDecode === 0) {
this.uglyLoadComplete();
}
Preloading what the audio files needs the fetch call as follows
newAudioAssets.map(({ key, url }) =>
fetch(url[0])
.then((response) => response.arrayBuffer())
.then((audioAsBuffer) => {
// console.log("decoding " + key);
return webAudiosoundManager.decodeAudio(key, audioAsBuffer);
})
);
create method
Move the contents of your create method in a new “createAfterSafeAudioPreload” method.
Create should only contain this:
this.doneNormalPreload = true;
if (this.doneUglyPreload) {
this.createAfterSafeAudioPreload();
}
Now both doneNormalPreload and doneUglyPreload must have been set to true in order to call that createAfterSafeAudioPreload
Add the ugly load complete method to the scene’s body
This one makes sure to only start the game when normal preloading is done AND this hackish preloading is done:
private uglyLoadComplete() {
this.doneUglyPreload = true;
if (this.doneNormalPreload) {
this.createAfterSafeAudioPreload();
}
}
** Prevent the update() method from doing anything until told so
update(t: number, dt: number) {
if (this.canLoop) {
super.update(t, dt);
this.animatedTiles.updateAnimatedTiles();
this.playerInput.update();
this.processIrq();
}
}
This is because the create() method is called by the normal/clean preloader of Phaser and that schedules the scene update. So I made this ugly boolean check waste cycles every tick to prevent issues…
** Don’t forget to unregister the listener **
In the shutdown method, don’t forget to unregister on the sound member the following:
this.sound.off("decoded");
NB make sure to do it on this.sound, not this.events !!!