Multiple resolutions and interface scaling
Window size, logical design, Canvas Items, Viewport and aspect policies.
Keep four quantities separate: the physical window size, the logical design area, the root viewport's rendered resolution and any internal 3D resolution scale. Changing one is not a universal way to resize UI, sharpen pixel art or improve performance.
Choose the root scaling mode
Set a design size under Project Settings → Display → Window → Size → Viewport Width / Height. It is a base area for content, not a request to change the monitor resolution. Window size overrides are a separate choice for the initial desktop window.
Under Display → Window → Stretch, choose the mode and aspect policy. The C# root is GetTree().Root, a Window.
| Stretch mode | C# ContentScaleMode | Result |
|---|---|---|
| Disabled | Window.ContentScaleModeEnum.Disabled | No design-size stretching; window/content scaling configuration still matters. |
| Canvas Items | CanvasItems | Scale the logical 2D content while rendering at the fitted window area. Useful for UI and smoothly rendered 2D. |
| Viewport | Viewport | Render at the base-derived viewport resolution, then scale the resulting image. Useful for a deliberately fixed low-resolution image. |
The associated properties are ContentScaleSize, ContentScaleMode, ContentScaleAspect and ContentScaleFactor. Project keys are display/window/size/viewport_width, viewport_height, display/window/stretch/mode, aspect and scale.
For a 64 × 48 design in a 128 × 96 window at scale factor 1 and Ignore aspect, the tested Viewport mode produces a 64 × 48 root texture; Canvas Items produces a 128 × 96 texture while keeping scaled 2D placement. These are illustrative test dimensions, not recommended shipping resolutions.
Decide what different aspect ratios should reveal
| Aspect policy | Behavior |
|---|---|
| Keep | Preserve the base aspect; add bars when necessary. |
| Expand | Preserve scale/aspect while exposing additional logical width or height. Content must cover that area. |
| Keep Width | Keep the base width; accommodate tall windows by exposing more height, with bars where required in the other direction. |
| Keep Height | Keep the base height; accommodate wide windows by exposing more width, with bars where required in the other direction. |
| Ignore | Fit without preserving the design aspect. Shapes can stretch nonuniformly. |
In the native test, a 64 × 48 design in a 192 × 96 window retains a 64 × 48 logical area with Keep, while Expand exposes 96 × 48. An expanded view may reveal unfilled backgrounds or objects outside the intended composition; this is a layout/content task, not a renderer failure.
Make UI adapt rather than scaling fixed positions
Use Containers for flow/layout and anchors/offsets for intentional edge attachment. Design for the minimum supported logical area, not only the largest development monitor. Test long translated labels, scrolling, font sizes, touch targets and safe areas on the target device. Aspect fitting alone does not resolve these constraints.
ContentScaleFactor changes the scale of the whole content area; it is not a font-size-only option. In the tested Canvas Items case, changing 1 to 2 halves the logical visible area from 64 × 48 to 32 × 24. A UI-size option can therefore require reflow. Changing the design size to “make buttons bigger” can also change how much game content is visible.
For a 3D game with sharp UI, lower the 3D rendering scale independently when appropriate; see resolution scaling. Lowering the entire root viewport resolution also lowers the resolution of UI rendered inside it. Screen resolution does not remove the need to test 3D camera framing at different aspects.
Keep pixel art intentional
Use a suitable low design resolution, Viewport mode where a fixed raster is desired, and nearest filtering for assets that must remain pixel-sharp. Fractional scaling can produce uneven pixel widths; integer stretch can preserve whole-number enlargement but can leave additional borders or lack room at small sizes. The project option is display/window/stretch/scale_mode; its integer mode does not guarantee that every window will fill without bars.
Camera movement and sprite placement can still introduce subpixel motion. Check animated output; a sharp static sprite does not prove stable scrolling. Do not apply pixel-art filtering rules to ordinary UI or photographic textures indiscriminately.
Distinguish root scaling from a SubViewport
A SubViewport has its own output Size and may have a separate 2D logical override. A stretching SubViewportContainer controls its child Size; StretchShrink reduces that child's resolution. Root ContentScaleSize is not a substitute for any of these. See viewports for the executed ViewportTools helper and an input-coordinate explanation.
When forwarding pointer events manually into a scaled texture, account for its actual displayed rectangle, aspect bars and logical-to-output mapping. Do not multiply a mouse position by a resolution ratio without considering those offsets.
Test the cases that expose mistakes
| Symptom | Check first |
|---|---|
| UI becomes blurry after a performance adjustment | Was the whole root viewport lowered instead of only 3D resolution? |
| Circles become ellipses | Ignore aspect or a separately stretched texture. |
| Empty edges on ultrawide | Expand reveals more world; extend backgrounds/layout or choose a different aspect policy. |
| Controls run off the screen | Minimum logical area, Container sizing, anchors and translated text. |
| Click positions are offset | Bars/crop/scale applied to display but not to manually forwarded input. |
| Editor preview and standalone differ | Embedded-game fitting and actual game-window size; compare the same content configuration. |
This guide's native evidence covers Windows Forward+/Vulkan root Viewport/CanvasItems pixels and dimensions, ContentScaleFactor and Keep/Expand logical areas. Integer-scale motion quality, OS DPI behavior, embedding, mobile safe areas and full exported-device coverage require their own checks; they are not claimed verified here.