Answer
Below is a concise, step‑by‑step pattern that addresses the three concerns raised in the question.
1. Mark a failed asset as optional
In PixiJS 7.x the ResourceLoader respects an optional flag on each Resource. When the flag is set, load errors are ignored and the loader continues.
// Create an optional resource (e.g., a texture that may be missing)
const optionalTex = new PIXI.resources.Resource('assets/missing.png', { optional: true });
// Add it to the loader
const loader = new PIXI.Loader();
loader.add(optionalTex);
// Listen for errors only if you want to log them
loader.on('error', (loader, resource) => {
if (!resource.optional) {
console.error('Required asset failed:', resource.url);
}
});
loader.load(() => {
// All required assets are loaded; optional failures are silently skipped
startGame();
});
2. Detect WebGL2 mismatch and fall back to WebGL1
PixiJS does not automatically retry with WebGL1 when a WebGL2 context cannot be created. The recommended approach is to attempt a WebGL2 renderer first, inspect its context, and if it is not a WebGL2RenderingContext, destroy that attempt and recreate the Application with WebGL1 forced.
function createApp(preferWebGL2 = true) {
const settings = {};
if (preferWebGL2) {
settings.context = { webgl: 2 }; // ask for WebGL2
} else {
// Force WebGL1 via the global setting or renderer option
PIXI.settings.PREFER_ENV = PIXI.ENV.WEBGL1;
settings.context = { webgl: 1 };
}
return new PIXI.Application({ width: 800, height: 600, autoStart: false, ...settings });
}
let app = createApp(true);
// After the renderer is initialized, check the context type
app.renderer.once('init', () => {
const ctx = app.renderer.context;
const isWebGL2 = ctx instanceof WebGL2RenderingContext;
if (!isWebGL2) {
console.warn('WebGL2 not available, falling back to WebGL1');
app.destroy(true, { children: true, texture: true, baseTexture: true });
app = createApp(false); // WebGL1 forced
// Re‑add listeners, reload assets, etc.
setupAndStart(app);
} else {
setupAndStart(app);
}
});
function setupAndStart(application) {
// Load assets, then start ticker
loader.load(() => {
application.ticker.start();
// game init …
});
}
3. Cleanly dispose of the old Application instance
To avoid duplicate listeners, memory leaks, or leftover textures, destroy the existing Application before creating a new one. The destroy method accepts a boolean to remove the view from the DOM and an options object to clean up children, textures, and base textures.
// Safely tear down the current app
if (app) {
app.destroy(true, {
children: true, // remove display‑object hierarchy
texture: true, // destroy base textures
baseTexture: true // destroy parent textures
});
app = null;
}
// Now create a fresh instance (see createApp above)
app = createApp(preferWebGL2);
Verification steps (safe, scoped)
- Optional asset: run the snippet with a non‑existent URL; confirm no
error event is emitted for that resource and the loader completes.
- WebGL fallback: disable WebGL2 in browser dev‑tools (
// @ts-ignore
PIXI.settings.PREFER_ENV = PIXI.ENV.WEBGL1;) or use chrome://flags#disable-webgl2, then instantiate the app; verify app.renderer.context is a WebGLRenderingContext (not WebGL2).
- Application disposal: after calling
app.destroy, inspect app.ticker listeners (app.ticker._head should be null) and ensure no texture objects remain in PIXI.utils.TextureCache that were owned by the old app.
If you are using a custom subclass of ResourceLoader or need to preserve renderer state for reuse, let me know whether you require the renderer to survive the disposal step, as that changes the destroy options.