How to Handle Orientation Change in Phaser Mobile Web Game

If you are not using RESIZE mode for mobile game development and using SHOW_ALL or other scale modes, then you would need to lock the game for one specific orientation. In the previous articles we discussed about the mobile game which was to be published to Android store (IntoductionCalculationGame Screen), I also uploaded the game online. The game was played in Landscape mode and I initially developed it for desktop. When I opened it in mobile, though I could open it in Portrait mode and everything was calculated well, I wanted to lock it for Landscape orientation when opened in browser on a mobile device. The orientation was easily locked in Android app by simply changing some configuration option but for browser version I had to write some addtional code. I got some help from Emanuele’s blog on the same topic and borrowed some code. I had to handle it differently since I was doing some additional game size calcuation on load and if the game was not loaded in full screen mode then aspect ratio of the game screen was not calculated properly (as browser address bar was also coming into the picture for aspect ratio calculation) so I decided that the game will only be loaded in Landscape mode and in Portrait mode we will not initialize the game and will ask user to turn the device. When user turns the device, we will load the game. Once the game is loaded in correct orientation and then turned to incorect orientation, we will simply overlay an image with message to turn device and game will continue to run in the background. The changes in the code goes as following

Declare following class in your CSS file (playlandscape.png is the overlay image we want to display on incorrect orientation)

#turn{
	width:100%;
	height:100%;
	position:fixed;
	top:0px;
	left:0px;
	background-color:white;
	background-image:url('images/playlandscape.png');
	background-repeat:no-repeat;
	background-position: center center;
    background-size:contain;
	display:none;
}

Add a div in the your html (highlighted below)

<body>
    <div id="traindominoes"></div>
    <div id="turn"></div>
</body>

Declare a flag to check which orientaion the game was loaded in. In boot class simply assign the orientation (See highlighted lines 2 and 11 below). In lines 13, 14, 15 we fixed the orientation to landscape (set first parameter to true for Landscape and second for Portrait) and defined two methods to handle correct/incorrect orientations.

// ---------------boot---------------------
var isInitializedInPotrait;

boot = {
    init: function () {
    },
    preload: function () {
        mygame.load.image("loading", "images/loading.png");
    },
    create: function () {
        isInitializedInPotrait = mygame.scale.isGamePortrait;
        mygame.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
        mygame.scale.forceOrientation(true, false);
        mygame.scale.enterIncorrectOrientation.add(handleIncorrect);
        mygame.scale.leaveIncorrectOrientation.add(handleCorrect);
        mygame.scale.setScreenSize = true;
        mygame.stage.scale.pageAlignHorizontally = true;
        mygame.stage.scale.pageAlignVeritcally = true;
        mygame.state.start("Loading");
    }
};

Now check out the code below which does rest of the work. We modified calculateSize method to check if we are loading on mobile and in Landscape mode. I did a simple check for currentAspectRatio and devicePixelRatio to evaluate this which works well for most use cases (see highlighted).

function calculateSize() {
    // Get Pixel Width and Height of the available space
    var w_w = window.innerWidth * window.devicePixelRatio;
    var w_h = window.innerHeight * window.devicePixelRatio;
    var g_w = w_w;
    // We prepared all assets according to aspect ratio of 1.5
    // Since we are going to use different assets for different resolutions, we are not looking at the resolution here
    var originalAspectRatio = 1.5;
    // Get the actual device aspect ratio
    var currentAspectRatio = g_w / w_h;
    if (currentAspectRatio < 1 && window.devicePixelRatio !== 1) {
        return false;
    }
    if (currentAspectRatio > originalAspectRatio) {
        // If actual aspect ratio is greater than the game aspect ratio, then we have horizontal padding 
        // In order to get the correct resolution of the asset we need to look at the height here
        // We planned for 1350x900 and 960x640 so if height is greater than 640 we pick higher resolution else lower resolution
        if (w_h > 640) {
            globalParams.selectedResolution = GameResolution._1350;
            globalParams.calculatedHeight = 900;
            globalParams.calculatedWidth = w_w * (900 / w_h);
        } else {
            globalParams.selectedResolution = GameResolution._960;
            globalParams.calculatedHeight = 640;
            globalParams.calculatedWidth = w_w * (640 / w_h);
        }
    } else {
        // If actual aspect ratio is less than the game aspect ratio, then we have vertical padding 
        // In order to get the correct resolution of the asset we need to look at the width here
        if (w_w > 960) {
            globalParams.selectedResolution = GameResolution._1350;
            globalParams.calculatedWidth = 1350;
            globalParams.calculatedHeight = w_h * (1350 / w_w);
        } else {
            globalParams.selectedResolution = GameResolution._960;
            globalParams.calculatedWidth = 960;
            globalParams.calculatedHeight = w_h * (960 / w_w);
        }
    }
    return true;
}

function handleIncorrect() {
    if (!mygame.device.desktop) {
        document.getElementById("turn").style.display = "block";
    }
}

function handleCorrect() {
    if (!mygame.device.desktop) {
        document.getElementById("turn").style.display = "none";
    }
}

var mygame;
var globalParams = {
    calculatedWidth: 1350,
    calculatedHeight: 900
}

window.onload = function () {
    if (calculateSize()) {
        loadGame();
    } else {
        document.getElementById("turn").style.display = "block";
        window.addEventListener("orientationchange", onOrientationChange, false);
    }
}

function onOrientationChange() {
    if (typeof isInitializedInPotrait === 'undefined') {
        document.getElementById("turn").style.display = "none";
        setTimeout(function () {
            if (calculateSize()) {
                loadGame();
            } else {
                document.getElementById("turn").style.display = "block";
            }
        }, 1000);
    }
}

function loadGame() {
    window.removeEventListener("orientationchange", onOrientationChange);
    mygame = new Phaser.Game(globalParams.calculatedWidth, globalParams.calculatedHeight, Phaser.AUTO, "traindominoes");

    mygame.state.add("Boot", boot);
    mygame.state.add("Loading", loading);
    mygame.state.start("Boot");
}

We had added two methods for handling correct and incorrect orintations in boot class. Phaser calls these methods accordingly based on device’s orientation. In these methods we simply overlay an image on top of game canvas if incorrect orientation or, hide the overlay image if correct orientation. This works well when user has loaded the game in Landscape mode and then turns device to Portrait mode.

If user opens the game in Portrait mode, then we don’t want to load the game. That we are handling in calculateSize method as explained above. If game was opened in incorrect mode then we will simply add onOrientationChage event and overlay the message. Once user turns the device to correct orientation, then only we will initialize the game which will give us the perfect calculated screen. Once game is correctly initialized, we won’t need onOrientationChage event so we will remove the event as soon as the game is loaded.

In portrait mode we see the “turn device” message as shown below

When we turn device to correct orientation, it displays the game.

 


Leave A Comment

Your email address will not be published. Required fields are marked *