Tips for developing a dynamic game that adjusts to different screen sizes using Phaser 3

Recently, I've been on the hunt for a way to ensure my game adapts seamlessly to various screen resolutions when using Phaser 3. In the past, I achieved this with ease in Construct 2.

However, I'm now curious to explore how best to implement this feature in Phaser 3. Any suggestions or tips would be greatly appreciated!

Answer №1

After conducting thorough research, I discovered a solution to the issue at hand:

The key is to utilize a parent scene to oversee all other child scenes. This parent scene will match the device's screen size and adjust the child scenes accordingly while maintaining the aspect ratio.

HandlerScene.js

export default class Handler extends Phaser.Scene {

    // Variables
    sceneRunning = null

    constructor() {
        super('handler')
    }

    create() {
        this.cameras.main.setBackgroundColor('#FFF')
        this.launchScene('preload')
    }

    launchScene(scene, data) {
        this.scene.launch(scene, data)
        this.gameScene = this.scene.get(scene)
    }

    updateResize(scene) {
        scene.scale.on('resize', this.resize, scene)

        const scaleWidth = scene.scale.gameSize.width
        const scaleHeight = scene.scale.gameSize.height

        scene.parent = new Phaser.Structs.Size(scaleWidth, scaleHeight)
        scene.sizer = new Phaser.Structs.Size(scene.width, scene.height, Phaser.Structs.Size.FIT, scene.parent)

        scene.parent.setSize(scaleWidth, scaleHeight)
        scene.sizer.setSize(scaleWidth, scaleHeight)

        this.updateCamera(scene)
    }

    resize(gameSize) {
        // 'this' refers to the current running scene
        if (!this.sceneStopped) {
            const width = gameSize.width
            const height = gameSize.height

            this.parent.setSize(width, height)
            this.sizer.setSize(width, height)

            const camera = this.cameras.main
            const scaleX = this.sizer.width / this.game.screenBaseSize.width
            const scaleY = this.sizer.height / this.game.screenBaseSize.height

            camera.setZoom(Math.max(scaleX, scaleY))
            camera.centerOn(this.game.screenBaseSize.width / 2, this.game.screenBaseSize.height / 2)
        }
    }

    updateCamera(scene) {
        const camera = scene.cameras.main
        const scaleX = scene.sizer.width / this.game.screenBaseSize.width
        const scaleY = scene.sizer.height / this.game.screenBaseSize.height

        camera.setZoom(Math.max(scaleX, scaleY))
        camera.centerOn(this.game.screenBaseSize.width / 2, this.game.screenBaseSize.height / 2)
    }

}

This approach allows for running multiple scenes simultaneously within the parent scene.

PreloadScene.js

export default class Preload extends Phaser.Scene {

    handlerScene = null
    sceneStopped = false

    constructor() {
        super({ key: 'preload' })
    }

    preload() {
        // Images
        this.load.image('logo', 'assets/images/logo.png')   

        this.width = this.game.screenBaseSize.width
        this.height = this.game.screenBaseSize.height

        this.handlerScene = this.scene.get('handler')
        this.handlerScene.sceneRunning = 'preload'
        this.sceneStopped = false

        ...
    }

    create() {
        const { width, height } = this
        // CONFIG SCENE         
        this.handlerScene.updateResize(this)
        // CONFIG SCENE  

        // GAME OBJECTS  
        this.add.image(width / 2, height / 2, 'logo').setOrigin(.5)
        // GAME OBJECTS
    }
}

In the child scenes, the updateResize function of the parent scene must be called from the create function of each scene.

ConfigGame.js

import Handler from './scenes/handler.js'
import Preload from './scenes/preload.js'

// Aspect Ratio 16:9 - Portrait
const MAX_SIZE_WIDTH_SCREEN = 1920
const MAX_SIZE_HEIGHT_SCREEN = 1080
const MIN_SIZE_WIDTH_SCREEN = 270
const MIN_SIZE_HEIGHT_SCREEN = 480
const SIZE_WIDTH_SCREEN = 540
const SIZE_HEIGHT_SCREEN = 960

const config = {
    type: Phaser.AUTO,
    scale: {
        mode: Phaser.Scale.RESIZE,
        parent: 'game',
        width: SIZE_WIDTH_SCREEN,
        height: SIZE_HEIGHT_SCREEN,
        min: {
            width: MIN_SIZE_WIDTH_SCREEN,
            height: MIN_SIZE_HEIGHT_SCREEN
        },
        max: {
            width: MAX_SIZE_WIDTH_SCREEN,
            height: MAX_SIZE_HEIGHT_SCREEN
        }
    },
    dom: {
        createContainer: true
    },
    scene: [Handler, Preload]

}

const game = new Phaser.Game(config)

// Global

game.screenBaseSize = {
    maxWidth: MAX_SIZE_WIDTH_SCREEN,
    maxHeight: MAX_SIZE_HEIGHT_SCREEN,
    minWidth: MIN_SIZE_WIDTH_SCREEN,
    minHeight: MIN_SIZE_HEIGHT_SCREEN,
    width: SIZE_WIDTH_SCREEN,
    height: SIZE_HEIGHT_SCREEN
}

The mode: Phaser.Scale.RESIZE setting is crucial, along with defining maximum and minimum screen sizes.

You can find my complete solution here:

https://github.com/shimozurdo/mobile-game-base-phaser3

For further explanation, visit:

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best method for transferring states between components?

When it comes to React JS, I am a beginner looking to develop a ToDo list application. In my project, I have the main page App.js, where I created the component MainBlock. This MainBlock component structures the layout into left and right sides. On the rig ...

finding the node version in a code repository

Is it possible to determine the targeted node version (4 or 6) of a Node.js application by examining its code? I'm currently reviewing this: https://github.com/jorditost/node-postgres-restapi ...

Implementing Conditional Class Addition in React.js

Currently, I am utilizing Reactjs (Nextjs) to work on my project. The project consists of a "Home page" as well as several other pages such as about and services. To integrate these pages in Nextjs, I created "_document.js". However, I have encountered a ...

Real-time updates for UI data in Next.js Firestore are not being reflected

I'm currently working on implementing real-time changes using nextjs and Firebase Firestore. However, I've noticed that I still need to refresh the page in order for the updates to be visible. const [getUsers, setUsers] = useState(""); const che ...

Getting the value of dynamically generated buttons when they are clicked in PHP - a simple guide

In my PHP code, I have successfully created dynamic buttons. However, I am facing an issue where I need to retrieve the value of a specific button when it is clicked, and populate all the information in a form. Below is the code snippet for handling the b ...

Unable to retrieve information from a child component in React

So, this component acts as the main container class TaskList extends React.Component { state = { task: '', } handleTaskClick = () => { taskArray.push({id: idCount, text: this.state.task, completed: false}) idCount++ this. ...

What are the best ways to utilize getElementById in React?

As a beginner in React, I am attempting to create an auto text animation for my project. While I have successfully implemented it in VanillaJS, I am facing challenges with doing the same in React. import React, { Component } from 'react' class A ...

Using threejs to pass multiple secondary geometries into vertex shaders

Suppose I have a geometry that I am using to create points or an InstancedMesh by utilizing the vertices. However, if I want to change this underlying geometry to something else - such as from a cone to a sphere - that has the same number of vertices, how ...

How can JavaScript be used to create a unique signup and login process on

I am struggling with storing sign up and login details in JavaScript. In my previous project, I used PHP and a database to handle this information, so transitioning to JavaScript has been challenging. Here is an example of the sign-up HTML code: <!DOC ...

Is there a way to display a React component containing an array that is constantly changing due to an external function?

I am facing a challenge involving a component that needs to render an array of divs. The requirement is to add another div after a certain external function is triggered. This function must be declared outside the component for export purposes. The issue ...

Tips for integrating Material UI with useRef

There seems to be an issue with the code, but I haven't been able to pinpoint exactly what it is. My question is: How do you properly use useRef with Material UI? I am attempting to create a login page. The code was functioning fine with the original ...

Tips for managing this JavaScript JSON array

Here is an example of a JSON array: var data = { name: 'Mike', level: 1, children: [ { name: 'Susan', level: 2, }, { name: 'Jake', level: 2 }, { name: 'Roy', level: 2 }, ...

Stop allowing the entry of zero after a minus sign

One of the features on our platform allows users to input a number that will be automatically converted to have a negative sign. However, we want to ensure that users are unable to manually add a negative sign themselves. We need to find a solution to pre ...

"Encountering HTTP 400 Bad Request Error When Using Flask and Ajax for Post Requests

I'm currently developing a small Flask-based website and I want to use Ajax to send data from the client to the server. So far, I've only used Ajax requests to fetch data from the server, but this time I want to submit data via a POST request. O ...

Using jQuery to dynamically include option groups and options in a select box

Generate option groups and options dynamically using data retrieved via AJAX. <select name="catsndogs"> <optgroup label="Cats"> <option>Tiger</option> <option>Leopard</option> <option>Ly ...

How to deactivate a text box within a form that is dynamically generated using Angular

I have encountered an issue with disabling the textbox inside my dynamic form in AngularJS after clicking a button. My code works perfectly fine for disabling textboxes outside the dynamic form, but when I try to target the ID of the textbox within the dyn ...

Embed the AJAX response into HTML format

I am facing a challenge with rendering an AJAX response in an HTML structure within a PHP file. The issue is that the AJAX response contains multiple data objects, and each of them needs to be placed in specific locations within the HTML. For example, let ...

Unknown passport authentication method

I'm currently delving into a tutorial on building an authentication system using passport in Nodejs. The guide can be found here. My focus right now is on getting the signup form to function properly, but it keeps throwing this error: Error: Unknown ...

"Encountering a Type Error while attempting to destructure elements within ReactJS

Issue Upon querying objects from the GraphQl server and logging data, I can see the objects in the console. However, when attempting to destructure it as data: { getPosts : posts }, a type error is returned. Furthermore, trying to use map directly on data ...

how do I modify my JavaScript code to achieve the desired outcome

How can I program a button to disable after being pressed 10 times in less than a minute, but continue counting if not pressed for 1 minute? function buttonClicked() { if (totalCount + accCounter > 9) { document.getElementById("btn").disabled = ...