how to make buttons in Phaser 3 with sprite sheets

Make Buttons in Phaser 3

We all need a way to easily add buttons to our game. While Phaser CE had this built in, Phaser 3 does not. I understand why resources were put elsewhere since we can always just use a sprite anyway. It is nice to have a way, however, to quickly make buttons in Phaser 3 with rollover and press states.

What about mobile?

Sure mobile devices don’t have rollovers, but it still can show the downstate. The user needs to know that the button was pressed. It is good practice to give feedback on every action the user takes.

So What do we need to do?

To make buttons in Phaser 3, there are several ways to go about it. In Phaser CE, I might have used a group. Phaser 3 groups do not have the ability to set an x or a y position. Phaser Containers have this ability and they would be the best choice if I needed to have more than one element. Since I am going to keep this as close to the classic Phaser CE method, I will use a sprite sheet and create a class that extends a sprite.

how to make buttons in phaser 3

Using the button class

Normally I would set everything up in the class before I would show how to use it in your own code, but in this case, I think that it is helpful to see the properties we are going to pass down into the class to show how we make the buttons in Phaser 3. I’ll be using the basic phaser 3 template to build this project.

The first thing we will do is to preload the sprite sheet which each cell is 236 px wide and 65 px high

this.load.spritesheet("buttons", "images/buttons.png",{ frameWidth: 236, frameHeight: 65 });

This is how we create the button. As you can see from the example below we are using an object rather than parameters. This is in keeping with the syntax of Phaser 3 and we can also put the properties in any order.

var button = new BasicButton({
            'scene': this,
            'key':'buttons',
            'up': 0,
            'over':1,
            'down':2,
            'x': 240,
            'y': 480
        })

The Basic Button Class

Next, make a new file called BasicButton.js and put this code into it.

class BasicButton extends Phaser.GameObjects.Sprite {


}

Now we have an empty class that extends a sprite, which means it has all the properties and methods of a sprite, plus the custom ones we will add. Before we go any further, we need to add the constructor which is called when a new instance of the basic button class is created.

class BasicButton extends Phaser.GameObjects.Sprite {
    constructor(config) {
    
    
    }
}

The constructor is passed the single parameter of the config object which contains all of our buttons properties which are

  • scene: required – a reference to the scene where we are adding the button. This is required because there are many methods of the scene that must be called that are not available to a sprite.
  • key: required– The image key of the sprite sheet we need to create the button
  • up: optional – The frame that is the normal default state. If no up property is in the config object we will use 0
  • down: optional – The frame that shows when the button is pressed. If no down property is in the config object will copy the up property
  • over: optional – The frame that shows when the mouse is over the button. On computers only. If no over property is in the config object then we will again use the up property.
  • x: optional – The x position of the button. defaults to 0
  • y: optional – The y position of the button. defaults to 0

 

Setting up the defaults and verify the config object

If when setting up the button a scene or a key is omitted, we want to stop the execution of the code before it causes an error. We just log a warning and get out. If the optional properties are not set, then we define them here

class BasicButton extends Phaser.GameObjects.Sprite {
    constructor(config) {
        
        //check if config contains a scene
        if (!config.scene) {
            console.log('missing scene');
            return;
        }
        //check if config contains a key
        if (!config.key) {
            console.log("missing key!");
            return;
        }
        //if there is no up property assume 0
        if (!config.up) {
            config.up = 0;
        }
        //if there is no down in config use up
        if (!config.down) {
            config.down = config.up;
        }
        //if there is no over in config use up
        if (!config.over) {
            config.over = config.up;
        }
     }
     
  }

Making the sprite!

At this point, all we have is the data. What we need is the visual button! This is the stuff that is built into the sprite’s code already. To make the sprite does what it normally does we need to call the sprites constructor from our custom class’ constructor using the Super method. Here we pass the scene, the x and y position, which we will set to 0 for now. Also we will Pass the key and the frame number into the super method.

//call the constructor of the parent
//set at 0,0 in case there is no x and y
//in the config
super(config.scene, 0, 0, config.key, config.up);

Why didn’t we pass in the x and the y?

Looking back I could have written code so that if there was no x or y properties in the config, we could have done something similar as we had done with the up property.

if (!config.x) {
            config.x= 0;
}

But this is how I wrote it instead:

//if there is an x assign it
        if (config.x) {
            this.x = config.x;
        }
//if there is an x assign it
        if (config.y) {
            this.y = config.y;
        }

I simply checked for the position variables in the object and set the object’s properties if it had them. The reason I did this after calling the super method, and this is IMPORTANT: You can not use the THIS keyword until you call super. This is vital to remember when you want to extend any class in Phaser 3.

If you don’t do this first you will get this error:

uncaught ReferenceError: Must call super constructor in derived class before accessing ‘this’ or returning from derived constructor

Adding the button to the canvas

Now we have created the sprite, but we can’t see it yet. It exists, but only in memory. For this, we need to use one line of code after all the properties have been set.

//add this to the scene
config.scene.add.existing(this);

Now your button shows up on stage but making buttons in Phaser 3 requires more than just setting the image and putting it in place. We need to add internal event listeners. We can start by making the sprite interactive and then setting a listener for over, up, out and down.

     //make interactive and set listeners
     this.setInteractive();
     this.on('pointerdown',this.onDown,this);
     this.on('pointerup',this.onUp,this);
     this.on('pointerover',this.onOver,this);
     this.on('pointerout',this.onUp,this);

Notice for pointerout event, I just use the onUp function again

Now the last part is simply to add in the functions called, and set the frame of the appropriate config object property.

onDown()
    {
    	this.setFrame(this.config.down);
    }
    onOver()
    {
    	this.setFrame(this.config.over);
    }
    onUp()
    {
    	this.setFrame(this.config.up);
    }

Now I realize that you may find to make buttons in Phaser 3 is a bit more complicated than in Phaser CE, but one of the wonderful things about code is what you build it you can use it over and again. You can even just download this code and use it without worrying about programming it yourself.

What you may have noticed though is that we don’t know in the scene when the button has been clicked. My personal preference is to use events, but for now, you can just treat the button as any other sprite.

 

class SceneMain extends Phaser.Scene {
    constructor() {
        super('SceneMain');
    }
    preload() {
        this.load.spritesheet("buttons", "images/buttons.png",{ frameWidth: 236, frameHeight: 65 });
        
    }
    create() {
        var button = new BasicButton({
            'scene': this,
            'key':'buttons',
            'up': 0,
            'over':1,
            'down':2,
            'x': 240,
            'y': 480
        });
        button.on('pointerdown',this.onPressed,this);
    }
    onPressed()
    {
        console.log("I am pressed!");
    }
    update() {}
}

I hope that you found this tuturiol on how to make buttons in phaser 3 helpful. I intend to expand on it with some custom events later, so stay tuned!

Leave a Comment