1. Virtual Screen Instead of window.innerWidth Everywhere
The main screen config is in game/src/config/game.config.ts:
screen: {
land: { width: 960, height: 540 },
port: { width: 540, height: 960 },
dpr: 'auto',
maxDpr: 2,
render: {
scaleFactor: GameRenderScalePolicy.resolveScaleFactor(),
},
},
I define two base virtual coordinate systems:
- landscape:
960 x 540 - portrait:
540 x 960
It does not mean the game is always exactly this size. The engine reads the real viewport, detects orientation, and computes a virtual canvas that fills the available area.
Simplified code from engine/screen/Scaler.ts:
const isLand = viewport.width >= viewport.height;
const orientation = isLand ? 'land' : 'port';
const virt = isLand ? this.config.land : this.config.port;
const useWidth = fit === 'width' || (fit === 'auto' && isLand);
const useHeight = fit === 'height' || (fit === 'auto' && !isLand);
const canvasWidth = useWidth ? virt.width : Math.round(virt.height * (vw / vh));
const canvasHeight = useHeight ? virt.height : Math.round(virt.width * (vh / vw));
The idea is:
- in landscape, width is usually the stable axis;
- in portrait, height is usually the stable axis;
- the other axis is derived from the actual aspect ratio;
- gameplay code works in virtual coordinates;
- the canvas visually fills the viewport.
This keeps game layout predictable and avoids spreading browser dimensions across the codebase.