Game reloads or crashes on iPhone when loading assets from multiple Remote Bundles (Cocos Creator v3.8.1)
Hello everyone,
I’m developing a game using Cocos Creator v3.8.1, targeting both native and HTML5 platforms.
The game is split into 29 remote bundles, each representing a mini game. The sizes of the bundles range from 128.6 KB to 20 MB.
During gameplay:
- I load all assets from a bundle when a mini game is opened, and after that, load assets from the next bundle to switch to a different mini game.
- This process continues with load – switch – load.
The issue:
- On mobile browsers (Safari/Chrome) and native app (iPhone 8 Plus), the game either reloads or crashes after loading assets from about 10 bundles. However, the exact number of bundles is uncertain, and the chances of the crash/reload occurring increases with the more assets loaded.
- Test device used: iPhone 8 Plus (older iOS).
- Tests on Android (Note 9) have no issues.
The code responsible for loading assets:
loadAny(...args: any[]): Promise<Asset[]> {
return new Promise(async (resolve, reject) => {
try {
let [assets, options, onProgress, onComplete] = args;
options = options ?? {}; // If options is not provided, initialize as an empty object
const loadPromises = assets.map((assetsInfo) => {
return new Promise<AssetManager.Bundle>((res, rej) => {
const { bundle } = assetsInfo;
ResourceMap.log(`[ResourceMap] loadAny assetsInfo`, assetsInfo);
if (bundle && bundle.length > 0 && cc[`settings`].querySettings("assets", "projectBundles").includes(bundle)) {
this.loadBundle(bundle).then(() => {
res(assetsInfo);
}).catch((err) => {
ResourceMap.error(`[ResourceMap] loadAny err`, err);
res(null);
});
} else {
res(assetsInfo);
}
});
});
// Wait for all promises to complete
let results = await Promise.all(loadPromises);
// Filter out any null results
assets = results.filter(result => result !== null) as AssetManager.Bundle[];
// After preloading completes, move to the loading phase
await this.loadAssetsInParallel(assets, options, (loaded, total) => {
if (onProgress) {
onProgress(loaded, total); // Forward progress during preload phase
}
}, (err, results) => {
if (err) {
if (onComplete) {
onComplete(err); // Complete callback after loading
}
resolve(results); // Resolve with the data after both preloading and loading
} else {
if (onComplete) {
onComplete(null, results); // Complete callback after loading
}
resolve(results); // Resolve with the data after both preloading and loading
}
});
} catch (error) {
reject(error); // Reject the promise in case of any error
}
});
}
loadAssetsInParallel(assets, options, onProgress, onComplete) {
let total = assets.length;
let loaded = 0;
const assetLoadingPromises = assets.map((asset) => {
return new Promise((resolve, reject) => {
if (this.uuidMap.has(asset.uuid)) {
loaded++;
if (onProgress) {
onProgress(loaded, total); // Forward progress during preload phase
}
resolve({ asset: this.uuidMap.get(asset.uuid), info: asset }); // Resolve the promise once the asset is loaded
} else {
assetManager.loadAny(asset, options, (loaded, total, item) => {
ResourceMap.log(`[ResourceMap] loadAny uuid: ${item.uuid}, path: ${item.url}, bundle: ${item['config'].name}, type: ${js.getClassName(item?.info?.['ctor'])}`);
}, (err, data) => {
loaded++;
if (onProgress) {
onProgress(loaded, total); // Forward progress during preload phase
}
if (err) {
ResourceMap.error(`[ResourceMap] loadAny err`, err);
resolve(null); // Resolve with null in case of an error
return;
}
resolve({ asset: data, info: asset }); // Resolve the promise once the asset is loaded
});
}
});
});
try {
let data = await Promise.all(assetLoadingPromises);
data = data.filter(result => result !== null);
let results = [];
data.forEach((v) => {
let a = v.asset;
if (Array.isArray(a)) {
a.forEach((_v) => {
results.push(_v);
this.updateUUIDMap(_v);
});
} else {
results.push(a);
this.updateUUIDMap(a);
}
});
if (onComplete) {
onComplete(null, results); // Complete callback after loading
}
return results;
} catch (err) {
ResourceMap.error(`[ResourceMap] loadAny An error occurred during parallel asset loading:`, err);
if (onComplete) {
onComplete(err, null);
}
}
}
Questions:
-
Has anyone experienced similar issues when loading assets from many remote bundles on iOS?
-
Could this be due to iOS memory limitations (low RAM or webview/browser constraints)?
-
Are there any optimizations for loading/unloading assets to avoid reloads/crashes?
-
How should I reconsider dividing bundles for a project with many mini games?
Any tips, thoughts, or just pointing me in the right direction would mean a lot. Thanks in advance!