Screen & Viewport

Excalibur has a screen abstraction for dealing with Screen.viewport size, and Screen.resolution. It also handles HiDPI scaling, the browser fullscreen API, and translation of screen coordinates into game world coordinates.

The Screen.viewport represents the size of the window into the game work in CSS pixels on the screen.

The Screen.resolution represents the number of logical pixels stretched across that viewport.

The screen abstraction can be accessed most easily off of a constructed engine.

const game = new ex.Engine({
  // sets the viewport as it did before
  // width and height are provided for backwards compatibilty
  width: 800,
  height: 600,
  // or optionally use the viewport option
  viewport: { width: 800, height: 600 },

  // sets the resolution
  resolution: ex.Resolution.GameBoy,
})

const screen = game.screen

Viewport and Resolution

The best way to change the viewport and resolution is in the Engine constructor arguments. If the viewport or resolution is changed after constructor time, Screen.applyResolutionAndViewport must be called to have the changes take effect.

// get or set the viewport
const viewport = game.screen.viewport
game.screen.viewport = { width: 400, height: 300 }

// get or set the resolution
const resolution = game.screen.resolution
game.screen.resolution = { width: 100, height: 100 }

// Apply changes to viewport and resolution to the canvas and graphics context
game.screen.applyResolutionAndViewport()

Sometimes you might want to switch between 2 different resolutions and viewports, perhaps for different scenes, or for some in game animation. This can be done with Screen.pushResolutionAndViewport and Screen.popResolutionAndViewport.

// Save the current resolution and viewport
game.screen.pushResolutionAndViewport()
// Change and apply
game.screen.resolution = ex.Resolution.NES
game.screen.applyResolutionAndViewport()

// Show some animation or do something at NES resolution

// Restore the old resolution and viewport, then apply
game.screen.popResolutionAndViewport()
game.screen.applyResolutionAndViewport()

Coordinate systems

In Excalibur, due to HTML canvas native and scaled resolution, there are essentially two kinds of coordinates: a screen coordinate and a world coordinate.

Screen coordinates

A screen coordinate is a point on a user's physical screen. (0, 0) in screen coordinates is the top-left of the canvas. Excalibur translates mouse event positions into screen coordinates for you. Screen coordinates ignore the game camera, think of it like where the user is physically pointing on top of your game.

World coordinates

A world coordinate is a point in the game world relative to a scene's camera. The world space is the default place where actors operate. (0, 0) in the game world may be displayed at the center of your game because that may be the camera's focal point.

Converting between coordinates

Usually, your game logic should only deal in terms of world coordinates because this is the coordinate system you're positioning actors in or drawing in. Sometimes though, you need to convert between systems when interfacing with input.

Excalibur provides two methods to convert between systems, Engine.screenToWorldCoordinates and Engine.worldToScreenCoordinates. Use these methods to convert between coordinate systems as they take into account all the information Excalibur collects to appropriately do the math for you including scaling, device pixel ratio, and more.

Game resolution

An HTML canvas element has a "native" resolution which is specified using the width and height HTML attributes. In Excalibur, these can be set using canvasWidth and canvasHeight respectively.

Native Resolution

If you don't explicitly set canvasWidth or canvasHeight Excalibur will manage the values for you, meaning that the "native" resolution will be the physical screen width/height so there is no "scaling" happening. This means your game logic must be responsive if the resolution of the game changes and you cannot depend on a "fixed" width/height coordinate system. Games which can be played on mobile devices and desktop will work, since your game logic can detect what screen size the game is being played on and respond accordingly.

Scaled Resolution

Alternatively, if a native resolution is set and then CSS is used to change the styled width and height, this makes your game scale to whatever the CSS dimensions are. You would want to explicitly set a native resolution if your game depends on having a specific width/height of the "draw area". For example, you may want to design a game that depends on a fixed size of 1280x720 but you want to allow the user to view it at higher resolutions on their browser, so you may scale the canvas to a 16:9 ratio. Your game logic and positioning logic will still work since the game world is still 720px wide even though it may be displaying at 1080px wide on a high-res screen.

HiDPI Displays

HiDPI displays scale device pixels. For example, on a normal monitor, a 1280x720 game canvas will return 1280 and 720 for the width and height respectively. However, on a 2X HiDPI display what's actually drawn is multiplied by a device pixel ratio of 2 so, 2560 and 1440. Any time you need to get this "actual" width and height of what's being drawn to the canvas, the engine exposes Engine.drawWidth and Engine.drawHeight that takes into account the device pixel ratio. In this HiDPI scenario, Engine.canvasWidth and Engine.canvasHeight would still return the native 1280x720 resolution.

Managing display modes

Excalibur supports multiple display modes for a game. Pass in a displayMode option when creating a game to customize the viewport.

Fullscreen Display Mode

DisplayMode.FullScreen will style the canvas with CSS to take up the entire window viewport. If canvasWidth and canvasHeight are not set explicitly, the native resolution will be the same as the viewport size.

Container Display Mode

If you use DisplayMode.Container, the canvas will automatically resize to fit inside of it's parent DOM element. This allows you maximum control over the game viewport, e.g. in case you want to provide HTML UI on top or as part of your game.

Position Display Mode

You can use DisplayMode.Position to specify where the game window will be displayed on screen. If this DisplayMode is selected, then a position option must be provided to the Engine constructor. The position option can be a string or an AbsolutePosition.

The first word in a string must be the desired vertical alignment of the window. The second (optional) word is the desired horizontal alignment.

  • Valid string examples: "top left", "top", "bottom", "middle", "middle center", "bottom right"

For an AbsolutePosition, the value for each property is interpreted similar to CSS, where a number value is in pixels and a string is whatever valid CSS unit you want to pass.

  • Valid AbsolutePosition examples: {top: 5, right: "10%"}, {bottom: "49em", left: "10px"}, {left: 10, bottom: 40}

The <canvas> element will be positioned using CSS with the values you pass in.

Fullscreen API

The screen abstraction now supports the browser fullscreen api. This will cause the game to be displayed fullscreen until the user exits (usually with the escape key or by gesturing to the exit button at the top of the browser window).

await game.screen.goFullScreen()

await game.screen.exitFullScreen()