Fullscreen Scaling in Phaser

This weekend I’ve been playing a bit with nailing down multi-device scaling in Phaser games. This was a bit more confusing than I expected, but I mostly figured out why and thought I’d share.

Update: I’ve posted this example and a state-based project template to GitHub.

My objectives were very simple:

  • Set logical view dimensions around which the game will be based;
  • Scale the game to fill the available window when loaded, maintaining aspect ratio and content;
  • Upon clicking have the game go fullscreen and scale to fill the screen, maintaining aspect ratio and content.

This minimal example demonstrates those (click to fullscreen, again to return):

 

In the blog view here the game has been placed inside a 300px iframe and scales down accordingly from an 800×600 logical view. After you click it scales up to fill the screen as much as it can without losing content. All of this is using Phaser 2.1.3, the latest release. Note that this is just in Chrome & Chromium. Desktop Firefox seems to have have wholly separate issues, and on Android Firefox fullscreening doesn’t seem to do anything.

This test is extremely similar to Phaser’s dragon and anime fullscreen examples as well as its Full Screen Mobile project template. However, those have a couple issues and do not work ideally for me on both my desktop (Linux laptop, Chromium) and phone (Galaxy S3, Chrome). In the end the problems were small but critical.

First, Phaser’s ScaleManager has an undocumented property fullScreenScaleMode that controls how games scale in fullscreen mode. The examples use this. The project template however uses the property scaleMode. It’s fairly easy to miss that these are using different properties, and that the fullscreen variant even exists. Further, it seems you actually have to set both to get the expected behavior, at least for SHOW_ALL scaling and my two platforms. The code below has a simple chart of the effects under various combinations of setting or not setting both of these.

Second, the ScaleManager property pageAlignHorizontally seems to have a bug. In windowed mode it works as expected, but in fullscreen it actually causes the game to be horizontally off-centered. For the moment I’m planning to put the game into a container div to center in windowed mode. Phaser seems to take care of centering the game in fullscreen mode even without this being set. I haven’t diagnosed this further. My guess is the base code is setting a left margin to center the game if the property is set, and then fullscreen mode applies a left margin again without checking to see if that has already been done.

Third, it’s not clear from the examples and docs what of Phaser’s ScaleManager API is needed and what is not. The project template in particular does several things that don’t seem to matter, for example calling ScaleManager::setScreenSize(). Removing that seems to have no effect with these settings, though the docs say it’s necessary. Conversely, it is important to call ScaleManager::refresh() to have the game scale at the start. Otherwise you have to go fullscreen or cause another window change to have the settings apply. The examples however don’t call this.

In any event, after finally identifying all these the test seems to work well on both my laptop and phone. Code is as follows:

<html>
<head>
  <script src="/deps/phaser-2.1.3-arcade-min.js"></script>
</head>

<body style="margin:0px; padding: 0px;">
<div id="game"></div>

<script type="text/javascript">
var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'game',
  { preload: preload, create: create });

function preload() {
  game.stage.backgroundColor = '#336699';
  game.load.image('logo', '20141026-scaletest-logo.png');
}

function create() {

  // Put a graphic in the center to demonstrate.
  sprite = game.add.sprite(game.world.centerX, game.world.centerY, 'logo');
  sprite.anchor.set(0.5);

  /*
   * How the following modes take affect.
   *
   * On a laptop (Linux, Chromium browser):
   *   FS Scale  Reg Scale Reg Result  FS Result
   *   On        On        Fills       Fills
   *   On        Off       Unscaled    Fills
   *   Off       On        Fills       Unscaled
   *   Off       Off       Unscaled    Unscaled
   *
   * On a phone (Galaxy S3, Chrome browser):
   *   FS Scale  Reg Scale N Result    FS Result
   *   On        On        Fills       Fills
   *   On        Off       Too big     Unscaled
   *   Off       On        Fills       Unscaled; buggy
   *   Off       Off       Too big     Exact fit
   *
   */
  game.scale.fullScreenScaleMode = Phaser.ScaleManager.SHOW_ALL;
  game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;

  // Properly centers game in windowed mode, but aligning
  // horizontally makes it off-centered in fullscreen mode.
  //game.scale.pageAlignHorizontally = true;
  //game.scale.pageAlignVertically = true;

  // Docs say this is necessary, but it doesn't seem to change behavior?
  //game.scale.setScreenSize(true);

  // This is necessary to scale before waiting for window changes.
  game.scale.refresh();

  game.input.onDown.add(gofull, this);

}

function gofull() {
  if (game.scale.isFullScreen) {
    game.scale.stopFullScreen();
  } else {
    game.scale.startFullScreen(false);
  }
}
</script>
</body>
</html>