GameMaker Studio renders content at multiple resolutions at different stages in the rendering process. Sometimes they'll all match, but not always. To understand display scaling, it's important to understand the various resolutions at which your project operates.
It's helpful to think of each resolution as a layer, all stacked together to form the final image:
- At the lowest level is the room, where all your objects and sprites are drawn. The room is projected onto a view (or view camera) which represents the currently visible area (or, in keeping with the analogy, a small 'slice' of the bigger room).
- Next, the view camera is projected onto a port (or viewport) which determines where the camera appears on the screen. Typically, the viewport will fill the entire window or display, but it is not required to. In fact, it's not required to match the view camera OR the screen in any way, and can be as big or small or wide or tall as it pleases, potentially resulting in stretching or squishing the view camera's pixels beyond their original resolution.
- Next, the viewport is projected onto the application surface, which is the final step in the primary rendering process. All viewports are calculated and displayed on a single flat texture which always fills the output window (according to the scaling mode set in the project's Game Settings). However, like the viewport before it, the application surface has its own independent resolution and is not required to match the viewport OR the window. It is simply a way to collect all previous layers into one.
- Next, there is the GUI layer. GameMaker provides this separate layer for drawing UI and HUD elements that don't belong in the room itself. And--you guessed it--it also has its own independent resolution. Like the application surface, it is nearly always forced to always fill the output window (according to the scaling mode set in the project's Game Settings), potentially resulting in stretching or squishing. While it technically can be moved and scaled (similar to a view) this is generally not advisable. By default, the GUI layer will match resolution with either the application surface or the game window, whichever GameMaker calculates is more preferable.
- Finally, we have the output window (or display--but because an application is still technically in a window even when running in fullscreen, 'window' should always be assumed). The window will frame content in one of two ways, determined by the scaling mode set in the project's Game Settings: Aspect Ratio, or Full Scale. Aspect Ratio scaling will resolve any differences between the window itself and the application surface/GUI layer by adding black bars on either side of the image, while Full Scale will simply stretch the application surface/GUI layer to fill the window.
So, then, a GameMaker Studio application does not run at one resolution, but potentially five different resolutions all at once!
Changing resolutions causes any surfaces (including the application surface) to break and therefore require re-rendering. For custom surfaces containing dynamically-generated content, this can result in lost visual information. To avoid this problem while scaling, see
draw_get_surface from GML+!
It's important to note that Xtend does not replace this rendering process, but rather manages it intelligently. In addition, Xtend cannot override the built-in scaling method set in the project's Game Settings. While both available methods will produce the same results a majority of the time with a good display scaler, there are some cases where Aspect Ratio is preferred, so this setting should always be enabled when using Xtend.
This shouldn't be confused with Xtend's own
aspect scaling mode, as all Xtend scaling modes are subject to GameMaker's built-in scaling.
While one might assume that the goal of display scaling is to match application resolution with window resolution, this is no longer true in modern applications. In fact, with Xtend, only the application surface and GUI layer will match window resolution 1:1. This ensures that UI and HUD elements can appear at full resolution regardless of internal resolution, while internal contents are not skewed by mismatches between the application surface and window.
For everything else, the goal is twofold:
- Preserve square pixels
- Match view shape proportionately to eliminate black bars
Unfortunately, it is not always possible to achieve the first goal perfectly, but it is possible to get close enough that any discrepencies are invisible on modern high-resolution displays.
For low-resolution art styles, see Xtend's
pixel scaling mode, which simulates integer scaling for true pixel perfection
Content that scales elegantly to different shapes of display is called Responsive Design. First popularized by websites that must work equally well on both desktop and mobile devices, the same principles now apply to a broad variety of applications. While no specific design changes are required to use Xtend, for best results, it's important to foster a few particular design habits in your programming:
First, the #1 rule of responsive design is to ditch the concept of pixels as a unit of measurement. Instead, all coordinates and dimensions must be thought of as percentages of the output viewport or other key visual elements (which are in turn scaled relative to the viewport).
This can be expressed in different ways, but a common approach is to calculate a pixel value by simply multiplying the primary viewport dimensions by a fraction. Xtend includes built-in macros for this, aptly named
view_height, where, for example,
view_width*0.5 (or 50%) would be center. GameMaker Studio itself also includes functions for getting the dimensions of other viewports (
camera_get_view_width(view_camera)), layers (
display_get_gui_width()), and elements (
sprite_get_width(my_sprite)). Calculating pixel values as percentages of other pixel values ensures elements retain the same visual placement at any resolution.
What makes these values especially powerful is that they apply to both position and scale. Designing relative to percentages will keep your layout consistent at different sizes, and also resize individual elements within that layout appropriately for the available space.
By checking for different resolution or aspect ratio thresholds, you can even trigger entirely different layouts suitable for different viewport shapes. For example,
if (view_aspect > 1) will return
true in landscape orientation and
false in portrait.
if (view_width < 1280) will determine whether the current resolution is sub-HD, at which point a larger font may be required for text elements.
Scale can also play a useful role as a multiplier of base resolution. Xtend includes built-in macros for
view_yscale which can be applied to all sorts of elements to ensure they occupy a suitable area of the display at any resolution. This includes both functions which explicitly define scale (e.g.
draw_sprite_ext) and those which define dimensions as a value of pixels (e.g.
draw_sprite_transformed). Either supply the scale value itself, or multiply the base dimensions by the scale value to produce flexible content.
To scale groups of elements as a whole, see
instance_link from GML+!
A rule of thumb: shift your design paradigm southeast by 50%. Computers in general like to draw things from the top left corner to the bottom right, but that doesn't mean your design should work the same way. In many cases, you'll want to base your content around the center of the viewport rather than a corner. Xtend also includes built-in macros for
view_ycenter to provide an easy origin point. Of course, what matters is that these values will always be center regardless of window shape. You can then add or subtract to position content from there.
This rule is not universal, but critical where it matters. Nothing will break faster when changing display scale than manually centered content designed for a fixed resolution.
You can also center viewports with Xtend! See
With these concepts in mind, it's also important to know where responsive design doesn't apply. Remember, all five layers in the rendering 'onion' exist to provide a window into your game world. That world is still ultimately comprised of pixels, and may or may not benefit from changing itself relative to the viewer.
While puzzle games and visual novels are integrated directly into the user interface, platformers and action games primarily exist in a separate space. In the end, only you, the designer, can decide which elements require responsive design and which don't to provide the best experience.
Of course, even the best design has its limits. To protect against the extremes, Xtend's config allows setting a minimum and maximum aspect ratio, beyond which the application will letterbox or pillarbox. The result still takes advantage of the available space as much as is reasonable, but without breaking design. After all, there's no shame in creating a good design that works in some scenarios over winding up with a design too broad to feel great in any scenario.
Target whatever range of design you can and let Xtend take care of the rest! To learn how, continue on to the rest of this reference guide.