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 [[Engine.canvasWidth|canvasWidth]] and [[Engine.canvasHeight|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 [[EngineOptions.displayMode|displayMode]] option when creating a game to customize the viewport.

Fixed Display Mode

[[DisplayMode.Fixed]] is the default, use a specified resolution for the game. Like 800x600 pixels for example.

Fill Display Mode

[[DisplayMode.Fill]] 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.

Fit Display Mode

[[DisplayMode.Fit]] fit to screen using as much space as possible while maintaining aspect ratio and resolution.

Fit Display Mode

[[DisplayMode.Fit]] to screen using as much space as possible while maintaining aspect ratio and resolution.

This is not the same as [[Screen.goFullScreen]]

You may want to center your game here is an example

<!-- html -->
<body>
  <main>
    <canvas id="game"></canvas>
  </main>
</body>
// css
main {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  width: 100%;
}

Fill Display Mode

[[DisplayMode.Fill]] will fill the entire screen's css width/height for the game resolution dynamically. This means the resolution of the game will change dynamically as the window is resized. This is not the same as [[Screen.goFullScreen]]

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.

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).

This requires an explicit user gesture due to browser security, so wiring it into a native HTML button click is the easiest way to do this.

await game.screen.goFullScreen()

await game.screen.exitFullScreen()